Enamel is a python script that generates C helpers from a Clay configuration file to easily get the value of your settings.
Enamel will :
- handle AppMessages automatically (app_message_open, handler registration, ...)
- save/load the value of the settings in the persistant storage automatically
- provide a getter for each of your settings
You can focus on your watchapp/face, Enamel will do the rest !
You can find a basic demo project using Enamel here
- You project must contain a valid configuration file in
src/js/config.json
(see https://github.com/pebble/clay) - Extract Enamel at the root of your Pebble project or create a git submodule
- Copy and paste the following line into the top of your
wscript
:
from enamel.enamel import enamel
- Change the
build
of your wscript from
ctx.pbl_program(source=ctx.path.ant_glob('src/**/*.c'), target=app_elf)
to
ctx(rule = enamel, source='src/js/config.json', target=['enamel.c', 'enamel.h'])
ctx.pbl_program(source=ctx.path.ant_glob('src/**/*.c') + ['enamel.c'], target=app_elf)
- Launch your Pebble build : 2 files (enamel.c and enamel.h) should be generated in
build
and compiled
⚠️
The first time you launch a build, you will get an error message because Jinja2 module is missing.
Just follow the instructions to fix your environment.
If you can't modify the wscript (Cloudpebble development) you can call directly the python script.
Enamel needs the Jinja2 module for the code generation. Install the dependency with
pip install Jinja2
The following command will generate 2 files (enamel.c and enamel.h), you just need to copy them in your project
python enamel.py --appinfo /path/to/your/appinfo.json --config /path/to/your/config.json
Call python enamel.py --help
for help
- Setup your project correctly for Clay : https://github.com/pebble/clay
- Include
enamel.h
in your c file :
#include "enamel.h"
- Initialize enamel in your
init
function :
static void init(void) {
// Initialize Enamel to register App Message handlers and restores settings
enamel_init();
// Let enamel init the appmessage framework
enamel_app_message_open(0, 0, NULL);
...
}
- Deinitialize enamel in your
deinit
function :
static void deinit(void) {
...
// Deinit Enamel to unregister App Message handlers and save settings
enamel_deinit();
}
- (Optional) Register a custom
in_received_handler
in yourinit
.
This handler will be automatically called by enamel when a setting is received.
static void in_received_handler(DictionaryIterator *iter, void *context) {
APP_LOG(0, "Settings received %d", enamel_get_myinteger());
window_set_background_color(window, enamel_get_background());
// do what you want here
// you will probably udpate your textlayers, colors, ... with the new settings
// and mark your layers dirty
}
...
static void init(void) {
// Initialize Enamel to register App Message handlers and restores settings
enamel_init();
// Register our custom receive handler
enamel_app_message_open(0, 0, in_received_handler);
...
}
- Get the value of your setting with :
enamel_get_Mysetting(); // where 'Mysetting' is an appKey in your configuration file
- (Advanced usage) You can manually manage your appmessage and call
enamel_inbox_received_handle
to notify enamel when settings are received :
static void in_received_handler(DictionaryIterator *iter, void *context) {
// Do some stuff with the dictionary
// Call enamel to parse the dictionary if it contains settings
enamel_inbox_received_handle(iter, context);
}
...
static void init(void) {
// Initialize Enamel to register App Message handlers and restores settings
enamel_init();
// handle appmessages manually
app_message_register_inbox_received(in_received_handler);
app_message_open(500, 500);
...
}
Method | Description |
---|---|
void enamel_init() |
Initialize Enamel and read settings from persistant storage |
void enamel_app_message_open(const uint32_t size_inbound, const uint32_t size_outbound, AppMessageInboxReceived received_callback) |
Let Enamel manages the appmessages. If size_inbound is 0 , Enamel will calculate the inbound size automatically for you. The received_callback will be called by Enamel when a message is received |
void enamel_deinit() |
Deinitialize Enamel and save the settings in the persistant storage |
<type> enamel_get_<appKeyId>() |
Return the value for the setting appKeyId |
<type> enamel_get_<appKeyId>(uint16_t index_) |
Only relevant for checkboxgroup . Return the value at given index for the setting appKeyId |
uint16_t enamel_get_<appKeyId>_count() |
Only relevant for checkboxgroup . Return the number of values for the setting appKeyId |
uint16_t enamel_get_inbox_size() |
Return the size of a message containing all the settings (usefull if the appmessage is setup manually) |
Clay Type | Type returned by the getter |
---|---|
input |
char* |
toggle |
bool |
color |
GColor |
select/radiogroup |
char* or enum |
checkboxgroup |
char* or enum |
slider |
int32_t |
If the value of the options are string
in the config.json
, Enamel will generate a char*
getter
If the value of the options are integer
, Enamel will generate an enum
mapping all the possible values for this setting and the getter will return this enum
For the given setting :
{
"type": "radiogroup",
"appKey": "favorite_food",
"label": "Favorite Food",
"defaultValue": "1",
"options": [
{
"label": "Sushi",
"value": 0
},
{
"label": "Pizza",
"value": 1
},
{
"label": "Burgers",
"value": 2
}
]
}
Enamel will generate
typedef enum {
FAVORITE_FOOD_SUSHI = 0,
FAVORITE_FOOD_PIZZA = 1,
FAVORITE_FOOD_BURGER = 2,
} FAVORITE_FOODValue;
FAVORITE_FOODValue enamel_get_favorite_food();
You can then easily code switch case for this setting
switch(enamel_get_favorite_food()){
case FAVORITE_FOOD_SUSHI : break; //do something
case FAVORITE_FOOD_PIZZA : break; //do something
case FAVORITE_FOOD_BURGER : break; //do something
}
Enamel will also generate a constant for your slider containing the 'precision' of your slider, e.g.
#define MY_SLIDER_PRECISION 100