Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Inspired by the visual language of the Timeline found on the Pebble Time, TimeSt
* Colorful: includes over 20 preset color schemes, and also supports custom colors using any color the Pebble Time can display—also supports saving, loading, and sharing custom presets!
* Configurable: TimeStyle features a wide variety of different complications, including step counts, sleep times, weather forecasts, the week number, seconds, the time in another time zone, the battery level, and more.
* Keeps you informed: TimeStyle automatically displays notifications when the battery is low or when your phone disconnects.
* Battery reporting: Send battery status updates to a remote HTTP endpoint (e.g., Home Assistant webhook) when the battery level changes or the charger is connected or disconnected. This feature is adapted from [ForeCal](https://github.com/skylord123/ForeCal).
* Works in 30 different languages, more than any other Pebble face: English, French, German, Spanish, Italian, Dutch, Turkish, Czech, Slovak, Portuguese, Greek, Swedish, Polish, Romanian, Vietnamese, Catalan, Norwegian, Russian, Estonian, Basque, Finnish, Danish, Lithuanian, Slovenian, Hungarian, Croatian, Serbian, Irish, Latviann, and Ukrainian.

## Want to try it?
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@
"WeatherUseNightIcon",
"SettingWidget0ID",
"SettingWidget1ID",
"SettingWidget2ID"
"SettingWidget2ID",
"BatteryPercent",
"IsCharging"
],
"resources": {
"media": [
Expand Down
5 changes: 4 additions & 1 deletion src/c/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,12 @@ void bluetoothStateChanged(bool newConnectionState) {
Sidebar_redraw();
}

// force the sidebar to redraw any time the battery state changes
// force the sidebar to redraw any time the battery state changes and send battery data to phone for remote reporting
void batteryStateChanged(BatteryChargeState charge_state) {
// Redraw sidebar to show updated battery icon
Sidebar_redraw();
// Send battery data to phone for remote reporting
messaging_sendBatteryData(charge_state.charge_percent, charge_state.is_charging);
}

// fixes for disappearing elements after notifications
Expand Down
12 changes: 11 additions & 1 deletion src/c/messaging.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@ void messaging_requestNewWeatherData() {
app_message_outbox_send();
}

void messaging_sendBatteryData(uint8_t batteryPercent, bool isCharging) {
DictionaryIterator *iter;
app_message_outbox_begin(&iter);

dict_write_uint8(iter, MESSAGE_KEY_BatteryPercent, batteryPercent);
dict_write_uint8(iter, MESSAGE_KEY_IsCharging, isCharging ? 1 : 0);

app_message_outbox_send();
}

void messaging_init(void (*processed_callback)(void)) {
// register my custom callback
message_processed_callback = processed_callback;
Expand All @@ -24,7 +34,7 @@ void messaging_init(void (*processed_callback)(void)) {
app_message_register_outbox_sent(outbox_sent_callback);

// Open AppMessage
app_message_open(512, 8);
app_message_open(512, 512);

// APP_LOG(APP_LOG_LEVEL_DEBUG, "Watch messaging is started!");
app_message_register_inbox_received(inbox_received_callback);
Expand Down
1 change: 1 addition & 0 deletions src/c/messaging.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <pebble.h>

void messaging_requestNewWeatherData();
void messaging_sendBatteryData(uint8_t batteryPercent, bool isCharging);

void messaging_init(void (*message_processed_callback)(void));
void inbox_received_callback(DictionaryIterator *iterator, void *context);
Expand Down
78 changes: 76 additions & 2 deletions src/pkjs/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,59 @@

var weather = require('./weather');

var CONFIG_VERSION = 9;
var CONFIG_VERSION = 10;
// var BASE_CONFIG_URL = 'http://localhost:3001/';
var BASE_CONFIG_URL = 'http://freakified.github.io/TimeStylePebble/';
var BASE_CONFIG_URL = 'https://caco3.github.io/TimeStylePebble/';

// Remote battery endpoint configuration
var remoteEndpointUrl = '';
var remoteEndpointToken = '';

// Check if remote endpoint is configured
function isEndpointConfigured() {
return remoteEndpointUrl && remoteEndpointUrl.trim() !== '';
}

// Send battery data to remote endpoint via HTTP POST with Bearer token
function sendBatteryToEndpoint(batteryPercent, isCharging) {
if (!isEndpointConfigured()) {
console.log('Remote endpoint not configured, skipping battery sync');
return;
}

console.log('Sending battery data to remote endpoint: ' + batteryPercent + '% (charging: ' + isCharging + ')');

var req = new XMLHttpRequest();
req.open('POST', remoteEndpointUrl, true);
req.setRequestHeader('Content-Type', 'application/json');

// Add Bearer token authorization if provided
if (remoteEndpointToken && remoteEndpointToken.trim() !== '') {
req.setRequestHeader('Authorization', 'Bearer ' + remoteEndpointToken.trim());
}

req.onload = function() {
if (req.readyState === 4) {
if (req.status >= 200 && req.status < 300) {
console.log('Battery data sent successfully to remote endpoint');
} else {
console.log('Failed to send battery data to remote endpoint. Status: ' + req.status);
}
}
};

req.onerror = function() {
console.log('Network error sending battery data to remote endpoint');
};

var payload = JSON.stringify({
battery_percent: batteryPercent,
is_charging: isCharging,
timestamp: new Date().toISOString()
});

req.send(payload);
}

// Listen for when the watchface is opened
Pebble.addEventListener('ready',
Expand All @@ -15,6 +65,14 @@ Pebble.addEventListener('ready',
window.localStorage.setItem('disable_weather', 'no');
}

// Load remote endpoint configuration from localStorage
if (window.localStorage.getItem('remote_endpoint_url')) {
remoteEndpointUrl = window.localStorage.getItem('remote_endpoint_url');
}
if (window.localStorage.getItem('remote_endpoint_token')) {
remoteEndpointToken = window.localStorage.getItem('remote_endpoint_token');
}

console.log('the wdisabled value is: "' + window.localStorage.getItem('disable_weather') + '"');
// if applicable, get the weather data
if (window.localStorage.getItem('disable_weather') != 'yes') {
Expand All @@ -29,6 +87,11 @@ Pebble.addEventListener('appmessage',
function (msg) {
console.log('Recieved message: ' + JSON.stringify(msg.payload));

// Check if this is a battery status update
if (msg.payload.BatteryPercent !== undefined && msg.payload.IsCharging !== undefined) {
sendBatteryToEndpoint(msg.payload.BatteryPercent, msg.payload.IsCharging);
}

// in the case of recieving this, we assume the watch does, in fact, need weather data
window.localStorage.setItem('disable_weather', 'no');
weather.updateWeather();
Expand Down Expand Up @@ -244,6 +307,17 @@ Pebble.addEventListener('webviewclosed', function (e) {
}
}

// remote battery endpoint settings
if (configData.remote_endpoint_url) {
remoteEndpointUrl = configData.remote_endpoint_url;
window.localStorage.setItem('remote_endpoint_url', configData.remote_endpoint_url);
}

if (configData.remote_endpoint_token) {
remoteEndpointToken = configData.remote_endpoint_token;
window.localStorage.setItem('remote_endpoint_token', configData.remote_endpoint_token);
}

// determine whether or not the weather checking should be enabled
var disableWeather;

Expand Down