Introduction
Starting new projects on any platform can be a laborious process, especially if you end up doing the same boilerplate code over and over again.
Background
The command line based Pebble environment can create a couple of basic project types, which I usually then proceed to reformat into my own personal style before I start working on whatever the project actually is.
I decided to see if I could find how the Pebble projects were created and was pleasantly surprised to find it was all done by easily editable Python code.
As Comes
The Pebble command provides four standard templates:
- Standard an app that detects button presses and prints what was pressed to the screen (default)
- Worker an app for running in the background (--worker)
- Simple an empty project (--simple)
- Javascript a JavaScript based app (--javascript)
For example:
pebble new-project --simple MyProject
Will produce an empty project in a folder called MyProject.
All these project templates are Python strings with embedded replacement bits & pieces to customize the new project. These are easily editable with your favourite editor.
First Useful Changes
The files we'll be looking at are:
<Pebble SDK>/pebble-tool/pebble_tool/commands/sdk/create.py
<Pebble SDK>/pebble-tool/pebble_tool/sdk/templates.py
The first, create.py, constructs a project folder hierarchy based on the arguments given.
The second file, templates.py, holds the actual text for the files within the project.
The first useful change we can make is to find DICT_DUMMY_APPINFO
and change the default company_name
to our own. Something that can easily be forgotten about while working on a project.
Also in templates.py is the source of the standard app, looking for FILE_DUMMY_MAIN
we can tweak the format to our preferred style and when we start a new project it's ready to go with no messing about.
NOTE: When editing Python code, the indenting is crucial, each line at a particular indent level should be using the same type of indenting, either tabs or spaces, but not a mixture.
Taking it Further
Now the basics have been uncovered we have the opportunity to do more. When I'm starting a new project, I don't really want to start with the same basic shell code, for example, I have a fledgling game "engine" I'd like to start with. Or if I'm writing a watchface, I'd like to start with a watchface shell with no button handling and a basic tick handling structure already in place.
We'll create a new project type for a watchface.
Extending the available project types in create.py, I can add a new option "--watchface
" near the end of the file:
parser.add_argument("--watchface", action="store_true", help="Generate a watchface.")
And, above that, alter the code that generates the project, I have expanded the if
/else
which makes it easier to neatly extend later:
with open(os.path.join(project_src, "{}.c".format(project_name)), "w") as f:
if args.simple:
f.write(FILE_SIMPLE_MAIN)
elif args.watchface:
f.write(FILE_DUMMY_WATCHFACE)
else:
f.write(FILE_DUMMY_MAIN)
appinfo_dummy = DICT_DUMMY_APPINFO.copy()
appinfo_dummy['uuid'] = str(uuid.uuid4())
appinfo_dummy['project_name'] = project_name
if args.watchface:
appinfo_dummy['is_watchface'] = 'true'
with open(os.path.join(project_root, "appinfo.json"), "w") as f:
f.write(FILE_DUMMY_APPINFO.substitute(**appinfo_dummy))
And finally for this file, near the top, we add a refernce to the new template string
(which will be added to templates.py shortly):
from pebble_tool.sdk.templates import (FILE_SIMPLE_MAIN, FILE_DUMMY_MAIN, FILE_DUMMY_APPINFO,
DICT_DUMMY_APPINFO, FILE_GITIGNORE, FILE_DUMMY_JAVASCRIPT_SRC,
FILE_WSCRIPT, FILE_DUMMY_WORKER, FILE_DUMMY_WATCHFACE)
f.write(FILE_SIMPLE_MAIN)
elif args.watchface:
f.write(FILE_DUMMY_WATCHFACE)
else:
f.write(FILE_DUMMY_MAIN)
appinfo_dummy = DICT_DUMMY_APPINFO.copy()
appinfo_dummy['uuid'] = str(uuid.uuid4())
appinfo_dummy['project_name'] = project_name
if args.watchface:
appinfo_dummy['is_watchface'] = 'true'
with open(os.path.join(project_root, "appinfo.json"), "w") as f:
f.write(FILE_DUMMY_APPINFO.substitute(**appinfo_dummy))
At the bottom of templates.py, we can add the shell for our watchface
:
FILE_DUMMY_WATCHFACE = """#include <pebble.h>
static Window* window = NULL;
static TextLayer* timeLayer = NULL;
static void tickHandler(struct tm* tickTime, TimeUnits unitsChanged)
{
static char timeString[20];
if (clock_is_24h_style())
strftime(timeString, sizeof(timeString), "%H:%M", tickTime);
else
strftime(timeString, sizeof(timeString), "%I:%M", tickTime);
text_layer_set_text(timeLayer, timeString);
}
static void windowLoad(Window* window)
{
Layer* windowLayer = window_get_root_layer(window);
GRect bounds = layer_get_bounds(windowLayer);
timeLayer = text_layer_create((GRect) { .origin = { 0, 72 }, .size = { bounds.size.w, 20 } });
text_layer_set_text(timeLayer, "Pebble");
text_layer_set_text_alignment(timeLayer, GTextAlignmentCenter);
layer_add_child(windowLayer, text_layer_get_layer(timeLayer));
}
static void windowUnload(Window *window)
{
text_layer_destroy(timeLayer);
}
static void init(void)
{
window = window_create();
window_set_window_handlers(window, (WindowHandlers) {
.load = windowLoad,
.unload = windowUnload,
});
const bool animated = true;
window_stack_push(window, animated);
tick_timer_service_subscribe(MINUTE_UNIT, tickHandler);
}
static void deinit(void)
{
tick_timer_service_unsubscribe();
window_destroy(window);
}
int main(void)
{
init();
APP_LOG(APP_LOG_LEVEL_DEBUG, "Done initializing, pushed window: %p", window);
app_event_loop();
deinit();
}
"""
Now we can create a new project.
pebble new-project --watchface WatchFaceTest
No messing about, we now have a watchface
project ready to work on. Here it is running on the Aplite emulator:
We are now in a position to add any type of started project we want, including adding various options to allow for adding extra features, for example, if we want to use the flash storage or accelerometer, etc.
The Future - SDK Updates
Pebble release new SDKs fairly regularly, but they have been following this new project method for a while now so there should be no issues merging over your personalised project templates.
Here are my personalised versions of create.py and templates.py: CustomPebbleTemplate.tar.gz
History
- 2015-10-15: Added screen shot and extension possibilities
- 2015-10-08: First draft