Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/ClusterM/pebble-my-data.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbahbka <bahbka@bahbka.com>2014-07-06 15:52:18 +0400
committerbahbka <bahbka@bahbka.com>2014-07-06 15:52:18 +0400
commit665ae812b91b695f2a3bcd131b7f458a5b048b8c (patch)
tree885703a322f987584e351052c2c9b4184a396fab
parent696117deeba342a1e22f5bbe10a7b4189909d770 (diff)
Changelog:
Append coordinates to URL (configurable) Digital clock font AM/PM support Seconds dots blinking (configurable) Configurable vibration on bluetooth loss Turn on light (value in JSON) Blink content (value in JSON) Scroll-up content after update (value in JSON) Improved configuration page Some minor fixes
-rw-r--r--appinfo.json108
-rw-r--r--resources/configuration.html119
-rw-r--r--resources/digit_0.pngbin0 -> 183 bytes
-rw-r--r--resources/digit_0am.pngbin0 -> 150 bytes
-rw-r--r--resources/digit_0pm.pngbin0 -> 152 bytes
-rw-r--r--resources/digit_1.pngbin0 -> 145 bytes
-rw-r--r--resources/digit_1am.pngbin0 -> 183 bytes
-rw-r--r--resources/digit_1pm.pngbin0 -> 174 bytes
-rw-r--r--resources/digit_2.pngbin0 -> 200 bytes
-rw-r--r--resources/digit_3.pngbin0 -> 190 bytes
-rw-r--r--resources/digit_4.pngbin0 -> 193 bytes
-rw-r--r--resources/digit_5.pngbin0 -> 199 bytes
-rw-r--r--resources/digit_6.pngbin0 -> 196 bytes
-rw-r--r--resources/digit_7.pngbin0 -> 176 bytes
-rw-r--r--resources/digit_8.pngbin0 -> 196 bytes
-rw-r--r--resources/digit_9.pngbin0 -> 203 bytes
-rw-r--r--resources/digit_dots.pngbin0 -> 161 bytes
-rw-r--r--resources/digit_dots_space.pngbin0 -> 131 bytes
-rw-r--r--resources/empty.pngbin0 -> 126 bytes
-rw-r--r--src/js/pebble-js-app.src.js68
-rw-r--r--src/pebble-my-data.c485
-rw-r--r--stuff/fonts/digits_15x25.pngbin0 -> 503 bytes
22 files changed, 597 insertions, 183 deletions
diff --git a/appinfo.json b/appinfo.json
index 4047a91..4b97424 100644
--- a/appinfo.json
+++ b/appinfo.json
@@ -3,18 +3,25 @@
"shortName": "My Data",
"longName": "My Data",
"companyName": "bahbka",
- "versionCode": 2,
- "versionLabel": "1.1.0",
+ "versionCode": 3,
+ "versionLabel": "1.2.0",
"watchapp": {
"watchface": false
},
- "capabilities": [ "configurable" ],
+ "capabilities": [ "configurable", "location" ],
"appKeys": {
- "content": 0,
- "refresh": 1,
- "vibrate": 2,
- "font": 3,
- "theme": 4
+ "msg_type": 0,
+ "content": 1,
+ "refresh": 2,
+ "vibrate": 3,
+ "font": 4,
+ "theme": 5,
+ "scroll": 6,
+ "light": 7,
+ "blink": 8,
+ "config_location": 9,
+ "config_vibrate": 10,
+ "config_seconds": 11
},
"resources": {
"media": [
@@ -48,6 +55,91 @@
"type": "png-trans",
"name": "UPDATE_ERROR",
"file": "update_error.png"
+ },
+ {
+ "type": "png-trans",
+ "name": "EMPTY",
+ "file": "empty.png"
+ },
+ {
+ "type": "png-trans",
+ "name": "DIGIT_0",
+ "file": "digit_0.png"
+ },
+ {
+ "type": "png-trans",
+ "name": "DIGIT_1",
+ "file": "digit_1.png"
+ },
+ {
+ "type": "png-trans",
+ "name": "DIGIT_2",
+ "file": "digit_2.png"
+ },
+ {
+ "type": "png-trans",
+ "name": "DIGIT_3",
+ "file": "digit_3.png"
+ },
+ {
+ "type": "png-trans",
+ "name": "DIGIT_4",
+ "file": "digit_4.png"
+ },
+ {
+ "type": "png-trans",
+ "name": "DIGIT_5",
+ "file": "digit_5.png"
+ },
+ {
+ "type": "png-trans",
+ "name": "DIGIT_6",
+ "file": "digit_6.png"
+ },
+ {
+ "type": "png-trans",
+ "name": "DIGIT_7",
+ "file": "digit_7.png"
+ },
+ {
+ "type": "png-trans",
+ "name": "DIGIT_8",
+ "file": "digit_8.png"
+ },
+ {
+ "type": "png-trans",
+ "name": "DIGIT_9",
+ "file": "digit_9.png"
+ },
+ {
+ "type": "png-trans",
+ "name": "DIGIT_DOTS",
+ "file": "digit_dots.png"
+ },
+ {
+ "type": "png-trans",
+ "name": "DIGIT_DOTS_SPACE",
+ "file": "digit_dots_space.png"
+ },
+ {
+ "type": "png-trans",
+ "name": "DIGIT_0AM",
+ "file": "digit_0am.png"
+ },
+ {
+ "type": "png-trans",
+ "name": "DIGIT_0PM",
+ "file": "digit_0pm.png"
+ },
+ {
+ "type": "png-trans",
+ "name": "DIGIT_1AM",
+ "file": "digit_1am.png"
+ },
+ {
+ "type": "png-trans",
+ "name": "DIGIT_1PM",
+ "file": "digit_1pm.png"
}
]
}
diff --git a/resources/configuration.html b/resources/configuration.html
index b2757f3..d86da87 100644
--- a/resources/configuration.html
+++ b/resources/configuration.html
@@ -1,53 +1,114 @@
<!DOCTYPE html>
+<!-- -*-coding: utf-8 -*-
+vim: sw=2 ts=2 expandtab ai -->
+
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body { background-color: black; text-align: center; color: white }
- h1,h3 { margin: 0; padding: 0 }
- h3 { color: gray }
- h4 { text-align: center; margin: 0; padding: 0 }
- input { height: 40px; font-size: 20px }
- .url { width: 95% }
- .submit { width: 50% }
- .example { text-align: left }
+ h1 { margin: 0 }
+ small { color: gray }
+ a { color: white }
+ input { height: 1.5em; font-size: 1.2em; font-weight: bold }
+ .url { width: 93%; margin: 0.5em }
+ .submit { width: 93%; margin: 0.4em }
+ .param { display: inline-table; width: 95%; height: 3em }
+ .label,.checkbox { display: table-cell; vertical-align: middle }
+ .label { text-align: left }
+ .checkbox { text-align: right; width: 1.5em; height: 1.5em }
+ .example { width: 75%; display: inline-block; text-align: left; font-size: 0.6em }
</style>
+ <script>
+
+ var config = _CONFIG_;
+
+ function put_config() {
+ for (var param in config) {
+ var element = document.getElementById(param);
+ if (element) {
+ if (typeof config[param] === 'boolean') {
+ element.checked = config[param];
+ } else {
+ element.value = config[param];
+ }
+ }
+ }
+ }
+
+ function get_config() {
+ var form = document.getElementById('config_form');
+ for (config = {}, i = 0; i < form.length ; i++) {
+ id = form[i].id;
+ if (id != "save") {
+ if (form[i].type === 'checkbox') {
+ config[id] = form[i].checked;
+ } else {
+ config[id] = form[i].value;
+ }
+ }
+ }
+ return window.location.href = "pebblejs://close#" + encodeURIComponent(JSON.stringify(config));
+ }
+ </script>
</head>
- <body>
+ <body onload="put_config();">
<h1>My Data</h1>
- <h3>v1.1.0, by bahbka</h3>
- <br><br>
- <form action="javascript: settings();" id="settings_form">
- <h4>my server URL:</h4>
- <input type="text" id="url" class="url" value="_URL_">
+ <small>v1.2.0, by bahbka</small>
+ <hr size="1" />
+
+ <form action="javascript: get_config();" id="config_form">
+ Server URL:
+ <input type="text" id="url" class="url" value="your URL here">
+
+ <div class="param">
+ <div class="label">
+ Append GPS coordinates to URL:<br>
+ <small>(can eat your phone battery)</small>
+ </div>
+ <div class="checkbox">
+ <input type="checkbox" id="config_location" class="checkbox">
+ </div>
+ </div>
+
+ <div class="param">
+ <div class="label">
+ Vibrate on bluetooth connection loss:
+ </div>
+ <div class="checkbox">
+ <input type="checkbox" id="config_vibrate" class="checkbox">
+ </div>
+ </div>
+
+ <div class="param">
+ <div class="label">
+ Seconds dots blinking:<br>
+ <small>(can eat your pebble battery)</small>
+ </div>
+ <div class="checkbox">
+ <input type="checkbox" id="config_seconds" class="checkbox">
+ </div>
+ </div>
<input type="submit" id="save" class="submit" value="save and apply">
</form>
- <br><br><br>
- sample server output:
+ <hr size="1" />
+
+ Sample server output:<br>
+ <small>(see documentation on <a href="https://github.com/bahbka/pebble-my-data/blob/master/README.md">github</a>)</small>
<pre class="example">
{
"content": "Hello\\nWorld!",
"refresh": 300, // refresh delay, seconds
"vibrate": 0, // 0..3, 0 - don't vibrate
"font": 1, // 1..8, try them all
- "theme": 0 // 0 - black, 1 - white
+ "theme": 0, // 0 - black, 1 - white
+ "scroll": 1, // scroll up after update
+ "light": 1, // turn on light
+ "blink": 1 // blink content (count)
}
GET param short=1 or long=1 added to URL on
short or long select button update
</pre>
-
- <script>
- function settings() {
- var form = document.getElementById('settings_form');
- for (configuration = {}, i = 0; i < form.length ; i++) {
- id = form[i].id;
- if (id != "save") {
- configuration[id] = form[i].value;
- }
- }
- return window.location.href = "pebblejs://close#" + encodeURIComponent(JSON.stringify(configuration));
- }
- </script>
</body>
</html>
diff --git a/resources/digit_0.png b/resources/digit_0.png
new file mode 100644
index 0000000..27c4c3d
--- /dev/null
+++ b/resources/digit_0.png
Binary files differ
diff --git a/resources/digit_0am.png b/resources/digit_0am.png
new file mode 100644
index 0000000..5576638
--- /dev/null
+++ b/resources/digit_0am.png
Binary files differ
diff --git a/resources/digit_0pm.png b/resources/digit_0pm.png
new file mode 100644
index 0000000..615ccfb
--- /dev/null
+++ b/resources/digit_0pm.png
Binary files differ
diff --git a/resources/digit_1.png b/resources/digit_1.png
new file mode 100644
index 0000000..3c6d225
--- /dev/null
+++ b/resources/digit_1.png
Binary files differ
diff --git a/resources/digit_1am.png b/resources/digit_1am.png
new file mode 100644
index 0000000..93da58d
--- /dev/null
+++ b/resources/digit_1am.png
Binary files differ
diff --git a/resources/digit_1pm.png b/resources/digit_1pm.png
new file mode 100644
index 0000000..f4474bc
--- /dev/null
+++ b/resources/digit_1pm.png
Binary files differ
diff --git a/resources/digit_2.png b/resources/digit_2.png
new file mode 100644
index 0000000..3438d20
--- /dev/null
+++ b/resources/digit_2.png
Binary files differ
diff --git a/resources/digit_3.png b/resources/digit_3.png
new file mode 100644
index 0000000..1960708
--- /dev/null
+++ b/resources/digit_3.png
Binary files differ
diff --git a/resources/digit_4.png b/resources/digit_4.png
new file mode 100644
index 0000000..5c9c1cd
--- /dev/null
+++ b/resources/digit_4.png
Binary files differ
diff --git a/resources/digit_5.png b/resources/digit_5.png
new file mode 100644
index 0000000..65950ec
--- /dev/null
+++ b/resources/digit_5.png
Binary files differ
diff --git a/resources/digit_6.png b/resources/digit_6.png
new file mode 100644
index 0000000..abfc9a8
--- /dev/null
+++ b/resources/digit_6.png
Binary files differ
diff --git a/resources/digit_7.png b/resources/digit_7.png
new file mode 100644
index 0000000..6294a5a
--- /dev/null
+++ b/resources/digit_7.png
Binary files differ
diff --git a/resources/digit_8.png b/resources/digit_8.png
new file mode 100644
index 0000000..165243b
--- /dev/null
+++ b/resources/digit_8.png
Binary files differ
diff --git a/resources/digit_9.png b/resources/digit_9.png
new file mode 100644
index 0000000..0d0f7a3
--- /dev/null
+++ b/resources/digit_9.png
Binary files differ
diff --git a/resources/digit_dots.png b/resources/digit_dots.png
new file mode 100644
index 0000000..4d0b5e5
--- /dev/null
+++ b/resources/digit_dots.png
Binary files differ
diff --git a/resources/digit_dots_space.png b/resources/digit_dots_space.png
new file mode 100644
index 0000000..7862c83
--- /dev/null
+++ b/resources/digit_dots_space.png
Binary files differ
diff --git a/resources/empty.png b/resources/empty.png
new file mode 100644
index 0000000..b54ed3b
--- /dev/null
+++ b/resources/empty.png
Binary files differ
diff --git a/src/js/pebble-js-app.src.js b/src/js/pebble-js-app.src.js
index cc3c3ad..8a40b8c 100644
--- a/src/js/pebble-js-app.src.js
+++ b/src/js/pebble-js-app.src.js
@@ -1,11 +1,18 @@
// -*-coding: utf-8 -*-
// vim: sw=2 ts=2 expandtab ai
-//TODO geolocation
+var MSG = {
+ PERIODIC_UPDATE: 0,
+ SHORT_PRESS_UPDATE: 1,
+ LONG_PRESS_UPDATE: 2,
+ JSON_RESPONSE: 3,
+ CONFIG: 4,
+ ERROR: 5
+};
var config = {};
-function fetch_data(url) {
+function http_request(url) {
var req = new XMLHttpRequest();
req.open('GET', url, true);
@@ -16,14 +23,17 @@ function fetch_data(url) {
try {
var response = JSON.parse(req.responseText);
//console.log("success: " + JSON.stringify(response));
+ response["msg_type"] = MSG.JSON_RESPONSE;
Pebble.sendAppMessage(response);
} catch(e) {
console.log("json parse error");
+ Pebble.sendAppMessage({ "msg_type": MSG.ERROR });
}
} else {
console.log("fetch error");
+ Pebble.sendAppMessage({ "msg_type": MSG.ERROR });
}
}
@@ -32,6 +42,38 @@ function fetch_data(url) {
req.send(null);
}
+function fetch_data(url) {
+ if (config["config_location"]) {
+ if(navigator.geolocation) {
+ navigator.geolocation.getCurrentPosition(
+ function(position) {
+ var latitude = position.coords.latitude;
+ var longitude = position.coords.longitude;
+
+ var s = (url.indexOf("?")===-1)?"?":"&";
+ http_request(url + s + 'lat=' + latitude + '&lon=' + longitude);
+ },
+ function(error) {
+ //error error.message
+ /*
+ TODO inform user about error
+ PERMISSION_DENIED (numeric value 1)
+ POSITION_UNAVAILABLE (numeric value 2)
+ TIMEOUT (numeric value 3)
+ */
+ http_request(url);
+ },
+ { maximumAge: 1800000 } // 30 minutes
+ );
+ } else {
+ //error geolocation not supported
+ http_request(url);
+ }
+ } else {
+ http_request(url);
+ }
+}
+
Pebble.addEventListener("ready",
function(e) {
console.log("JavaScript app ready and running!");
@@ -44,7 +86,8 @@ Pebble.addEventListener("ready",
//console.log("loaded config = " + JSON.stringify(config));
} catch(e) {
- console.log("json parse error");
+ console.log("stored config json parse error");
+ Pebble.sendAppMessage({ "msg_type": MSG.ERROR });
}
}
}
@@ -53,21 +96,25 @@ Pebble.addEventListener("ready",
Pebble.addEventListener("appmessage",
function(e) {
//console.log("received message: " + JSON.stringify(e.payload));
+
+ config["msg_type"] = MSG.CONFIG;
+ Pebble.sendAppMessage(config); // send current config to pebble
+
if (config["url"]) {
var url = config["url"];
var s = (url.indexOf("?")===-1)?"?":"&";
- if (e.payload["refresh"] == 1) {
+ if (e.payload["refresh"] == MSG.SHORT_PRESS_UPDATE) {
url = url + s + "short=1";
- } else if (e.payload["refresh"] == 2) {
+ } else if (e.payload["refresh"] == MSG.LONG_PRESS_UPDATE) {
url = url + s + "long=1";
}
fetch_data(url);
} else {
- Pebble.sendAppMessage({ "content": "URL not defined, chech settings in Pebble App" });
+ Pebble.sendAppMessage({ "msg_type": MSG.JSON_RESPONSE, "content": "URL not defined, check settings in Pebble App" });
}
}
);
@@ -78,8 +125,9 @@ Pebble.addEventListener('showConfiguration', function () {
} else {
url = "";
}
+ //console.log("put options = " + JSON.stringify(config));
- Pebble.openURL('data:text/html,'+encodeURI('_HTMLMARKER_<!--.html'.replace('"_URL_"', url, 'g')));
+ Pebble.openURL('data:text/html,'+encodeURI('_HTMLMARKER_<!--.html'.replace('_CONFIG_', JSON.stringify(config), 'g')));
});
Pebble.addEventListener("webviewclosed", function(e) {
@@ -89,11 +137,15 @@ Pebble.addEventListener("webviewclosed", function(e) {
window.localStorage.setItem('pebble-my-data', e.response);
+ config["msg_type"] = MSG.CONFIG;
+ //console.log("push config = " + JSON.stringify(config));
+ Pebble.sendAppMessage(config); // send current config to pebble
+
if (config["url"]) {
fetch_data(config["url"]);
} else {
- Pebble.sendAppMessage({ "content": "URL not defined, chech settings in Pebble App" });
+ Pebble.sendAppMessage({ "msg_type": MSG.JSON_RESPONSE, "content": "URL not defined, check settings in Pebble App" });
}
}
});
diff --git a/src/pebble-my-data.c b/src/pebble-my-data.c
index d3cdcf9..1f088aa 100644
--- a/src/pebble-my-data.c
+++ b/src/pebble-my-data.c
@@ -12,7 +12,6 @@ vim: sw=2 ts=2 expandtab ai
static Window *window;
static TextLayer *text_date_layer;
-static TextLayer *text_time_layer;
static TextLayer *text_info_layer;
static ScrollLayer *scroll_layer;
@@ -20,6 +19,9 @@ static ScrollLayer *scroll_layer;
static Layer *line_layer;
static Layer *wbatt_level_layer;
+static BitmapLayer *pos_layer[5];
+static GBitmap *digits_bitmap[16];
+
static BitmapLayer *wbatt_icon_layer;
static GBitmap *wbatt_icon_bitmap = NULL;
static GBitmap *wbatt_charge_icon_bitmap = NULL;
@@ -31,27 +33,53 @@ static BitmapLayer *update_icon_layer;
static GBitmap *update_icon_bitmap = NULL;
static GBitmap *update_error_icon_bitmap = NULL;
+static GBitmap *empty_icon_bitmap = NULL;
+
static AppTimer *timer = NULL;
static AppTimer *timeout_timer = NULL;
+static AppTimer *blink_timer = NULL;
static uint8_t theme = 255;
+static uint8_t blink_theme_save;
+static uint8_t blink_count;
+
+bool config_vibrate = true;
+bool config_seconds = false;
+
#define DEFAULT_REFRESH 300*1000
#define RETRY_DELAY 60*1000
#define REQUEST_TIMEOUT 30*1000
+#define BLINK_INTERVAL 500
+#define BLINK_MAX 20
+
+#define CONTENT_SIZE 1024
+
+static char content[CONTENT_SIZE];
+
enum { // AppMessage keys
+ KEY_MSG_TYPE,
KEY_CONTENT,
KEY_REFRESH,
KEY_VIBRATE,
KEY_FONT,
- KEY_THEME
+ KEY_THEME,
+ KEY_SCROLL,
+ KEY_LIGHT,
+ KEY_BLINK,
+ KEY_CONFIG_LOCATION,
+ KEY_CONFIG_VIBRATE,
+ KEY_CONFIG_SECONDS
};
-enum { // update types
- PERIODIC_UPDATE,
- SHORT_PRESS_UPDATE,
- LONG_PRESS_UPDATE
+enum { // msg type
+ MSG_PERIODIC_UPDATE,
+ MSG_SHORT_PRESS_UPDATE,
+ MSG_LONG_PRESS_UPDATE,
+ MSG_JSON_RESPONSE,
+ MSG_CONFIG,
+ MSG_ERROR
};
enum { // themes
@@ -59,7 +87,7 @@ enum { // themes
THEME_WHITE
};
-static uint8_t update_type = PERIODIC_UPDATE;
+static uint8_t update_type = MSG_PERIODIC_UPDATE;
// fill layer used for drawing line and battery charge
static void fill_layer(Layer *layer, GContext* ctx) {
@@ -77,7 +105,6 @@ static void request_timeout(); // proto
static void request_update() {
// show update icon
bitmap_layer_set_bitmap(update_icon_layer, update_icon_bitmap);
- layer_set_hidden(bitmap_layer_get_layer(update_icon_layer), false);
// prepare request
DictionaryIterator *send_message;
@@ -109,41 +136,74 @@ static void schedule_update(uint32_t delay, uint8_t type) {
// reschedule update if data waiting timed out, also set update_error icon
static void request_timeout() {
bitmap_layer_set_bitmap(update_icon_layer, update_error_icon_bitmap);
- schedule_update(RETRY_DELAY, PERIODIC_UPDATE);
+ schedule_update(RETRY_DELAY, MSG_PERIODIC_UPDATE);
}
-// handle minute tick to update time/date (got from Simplicity watchface without modification)
-static void handle_minute_tick(struct tm *tick_time, TimeUnits units_changed) {
- // Need to be static because they're used by the system later.
- static char time_text[] = "00:00";
- static char date_text[] = "Xxxxxxxxx 00";
-
- char *time_format;
-
- if (!tick_time) {
- time_t now = time(NULL);
- tick_time = localtime(&now);
+// draw digit on position layer
+static void draw_digit(BitmapLayer *position, char digit) {
+ if (digit >= 48 && digit <= 63) {
+ bitmap_layer_set_bitmap(position, digits_bitmap[digit - 48]);
+ } else {
+ bitmap_layer_set_bitmap(position, digits_bitmap[11]); // space
}
+}
- // TODO: Only update the date when it's changed.
- strftime(date_text, sizeof(date_text), "%a %b %e", tick_time);
- text_layer_set_text(text_date_layer, date_text);
+// handle minute tick to update time/date
+static void handle_timer_tick(struct tm *tick_time, TimeUnits units_changed) {
+ if (units_changed == SECOND_UNIT) {
+ if (tick_time->tm_sec % 2 == 0) {
+ draw_digit(pos_layer[2], ':');
+ } else {
+ draw_digit(pos_layer[2], ' ');
+ }
- if (clock_is_24h_style()) {
- time_format = "%R";
} else {
- time_format = "%I:%M";
- }
+ // Need to be static because they're used by the system later.
+ static char time_text[] = "00:00";
+ static char date_text[] = "Mon Jan 1";
+ static char ampm[] = "AM";
- strftime(time_text, sizeof(time_text), time_format, tick_time);
+ char *time_format;
- // Kludge to handle lack of non-padded hour format string
- // for twelve hour clock.
- if (!clock_is_24h_style() && (time_text[0] == '0')) {
- memmove(time_text, &time_text[1], sizeof(time_text) - 1);
- }
+ if (!tick_time) {
+ time_t now = time(NULL);
+ tick_time = localtime(&now);
+ }
- text_layer_set_text(text_time_layer, time_text);
+ strftime(date_text, sizeof(date_text), "%a %b %e", tick_time);
+ text_layer_set_text(text_date_layer, date_text);
+
+ if (clock_is_24h_style()) {
+ time_format = "%R";
+ } else {
+ time_format = "%I:%M";
+ }
+
+ strftime(time_text, sizeof(time_text), time_format, tick_time);
+
+ // Kludge to handle lack of non-padded hour format string
+ // for twelve hour clock.
+ if (!clock_is_24h_style()) {
+ strftime(ampm, sizeof(ampm), "%p", tick_time);
+ if (time_text[0] == '0') {
+ if (strcmp(ampm, "AM") == 0) {
+ time_text[0] = '0' + 12; // 0 am
+ } else {
+ time_text[0] = '0' + 13; // 0 pm
+ }
+ } else {
+ if (strcmp(ampm, "AM") == 0) {
+ time_text[0] = '0' + 14; // 1 am
+ } else {
+ time_text[0] = '0' + 15; // 1 pm
+ }
+ }
+ }
+
+ for (int i = 0; i < 5; i++) {
+ draw_digit(pos_layer[i], time_text[i]);
+ }
+ }
}
// handle battery status changes
@@ -162,16 +222,18 @@ static void handle_battery(BatteryChargeState charge_state) {
// handle bluetooth status changes
static void handle_bluetooth(bool connected) {
if (connected) {
- layer_set_hidden(bitmap_layer_get_layer(no_phone_icon_layer), true);
- schedule_update(5 * 1000, PERIODIC_UPDATE); // schedule update when connection made
+ bitmap_layer_set_bitmap(no_phone_icon_layer, empty_icon_bitmap); // hide update icon (layer_set_hidden function sometimes works strange)
+ schedule_update(5 * 1000, MSG_PERIODIC_UPDATE); // schedule update when connection made
} else {
- layer_set_hidden(bitmap_layer_get_layer(no_phone_icon_layer), false);
- vibes_long_pulse(); // achtung! phone lost!
+ bitmap_layer_set_bitmap(no_phone_icon_layer, no_phone_icon_bitmap);
+ if (config_vibrate) {
+ vibes_long_pulse(); // achtung! phone lost!
+ }
}
}
// update data in main layer (content, font)
-static void update_info_layer(char *content, uint8_t font) {
+static void update_info_layer(char *content, uint8_t font, bool scroll_up) {
// change font
switch (font) {
case 1:
@@ -204,9 +266,13 @@ static void update_info_layer(char *content, uint8_t font) {
text_layer_set_text(text_info_layer, content); // set text
GSize max_size = text_layer_get_content_size(text_info_layer);
+ max_size.w = 144;
max_size.h += 4;
text_layer_set_size(text_info_layer, max_size); // resize layer
scroll_layer_set_content_size(scroll_layer, GSize(144, max_size.h)); // resize scroll layer
+ if (scroll_up) {
+ scroll_layer_set_content_offset(scroll_layer, GPoint(0, 0), true); // scroll to beginning with animation
+ }
}
// change theme (black/white), do things only when theme realy changed
@@ -224,29 +290,25 @@ static void change_theme(uint8_t t) {
// set foreground, background colors and update window, layers
window_set_background_color(window, bg);
- text_layer_set_text_color(text_time_layer, fg);
text_layer_set_text_color(text_date_layer, fg);
+
+ text_layer_set_background_color(text_info_layer, bg);
text_layer_set_text_color(text_info_layer, fg);
layer_mark_dirty(wbatt_level_layer);
layer_mark_dirty(line_layer);
// destroy and recreate bitmaps
- if (wbatt_icon_bitmap) {
- gbitmap_destroy(wbatt_icon_bitmap);
- }
- if (wbatt_charge_icon_bitmap) {
- gbitmap_destroy(wbatt_charge_icon_bitmap);
- }
- if (no_phone_icon_bitmap) {
- gbitmap_destroy(no_phone_icon_bitmap);
- }
- if (update_icon_bitmap) {
- gbitmap_destroy(update_icon_bitmap);
- }
- if (update_error_icon_bitmap) {
- gbitmap_destroy(update_error_icon_bitmap);
- }
+ gbitmap_destroy(wbatt_icon_bitmap);
+ gbitmap_destroy(wbatt_charge_icon_bitmap);
+ gbitmap_destroy(no_phone_icon_bitmap);
+ gbitmap_destroy(update_icon_bitmap);
+ gbitmap_destroy(update_error_icon_bitmap);
+ gbitmap_destroy(empty_icon_bitmap);
+
+
+ for (int i = 0; i < 16; i++)
+ gbitmap_destroy(digits_bitmap[i]);
if (theme == THEME_BLACK) {
wbatt_icon_bitmap = gbitmap_create_with_resource(RESOURCE_ID_WBATT_WHITE);
@@ -254,15 +316,51 @@ static void change_theme(uint8_t t) {
no_phone_icon_bitmap = gbitmap_create_with_resource(RESOURCE_ID_NO_PHONE_WHITE);
update_icon_bitmap = gbitmap_create_with_resource(RESOURCE_ID_UPDATE_WHITE);
update_error_icon_bitmap = gbitmap_create_with_resource(RESOURCE_ID_UPDATE_ERROR_WHITE);
+ empty_icon_bitmap = gbitmap_create_with_resource(RESOURCE_ID_EMPTY_WHITE);
+
+ digits_bitmap[0] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_0_WHITE);
+ digits_bitmap[1] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_1_WHITE);
+ digits_bitmap[2] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_2_WHITE);
+ digits_bitmap[3] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_3_WHITE);
+ digits_bitmap[4] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_4_WHITE);
+ digits_bitmap[5] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_5_WHITE);
+ digits_bitmap[6] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_6_WHITE);
+ digits_bitmap[7] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_7_WHITE);
+ digits_bitmap[8] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_8_WHITE);
+ digits_bitmap[9] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_9_WHITE);
+ digits_bitmap[10] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_DOTS_WHITE);
+ digits_bitmap[11] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_DOTS_SPACE_WHITE);
+ digits_bitmap[12] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_0AM_WHITE);
+ digits_bitmap[13] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_0PM_WHITE);
+ digits_bitmap[14] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_1AM_WHITE);
+ digits_bitmap[15] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_1PM_WHITE);
} else {
wbatt_icon_bitmap = gbitmap_create_with_resource(RESOURCE_ID_WBATT_BLACK);
wbatt_charge_icon_bitmap = gbitmap_create_with_resource(RESOURCE_ID_WBATT_CHARGE_BLACK);
no_phone_icon_bitmap = gbitmap_create_with_resource(RESOURCE_ID_NO_PHONE_BLACK);
update_icon_bitmap = gbitmap_create_with_resource(RESOURCE_ID_UPDATE_BLACK);
update_error_icon_bitmap = gbitmap_create_with_resource(RESOURCE_ID_UPDATE_ERROR_BLACK);
+ empty_icon_bitmap = gbitmap_create_with_resource(RESOURCE_ID_EMPTY_BLACK);
+
+ digits_bitmap[0] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_0_BLACK);
+ digits_bitmap[1] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_1_BLACK);
+ digits_bitmap[2] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_2_BLACK);
+ digits_bitmap[3] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_3_BLACK);
+ digits_bitmap[4] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_4_BLACK);
+ digits_bitmap[5] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_5_BLACK);
+ digits_bitmap[6] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_6_BLACK);
+ digits_bitmap[7] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_7_BLACK);
+ digits_bitmap[8] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_8_BLACK);
+ digits_bitmap[9] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_9_BLACK);
+ digits_bitmap[10] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_DOTS_BLACK);
+ digits_bitmap[11] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_DOTS_SPACE_BLACK);
+ digits_bitmap[12] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_0AM_BLACK);
+ digits_bitmap[13] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_0PM_BLACK);
+ digits_bitmap[14] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_1AM_BLACK);
+ digits_bitmap[15] = gbitmap_create_with_resource(RESOURCE_ID_DIGIT_1PM_BLACK);
}
- bitmap_layer_set_bitmap(no_phone_icon_layer, no_phone_icon_bitmap); // single place to set no_phone bitmap to layer
+ handle_timer_tick(NULL, MINUTE_UNIT); // update time
// update bitmaps layers
layer_mark_dirty(bitmap_layer_get_layer(wbatt_icon_layer));
@@ -271,6 +369,34 @@ static void change_theme(uint8_t t) {
}
}
+// change only info layer theme (for blinking)
+static void change_info_theme(uint8_t theme) {
+ if (theme == THEME_BLACK) {
+ text_layer_set_background_color(text_info_layer, GColorBlack);
+ text_layer_set_text_color(text_info_layer, GColorWhite);
+ } else {
+ text_layer_set_background_color(text_info_layer, GColorWhite);
+ text_layer_set_text_color(text_info_layer, GColorBlack);
+ }
+}
+
+// blink info theme
+static void blink_info() {
+ if (blink_timer) {
+ app_timer_cancel(blink_timer);
+ }
+
+ if (blink_count > 0) {
+ change_info_theme((blink_count + !blink_theme_save) % 2);
+
+ blink_count--;
+ blink_timer = app_timer_register(BLINK_INTERVAL, blink_info, NULL);
+
+ } else {
+ change_info_theme(blink_theme_save);
+ }
+}
+
// useful in the future probably
void out_sent_handler(DictionaryIterator *sent, void *context) {
}
@@ -278,73 +404,150 @@ void out_sent_handler(DictionaryIterator *sent, void *context) {
// reschedule update if request_update sending failed, also set update_error icon
void out_failed_handler(DictionaryIterator *failed, AppMessageResult reason, void *context) {
bitmap_layer_set_bitmap(update_icon_layer, update_error_icon_bitmap);
- schedule_update(RETRY_DELAY, PERIODIC_UPDATE);
+ schedule_update(RETRY_DELAY, MSG_PERIODIC_UPDATE);
}
// reschedule update if received data dropped, also set update_error icon
void in_dropped_handler(AppMessageResult reason, void *context) {
bitmap_layer_set_bitmap(update_icon_layer, update_error_icon_bitmap);
- schedule_update(RETRY_DELAY, PERIODIC_UPDATE);
+ schedule_update(RETRY_DELAY, MSG_PERIODIC_UPDATE);
}
// process received data
-// TODO blink whith theme and light function
void in_received_handler(DictionaryIterator *received, void *context) {
- layer_set_hidden(bitmap_layer_get_layer(update_icon_layer), true); // hide update icon
-
// update main layer with received content and font
- Tuple *content = dict_find(received, KEY_CONTENT);
- if (content) {
- Tuple *font = dict_find(received, KEY_FONT);
- if (font) {
- update_info_layer(content->value->cstring, font->value->uint8);
- } else {
- update_info_layer(content->value->cstring, 0);
- }
- }
-
- // maybe need to vibrate?
- Tuple *vibrate = dict_find(received, KEY_VIBRATE);
- if (vibrate) {
- switch(vibrate->value->uint8) {
- case 1:
- vibes_short_pulse();
- break;
- case 2:
- vibes_double_pulse();
- break;
- case 3:
- vibes_long_pulse();
- break;
- default:
- break;
+ Tuple *msg_type_tuple = dict_find(received, KEY_MSG_TYPE);
+ if (msg_type_tuple) {
+ if(msg_type_tuple->value->uint8 == MSG_CONFIG) {
+ Tuple *config_vibrate_tuple = dict_find(received, KEY_CONFIG_VIBRATE);
+ if (config_vibrate_tuple) {
+ if (strcmp(config_vibrate_tuple->value->cstring, "true") == 0) {
+ config_vibrate = true;
+ } else {
+ config_vibrate = false;
+ }
+ }
+
+ Tuple *config_seconds_tuple = dict_find(received, KEY_CONFIG_SECONDS);
+ if (config_seconds_tuple) {
+ if (strcmp(config_seconds_tuple->value->cstring, "true") == 0) {
+ if (!config_seconds) {
+ config_seconds = true;
+ tick_timer_service_subscribe(SECOND_UNIT, handle_timer_tick);
+ }
+ } else {
+ if (config_seconds) {
+ config_seconds = false;
+ tick_timer_service_subscribe(MINUTE_UNIT, handle_timer_tick);
+ handle_timer_tick(NULL, MINUTE_UNIT);
+ }
+ }
+ }
+
+ // TODO location icon?
+
+ } else if (msg_type_tuple->value->uint8 == MSG_ERROR) {
+ bitmap_layer_set_bitmap(update_icon_layer, update_error_icon_bitmap);
+ schedule_update(RETRY_DELAY, MSG_PERIODIC_UPDATE);
+
+ } else if (msg_type_tuple->value->uint8 == MSG_JSON_RESPONSE) {
+ bitmap_layer_set_bitmap(update_icon_layer, empty_icon_bitmap); // hide update icon (layer_set_hidden function sometimes works strange)
+
+ Tuple *content_tuple = dict_find(received, KEY_CONTENT);
+ if (content_tuple) {
+ memcpy(content, content_tuple->value->cstring, strlen(content_tuple->value->cstring));
+
+ Tuple *scroll_up_tuple = dict_find(received, KEY_SCROLL);
+ bool scroll_up = false;
+ if (scroll_up_tuple && scroll_up_tuple->value->uint8 == 1) {
+ scroll_up = true;
+ }
+
+ Tuple *font = dict_find(received, KEY_FONT);
+ if (font) {
+ update_info_layer(content, font->value->uint8, scroll_up);
+ } else {
+ update_info_layer(content, 0, scroll_up);
+ }
+ }
+
+ // maybe need to vibrate?
+ Tuple *vibrate = dict_find(received, KEY_VIBRATE);
+ if (vibrate) {
+ switch(vibrate->value->uint8) {
+ case 1:
+ vibes_short_pulse();
+ break;
+ case 2:
+ vibes_double_pulse();
+ break;
+ case 3:
+ vibes_long_pulse();
+ break;
+ default:
+ break;
+ }
+ }
+
+ // or switch theme?
+ Tuple *new_theme = dict_find(received, KEY_THEME);
+ if (new_theme) {
+ change_theme(new_theme->value->uint8);
+ }
+
+ // or turn on light?
+ Tuple *light = dict_find(received, KEY_LIGHT);
+ if (light && light->value->uint8 == 1) {
+ light_enable_interaction();
+ }
+
+ // or blink content?
+ Tuple *blink = dict_find(received, KEY_BLINK);
+ if (blink) {
+ if (blink->value->uint8 > 0) {
+ if (blink->value->uint8 > BLINK_MAX) {
+ blink_count = BLINK_MAX;
+ } else {
+ blink_count = blink->value->uint8 * 2;
+ }
+
+ blink_theme_save = theme;
+ blink_info();
+ }
+ }
+
+ // schedule next update
+ uint32_t delay = DEFAULT_REFRESH;
+ Tuple *refresh = dict_find(received, KEY_REFRESH);
+ if (refresh) {
+ delay = refresh->value->uint32 * 1000;
+ }
+
+ schedule_update(delay, MSG_PERIODIC_UPDATE);
}
}
-
- // or switch theme?
- Tuple *new_theme = dict_find(received, KEY_THEME);
- if (new_theme) {
- change_theme(new_theme->value->uint8);
- }
-
- // schedule next update
- uint32_t delay = DEFAULT_REFRESH;
- Tuple *refresh = dict_find(received, KEY_REFRESH);
- if (refresh) {
- delay = refresh->value->uint32 * 1000;
- }
-
- schedule_update(delay, PERIODIC_UPDATE);
}
// force update on select short click
static void select_click_handler(ClickRecognizerRef recognizer, void *context) {
- schedule_update(0, SHORT_PRESS_UPDATE);
+ if (blink_count > 0) { // if blinking - stop it!
+ blink_count = 0;
+ blink_info();
+
+ } else {
+ schedule_update(0, MSG_SHORT_PRESS_UPDATE);
+ }
}
// force update on select long click
static void select_long_click_handler(ClickRecognizerRef recognizer, void *context) {
- schedule_update(0, LONG_PRESS_UPDATE);
+ if (blink_count > 0) { // if blinking - stop it!
+ blink_count = 0;
+ blink_info();
+
+ } else {
+ schedule_update(0, MSG_LONG_PRESS_UPDATE);
+ }
}
// handle select button clicks
@@ -357,15 +560,25 @@ static void click_config_provider(void *context) {
static void window_load(Window *window) {
Layer *window_layer = window_get_root_layer(window);
- // time layer
- text_time_layer = text_layer_create(GRect(1, -7, 142, 30));
- text_layer_set_background_color(text_time_layer, GColorClear);
- text_layer_set_font(text_time_layer, fonts_get_system_font(FONT_KEY_BITHAM_30_BLACK));
- text_layer_set_text_alignment(text_time_layer, GTextAlignmentRight);
- layer_add_child(window_layer, text_layer_get_layer(text_time_layer));
+ // time layers
+ // pos1
+ pos_layer[0] = bitmap_layer_create(GRect(65, 2, 16, 25));
+ layer_add_child(window_layer, bitmap_layer_get_layer(pos_layer[0]));
+ // pos2
+ pos_layer[1] = bitmap_layer_create(GRect(82, 2, 16, 25));
+ layer_add_child(window_layer, bitmap_layer_get_layer(pos_layer[1]));
+ // pos3
+ pos_layer[2] = bitmap_layer_create(GRect(101, 2, 6, 25));
+ layer_add_child(window_layer, bitmap_layer_get_layer(pos_layer[2]));
+ // pos4
+ pos_layer[3] = bitmap_layer_create(GRect(109, 2, 16, 25));
+ layer_add_child(window_layer, bitmap_layer_get_layer(pos_layer[3]));
+ // pos5
+ pos_layer[4] = bitmap_layer_create(GRect(126, 2, 16, 25));
+ layer_add_child(window_layer, bitmap_layer_get_layer(pos_layer[4]));
// date layer
- text_date_layer = text_layer_create(GRect(1, 9, 142, 14));
+ text_date_layer = text_layer_create(GRect(0, 13, 142, 14));
text_layer_set_background_color(text_date_layer, GColorClear);
text_layer_set_font(text_date_layer, fonts_get_system_font(FONT_KEY_GOTHIC_14_BOLD));
layer_add_child(window_layer, text_layer_get_layer(text_date_layer));
@@ -380,30 +593,30 @@ static void window_load(Window *window) {
layer_add_child(window_layer, wbatt_level_layer);
// no phone icon layer
- no_phone_icon_layer = bitmap_layer_create(GRect(41, 2, 10, 10));
- layer_set_hidden(bitmap_layer_get_layer(no_phone_icon_layer), true);
+ no_phone_icon_layer = bitmap_layer_create(GRect(33, 2, 10, 10));
+ bitmap_layer_set_bitmap(no_phone_icon_layer, empty_icon_bitmap); // hide update icon (layer_set_hidden function sometimes works strange)
layer_add_child(window_layer, bitmap_layer_get_layer(no_phone_icon_layer));
// update icon layer
- update_icon_layer = bitmap_layer_create(GRect(53, 2, 10, 10));
- layer_set_hidden(bitmap_layer_get_layer(update_icon_layer), true);
+ update_icon_layer = bitmap_layer_create(GRect(44, 2, 10, 10));
+ bitmap_layer_set_bitmap(update_icon_layer, empty_icon_bitmap); // hide update icon (layer_set_hidden function sometimes works strange)
layer_add_child(window_layer, bitmap_layer_get_layer(update_icon_layer));
// line layer
- GRect line_frame = GRect(0, 25, 144, 2);
+ GRect line_frame = GRect(0, 29, 144, 2);
line_layer = layer_create(line_frame);
layer_set_update_proc(line_layer, fill_layer);
layer_add_child(window_layer, line_layer);
// main info layer
- scroll_layer = scroll_layer_create(GRect(0, 26, 144, 168-26));
+ scroll_layer = scroll_layer_create(GRect(0, 31, 145, 168-31));
scroll_layer_set_click_config_onto_window(scroll_layer, window);
scroll_layer_set_callbacks(scroll_layer, (ScrollLayerCallbacks) {
.click_config_provider = &click_config_provider
});
- text_info_layer = text_layer_create(GRect(0, 0, 144, 2000));
- text_layer_set_background_color(text_info_layer, GColorClear);
+ text_info_layer = text_layer_create(GRect(0, 0, 145, 2000));
+ //text_layer_set_background_color(text_info_layer, GColorClear);
scroll_layer_add_child(scroll_layer, text_layer_get_layer(text_info_layer));
layer_add_child(window_layer, scroll_layer_get_layer(scroll_layer));
@@ -418,8 +631,8 @@ static void window_load(Window *window) {
bluetooth_connection_service_subscribe(&handle_bluetooth);
handle_bluetooth(bluetooth_connection_service_peek());
- tick_timer_service_subscribe(MINUTE_UNIT, handle_minute_tick);
- handle_minute_tick(NULL, MINUTE_UNIT);
+ tick_timer_service_subscribe(MINUTE_UNIT, handle_timer_tick);
+ handle_timer_tick(NULL, MINUTE_UNIT);
}
static void window_unload(Window *window) {
@@ -427,34 +640,30 @@ static void window_unload(Window *window) {
battery_state_service_unsubscribe();
bluetooth_connection_service_unsubscribe();
- text_layer_destroy(text_time_layer);
text_layer_destroy(text_date_layer);
text_layer_destroy(text_info_layer);
scroll_layer_destroy(scroll_layer);
- if (wbatt_icon_bitmap) {
- gbitmap_destroy(wbatt_icon_bitmap);
- }
- if (wbatt_charge_icon_bitmap) {
- gbitmap_destroy(wbatt_charge_icon_bitmap);
- }
- if (no_phone_icon_bitmap) {
- gbitmap_destroy(no_phone_icon_bitmap);
- }
- if (update_icon_bitmap) {
- gbitmap_destroy(update_icon_bitmap);
- }
- if (update_error_icon_bitmap) {
- gbitmap_destroy(update_error_icon_bitmap);
- }
+ gbitmap_destroy(wbatt_icon_bitmap);
+ gbitmap_destroy(wbatt_charge_icon_bitmap);
+ gbitmap_destroy(no_phone_icon_bitmap);
+ gbitmap_destroy(update_icon_bitmap);
+ gbitmap_destroy(update_error_icon_bitmap);
+ gbitmap_destroy(empty_icon_bitmap);
+
+ for (int i = 0; i < 16; i++)
+ gbitmap_destroy(digits_bitmap[i]);
bitmap_layer_destroy(wbatt_icon_layer);
bitmap_layer_destroy(no_phone_icon_layer);
bitmap_layer_destroy(update_icon_layer);
- layer_destroy(wbatt_level_layer);
+ for (int i = 0; i < 5; i++)
+ bitmap_layer_destroy(pos_layer[i]);
+
layer_destroy(line_layer);
+ layer_destroy(wbatt_level_layer);
}
static void init(void) {
@@ -471,7 +680,7 @@ static void init(void) {
app_message_register_outbox_sent(out_sent_handler);
app_message_register_outbox_failed(out_failed_handler);
- const uint32_t inbound_size = 1024;
+ const uint32_t inbound_size = CONTENT_SIZE;
const uint32_t outbound_size = 64;
app_message_open(inbound_size, outbound_size);
diff --git a/stuff/fonts/digits_15x25.png b/stuff/fonts/digits_15x25.png
new file mode 100644
index 0000000..89ecddc
--- /dev/null
+++ b/stuff/fonts/digits_15x25.png
Binary files differ