Initial commit, adding original binaries, initial watch and companion apps which can talk
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
# SchwertUndMagieOnPebbleFrontend
|
||||
|
||||
A Pebble watchapp/watchface written in C using the Pebble SDK.
|
||||
|
||||
## Building & running
|
||||
|
||||
```sh
|
||||
pebble build # build for all targetPlatforms
|
||||
pebble install --emulator emery # install on the emery emulator
|
||||
pebble install --phone <ip> # install to a paired phone
|
||||
```
|
||||
|
||||
## Target platforms
|
||||
|
||||
`targetPlatforms` in `package.json` controls which watches you build for. The
|
||||
modern Pebble hardware is **emery** (Pebble Time 2), **gabbro** (Pebble Round
|
||||
2), and **flint** (Pebble 2 Duo); the original Pebble platforms (aplite,
|
||||
basalt, chalk, diorite) are included by default for backwards compatibility.
|
||||
|
||||
## Project layout
|
||||
|
||||
```
|
||||
src/c/ C source for the watchapp
|
||||
src/pkjs/ PebbleKit JS (phone-side) source, if any
|
||||
worker_src/c/ Background worker source, if any
|
||||
resources/ Images, fonts, and other bundled resources
|
||||
package.json Project metadata (UUID, platforms, resources, message keys)
|
||||
wscript Build rules — usually no need to edit
|
||||
```
|
||||
|
||||
By default this project is configured as a watchapp. To make it a watchface,
|
||||
set `pebble.watchapp.watchface` to `true` in `package.json`.
|
||||
|
||||
## Documentation
|
||||
|
||||
Full SDK docs, tutorials, and API reference: <https://developer.repebble.com>
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "sumop_frontend",
|
||||
"author": "MakeAwesomeHappen",
|
||||
"version": "1.0.0",
|
||||
"keywords": ["pebble-app"],
|
||||
"private": true,
|
||||
"dependencies": {},
|
||||
"pebble": {
|
||||
"displayName": "SchwertUndMagieOnPebbleFrontend",
|
||||
"uuid": "8039ba8c-f1e8-4620-838d-650fa35335b7",
|
||||
"sdkVersion": "3",
|
||||
"enableMultiJS": true,
|
||||
"targetPlatforms": [
|
||||
"aplite",
|
||||
"basalt",
|
||||
"chalk",
|
||||
"diorite",
|
||||
"emery",
|
||||
"flint",
|
||||
"gabbro"
|
||||
],
|
||||
"watchapp": {
|
||||
"watchface": false
|
||||
},
|
||||
"messageKeys": [
|
||||
"TIME",
|
||||
"COMMAND"
|
||||
],
|
||||
"resources": {
|
||||
"media": []
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
#include <pebble.h>
|
||||
|
||||
enum {
|
||||
KEY_TIME = 0, // Inbound: current time string from companion app
|
||||
KEY_COMMAND = 1 // Outbound: button name sent to companion app
|
||||
};
|
||||
|
||||
static Window *s_main_window;
|
||||
static TextLayer *s_time_layer;
|
||||
static TextLayer *s_hint_layer;
|
||||
|
||||
static char s_time_buffer[32] = "Waiting...";
|
||||
|
||||
static void send_key_to_phone(const char *key_str) {
|
||||
DictionaryIterator *iter;
|
||||
app_message_outbox_begin(&iter);
|
||||
if (iter == NULL) return;
|
||||
dict_write_cstring(iter, KEY_COMMAND, key_str);
|
||||
app_message_outbox_send();
|
||||
}
|
||||
|
||||
static void inbox_received_callback(DictionaryIterator *iterator, void *context) {
|
||||
APP_LOG(APP_LOG_LEVEL_DEBUG, "inbox_received_callback fired");
|
||||
Tuple *time_tuple = dict_find(iterator, KEY_TIME);
|
||||
if (time_tuple) {
|
||||
APP_LOG(APP_LOG_LEVEL_DEBUG, "KEY_TIME found, type=%d", (int)time_tuple->type);
|
||||
} else {
|
||||
APP_LOG(APP_LOG_LEVEL_WARNING, "KEY_TIME not found in message");
|
||||
}
|
||||
if (time_tuple && time_tuple->type == TUPLE_CSTRING) {
|
||||
snprintf(s_time_buffer, sizeof(s_time_buffer), "%s", time_tuple->value->cstring);
|
||||
APP_LOG(APP_LOG_LEVEL_DEBUG, "Setting time: %s", s_time_buffer);
|
||||
text_layer_set_text(s_time_layer, s_time_buffer);
|
||||
layer_mark_dirty(text_layer_get_layer(s_time_layer));
|
||||
}
|
||||
}
|
||||
|
||||
static void up_click_handler(ClickRecognizerRef recognizer, void *context) {
|
||||
send_key_to_phone("UP");
|
||||
}
|
||||
|
||||
static void down_click_handler(ClickRecognizerRef recognizer, void *context) {
|
||||
send_key_to_phone("DOWN");
|
||||
}
|
||||
|
||||
static void select_click_handler(ClickRecognizerRef recognizer, void *context) {
|
||||
send_key_to_phone("SELECT");
|
||||
}
|
||||
|
||||
static void click_config_provider(void *context) {
|
||||
window_single_click_subscribe(BUTTON_ID_UP, up_click_handler);
|
||||
window_single_click_subscribe(BUTTON_ID_DOWN, down_click_handler);
|
||||
window_single_click_subscribe(BUTTON_ID_SELECT, select_click_handler);
|
||||
}
|
||||
|
||||
static void main_window_load(Window *window) {
|
||||
Layer *window_layer = window_get_root_layer(window);
|
||||
GRect bounds = layer_get_bounds(window_layer);
|
||||
|
||||
s_time_layer = text_layer_create(GRect(0, 55, bounds.size.w, 50));
|
||||
text_layer_set_text(s_time_layer, s_time_buffer);
|
||||
text_layer_set_font(s_time_layer, fonts_get_system_font(FONT_KEY_BITHAM_30_BLACK));
|
||||
text_layer_set_text_alignment(s_time_layer, GTextAlignmentCenter);
|
||||
layer_add_child(window_layer, text_layer_get_layer(s_time_layer));
|
||||
|
||||
s_hint_layer = text_layer_create(GRect(0, 130, bounds.size.w, 30));
|
||||
text_layer_set_text(s_hint_layer, "UP / DN / SEL");
|
||||
text_layer_set_font(s_hint_layer, fonts_get_system_font(FONT_KEY_GOTHIC_14));
|
||||
text_layer_set_text_alignment(s_hint_layer, GTextAlignmentCenter);
|
||||
layer_add_child(window_layer, text_layer_get_layer(s_hint_layer));
|
||||
}
|
||||
|
||||
static void main_window_unload(Window *window) {
|
||||
text_layer_destroy(s_time_layer);
|
||||
text_layer_destroy(s_hint_layer);
|
||||
}
|
||||
|
||||
static void init(void) {
|
||||
s_main_window = window_create();
|
||||
window_set_click_config_provider(s_main_window, click_config_provider);
|
||||
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_callback);
|
||||
app_message_open(128, 64);
|
||||
}
|
||||
|
||||
static void deinit(void) {
|
||||
window_destroy(s_main_window);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
init();
|
||||
app_event_loop();
|
||||
deinit();
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
var SERVER = 'http://127.0.0.1:8888';
|
||||
|
||||
// Numeric keys matching the C enum: KEY_TIME=0, KEY_COMMAND=1
|
||||
var KEY_TIME = 0;
|
||||
var KEY_COMMAND = 1;
|
||||
|
||||
function httpGet(url) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', url, true);
|
||||
xhr.onload = function() {
|
||||
if (xhr.status !== 200) {
|
||||
console.log('[SuM] HTTP ' + xhr.status + ' for ' + url);
|
||||
}
|
||||
};
|
||||
xhr.onerror = function() {
|
||||
console.log('[SuM] request failed: ' + url);
|
||||
};
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
function sendTimeToWatch() {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', SERVER + '/time', true);
|
||||
xhr.onload = function() {
|
||||
console.log('[SuM] /time response: ' + xhr.status + ' ' + xhr.responseText);
|
||||
if (xhr.status === 200) {
|
||||
try {
|
||||
var data = JSON.parse(xhr.responseText);
|
||||
var msg = {};
|
||||
msg[KEY_TIME] = data.time;
|
||||
console.log('[SuM] sendAppMessage key=' + KEY_TIME + ' value=' + data.time);
|
||||
Pebble.sendAppMessage(
|
||||
msg,
|
||||
function() { console.log('[SuM] sendAppMessage ok'); },
|
||||
function(e) { console.log('[SuM] sendAppMessage failed: ' + JSON.stringify(e)); }
|
||||
);
|
||||
} catch (err) {
|
||||
console.log('[SuM] JSON parse error: ' + err);
|
||||
}
|
||||
}
|
||||
};
|
||||
xhr.onerror = function() {
|
||||
console.log('[SuM] GET /time failed - is the companion app running?');
|
||||
};
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
Pebble.addEventListener('ready', function() {
|
||||
console.log('[SuM] PebbleKit JS ready');
|
||||
setInterval(sendTimeToWatch, 1000);
|
||||
});
|
||||
|
||||
Pebble.addEventListener('appmessage', function(e) {
|
||||
var command = e.payload[KEY_COMMAND];
|
||||
if (!command) { return; }
|
||||
console.log('[SuM] key: ' + command);
|
||||
httpGet(SERVER + '/key?cmd=' + encodeURIComponent(command));
|
||||
});
|
||||
@@ -0,0 +1,54 @@
|
||||
#
|
||||
# This file is the default set of rules to compile a Pebble application.
|
||||
#
|
||||
# Feel free to customize this to your needs.
|
||||
#
|
||||
import os.path
|
||||
|
||||
top = '.'
|
||||
out = 'build'
|
||||
|
||||
|
||||
def options(ctx):
|
||||
ctx.load('pebble_sdk')
|
||||
|
||||
|
||||
def configure(ctx):
|
||||
"""
|
||||
This method is used to configure your build. ctx.load(`pebble_sdk`) automatically configures
|
||||
a build for each valid platform in `targetPlatforms`. Platform-specific configuration: add your
|
||||
change after calling ctx.load('pebble_sdk') and make sure to set the correct environment first.
|
||||
Universal configuration: add your change prior to calling ctx.load('pebble_sdk').
|
||||
"""
|
||||
ctx.load('pebble_sdk')
|
||||
|
||||
|
||||
def build(ctx):
|
||||
ctx.load('pebble_sdk')
|
||||
|
||||
build_worker = os.path.exists('worker_src')
|
||||
binaries = []
|
||||
|
||||
cached_env = ctx.env
|
||||
for platform in ctx.env.TARGET_PLATFORMS:
|
||||
ctx.env = ctx.all_envs[platform]
|
||||
ctx.set_group(ctx.env.PLATFORM_NAME)
|
||||
app_elf = '{}/pebble-app.elf'.format(ctx.env.BUILD_DIR)
|
||||
ctx.pbl_build(source=ctx.path.ant_glob('src/c/**/*.c'), target=app_elf, bin_type='app')
|
||||
|
||||
if build_worker:
|
||||
worker_elf = '{}/pebble-worker.elf'.format(ctx.env.BUILD_DIR)
|
||||
binaries.append({'platform': platform, 'app_elf': app_elf, 'worker_elf': worker_elf})
|
||||
ctx.pbl_build(source=ctx.path.ant_glob('worker_src/c/**/*.c'),
|
||||
target=worker_elf,
|
||||
bin_type='worker')
|
||||
else:
|
||||
binaries.append({'platform': platform, 'app_elf': app_elf})
|
||||
ctx.env = cached_env
|
||||
|
||||
ctx.set_group('bundle')
|
||||
ctx.pbl_bundle(binaries=binaries,
|
||||
js=ctx.path.ant_glob(['src/pkjs/**/*.js',
|
||||
'src/pkjs/**/*.json',
|
||||
'src/common/**/*.js']),
|
||||
js_entry_file='src/pkjs/index.js')
|
||||
Reference in New Issue
Block a user