diff --git a/README.md b/README.md index 0148905..aa92050 100755 --- a/README.md +++ b/README.md @@ -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? diff --git a/package.json b/package.json index e3c8b7a..d1c4ec2 100755 --- a/package.json +++ b/package.json @@ -63,7 +63,9 @@ "WeatherUseNightIcon", "SettingWidget0ID", "SettingWidget1ID", - "SettingWidget2ID" + "SettingWidget2ID", + "BatteryPercent", + "IsCharging" ], "resources": { "media": [ diff --git a/src/c/main.c b/src/c/main.c index 41808e6..e7c907d 100755 --- a/src/c/main.c +++ b/src/c/main.c @@ -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 diff --git a/src/c/messaging.c b/src/c/messaging.c index 6350f6a..26c22fd 100755 --- a/src/c/messaging.c +++ b/src/c/messaging.c @@ -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; @@ -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); diff --git a/src/c/messaging.h b/src/c/messaging.h index 534513b..c1ea04e 100755 --- a/src/c/messaging.h +++ b/src/c/messaging.h @@ -2,6 +2,7 @@ #include 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); diff --git a/src/pkjs/index.js b/src/pkjs/index.js index 670e37f..2f4e6ac 100755 --- a/src/pkjs/index.js +++ b/src/pkjs/index.js @@ -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', @@ -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') { @@ -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(); @@ -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;