Merge branch 'espruino:master' into Weather-Feels-Like-Updates

master
RKBoss6 2025-08-06 13:09:22 -04:00 committed by GitHub
commit 54f8807af8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 1094 additions and 416 deletions

View File

@ -19,3 +19,4 @@
0.18: Fix UTF8 conversion (check for `font` library, not `fonts`) 0.18: Fix UTF8 conversion (check for `font` library, not `fonts`)
0.19: Convert numeric weather values to int from BangleDumpWeather shortcut 0.19: Convert numeric weather values to int from BangleDumpWeather shortcut
0.20: Add feels-like temperature data field to weather parsing from BangleDumpWeather shortcut. 0.20: Add feels-like temperature data field to weather parsing from BangleDumpWeather shortcut.
0.21: Add BangleDumpLocation shortcut, to update location data on watch without needing to use the watch's GPS, and added more app names/ ids.

View File

@ -35,20 +35,21 @@ for now. It's just a few bucks/pounds/euro's.
If you like to try a free app first, you can always use NRF Toolbox or If you like to try a free app first, you can always use NRF Toolbox or
Bluetooth BLE Device Finder to find and connect your Bangle. Bluetooth BLE Device Finder to find and connect your Bangle.
### Weather and Calendar ### Weather, Calendar, and Location
By using the `Shortcuts` app on your phone, you can send weather and calendar data to your watch. This works by sending a notification, which is read by the watch through ANCS. The watch then parses the notification for the data. By using the `Shortcuts` app on your phone, you can send weather, calendar, and location data to your watch. This works by sending a notification, which is read by the watch through ANCS. The watch then parses the notification for the data.
While you may write your own shortcuts if you prefer (for example, to get weather from a different source), two are provided: While you may write your own shortcuts if you prefer (for example, to get weather from a different source), three are provided:
- Calendar shortcut: https://www.icloud.com/shortcuts/4eac12548b4c424dbcdb1bd58cff338f - [Calendar shortcut](https://www.icloud.com/shortcuts/4eac12548b4c424dbcdb1bd58cff338f)
- Weather shortcut: https://www.icloud.com/shortcuts/106c68bfac3746fe9a55761a3be8d092 - [Weather shortcut](https://www.icloud.com/shortcuts/106c68bfac3746fe9a55761a3be8d092)
- [Location shortcut](https://www.icloud.com/shortcuts/853c41e09a8e491f893a63b464d73ea1)
The weather shortcut requires an OpenWeatherMap api key, which you can get for free from https://openweathermap.org/api. The shortcut will prompt you for this when you add it to your phone. Note: The shortcuts must keep the names defaulted by the shortcut in order for the watch to detect the weather, calender, or location data. If you rename it from `BangleDump...` to something else, it will no longer get the info, and just display it as a notification on the watch.
These shortcuts can also be automated to run periodically, for example every hour, using the `Automation` tab in the Shortcuts app. These shortcuts can also be automated to run periodically, for example every hour, using the `Automation` tab in the Shortcuts app.
The shortcuts will send a notification, which can be annoying. One potential workaround for this would be to create a focus mode, and have an automation: The shortcuts will send a notification. Even though the notification is deleted as soon as Bangle.js receives it, it can be quite annoying. One potential workaround for this would be to create a focus mode, and have an automation:
- activate the focus mode (hiding notifications from the shortcut) - activate the focus mode (hiding notifications from the shortcut)
- run the shortcut - run the shortcut
- deactivate the focus mode - deactivate the focus mode
@ -60,4 +61,9 @@ Please file any issues on https://github.com/espruino/BangleApps/issues/new?titl
## Creator ## Creator
Gordon Williams - Gordon Williams
## Contributors
- RKBoss6
- stweedo

View File

@ -78,28 +78,56 @@ E.on('notify',msg=>{
"com.apple.reminders": "Reminders", "com.apple.reminders": "Reminders",
"com.apple.shortcuts": "Shortcuts", "com.apple.shortcuts": "Shortcuts",
"com.apple.TestFlight": "TestFlight", "com.apple.TestFlight": "TestFlight",
"com.apple.ScreenTimeNotifications": "ScreenTime", "com.apple.ScreenTimeNotifications": "Screen Time",
"com.apple.wifid.usernotification": "WiFi", "com.apple.wifid.usernotification": "WiFi",
"com.apple.Maps": "Maps",
"com.apple.Music": "Apple Music",
"com.apple.AppStore": "App Store",
"com.apple.Preferences": "Settings",
"com.apple.calculator": "Calculator",
"com.apple.camera": "Camera",
"com.apple.weather": "Weather",
"com.apple.VoiceMemos": "Voice Memos",
"com.apple.News": "News",
"com.apple.tv": "Apple TV",
"com.apple.findmy": "Find My",
"com.apple.compass": "Compass",
"com.apple.measure": "Measure",
"com.atebits.Tweetie2": "Twitter", "com.atebits.Tweetie2": "Twitter",
"com.burbn.instagram" : "Instagram", "com.burbn.instagram": "Instagram",
"com.facebook.Facebook": "Facebook", "com.facebook.Facebook": "Facebook",
"com.facebook.Messenger": "Messenger", "com.facebook.Messenger": "Messenger",
"com.google.Chromecast" : "Google Home", "com.google.Chromecast": "Google Home",
"com.google.Gmail" : "GMail", "com.google.Gmail": "GMail",
"com.google.hangouts" : "Hangouts", "com.google.hangouts": "Hangouts",
"com.google.ios.youtube" : "YouTube", "com.google.ios.youtube": "YouTube",
"com.hammerandchisel.discord" : "Discord", "com.google.ios.chrome": "Google Chrome",
"com.ifttt.ifttt" : "IFTTT", "com.google.Maps": "Google Maps",
"com.jumbo.app" : "Jumbo", "com.google.Drive": "Google Drive",
"com.linkedin.LinkedIn" : "LinkedIn", "com.google.GoogleMobile": "Google",
"com.hammerandchisel.discord": "Discord",
"com.ifttt.ifttt": "IFTTT",
"com.jumbo.app": "Jumbo",
"com.linkedin.LinkedIn": "LinkedIn",
"com.marktplaats.iphone": "Marktplaats", "com.marktplaats.iphone": "Marktplaats",
"com.microsoft.Office.Outlook" : "Outlook Mail", "com.microsoft.Office.Outlook": "Outlook Mail",
"com.nestlabs.jasper.release" : "Nest", "com.microsoft.Office.Word": "Microsoft Word",
"com.netflix.Netflix" : "Netflix", "com.microsoft.Office.Excel": "Microsoft Excel",
"com.reddit.Reddit" : "Reddit", "com.microsoft.Office.Powerpoint": "Microsoft PowerPoint",
"com.nestlabs.jasper.release": "Nest",
"com.netflix.Netflix": "Netflix",
"com.reddit.Reddit": "Reddit",
"com.skype.skype": "Skype", "com.skype.skype": "Skype",
"com.skype.SkypeForiPad": "Skype", "com.skype.SkypeForiPad": "Skype",
"com.spotify.client": "Spotify", "com.spotify.client": "Spotify",
"com.soundcloud.TouchApp": "SoundCloud",
"com.disney.disneyplus": "Disney+",
"com.hbo.hbonow": "HBO Max",
"com.amazon.Amazon": "Amazon Shopping",
"com.amazon.AmazonVideo": "Prime Video",
"com.dropbox.Dropbox": "Dropbox",
"com.evernote.iPhone.Evernote": "Evernote",
"com.trello": "Trello",
"com.storytel.iphone": "Storytel", "com.storytel.iphone": "Storytel",
"com.strava.stravaride": "Strava", "com.strava.stravaride": "Strava",
"com.tinyspeck.chatlyio": "Slack", "com.tinyspeck.chatlyio": "Slack",
@ -110,20 +138,22 @@ E.on('notify',msg=>{
"com.valvesoftware.Steam": "Steam", "com.valvesoftware.Steam": "Steam",
"com.vilcsak.bitcoin2": "Coinbase", "com.vilcsak.bitcoin2": "Coinbase",
"com.wordfeud.free": "WordFeud", "com.wordfeud.free": "WordFeud",
"com.yourcompany.PPClient": "PayPal", "com.paypal.PPClient": "PayPal",
"com.zhiliaoapp.musically": "TikTok", "com.zhiliaoapp.musically": "TikTok",
"com.pinterest": "Pinterest",
"com.tumblr.tumblr": "Tumblr",
"de.no26.Number26": "N26", "de.no26.Number26": "N26",
"io.robbie.HomeAssistant": "Home Assistant", "io.robbie.HomeAssistant": "Home Assistant",
"net.superblock.Pushover": "Pushover", "net.superblock.Pushover": "Pushover",
"net.weks.prowl": "Prowl", "net.weks.prowl": "Prowl",
"net.whatsapp.WhatsApp": "WhatsApp", "net.whatsapp.WhatsApp": "WhatsApp",
"nl.ah.Appie": "Albert Heijn",
"nl.postnl.TrackNTrace": "PostNL", "nl.postnl.TrackNTrace": "PostNL",
"org.whispersystems.signal": "Signal", "org.whispersystems.signal": "Signal",
"ph.telegra.Telegraph": "Telegram", "ph.telegra.Telegraph": "Telegram",
"tv.twitch": "Twitch", "tv.twitch": "Twitch"
// could also use NRF.ancsGetAppInfo(msg.appId) here };
};
//if (appNames[msg.appId]) msg.a //if (appNames[msg.appId]) msg.a
if (msg.title === "BangleDumpCalendar") { if (msg.title === "BangleDumpCalendar") {
@ -191,7 +221,7 @@ E.on('notify',msg=>{
wind: d.wind, wind: d.wind,
wdir: d.wdir, wdir: d.wdir,
loc: d.loc loc: d.loc
} };
// Convert string fields to numbers for iOS weather shortcut // Convert string fields to numbers for iOS weather shortcut
const numFields = ['code', 'wdir', 'temp','feels', 'hi', 'lo', 'hum', 'wind', 'uv', 'rain']; const numFields = ['code', 'wdir', 'temp','feels', 'hi', 'lo', 'hum', 'wind', 'uv', 'rain'];
numFields.forEach(field => { numFields.forEach(field => {
@ -201,6 +231,47 @@ E.on('notify',msg=>{
NRF.ancsAction(msg.uid, false); NRF.ancsAction(msg.uid, false);
return; return;
} }
if (msg.title === "BangleDumpLocation") {
const d = JSON.parse(msg.message);
/* Example:
{"lat":"2912.0744", "lon":"2333.332", "city":"Chicago"}*/
let locationJson = {
t: "location",
lat:d.lat,
lon:d.lon,
city:d.city
};
// Convert string fields to numbers
const numFields = ['lat', 'lon'];
numFields.forEach(field => {
if (locationJson[field] != null) locationJson[field] = +locationJson[field];
});
//load mylocation file
let myLocationJson = Object.assign({
lat: d.lat,
lon: d.lon,
location:d.city
}, require("Storage").readJSON("mylocation.json", true) || {});
//remove notification from phone
NRF.ancsAction(msg.uid, false);
if(Math.abs(myLocationJson.lat - locationJson.lat) < 0.0001 && Math.abs(myLocationJson.lon -locationJson.lon) < 0.0001){
//same location, do not write
return;
}
myLocationJson.lon=locationJson.lon;
myLocationJson.lat=locationJson.lat;
myLocationJson.location=locationJson.city;
require("Storage").write("mylocation.json",myLocationJson);
return;
}
require("messages").pushMessage({ require("messages").pushMessage({
t : msg.event, t : msg.event,

View File

@ -1,11 +1,12 @@
{ {
"id": "ios", "id": "ios",
"name": "iOS Integration", "name": "iOS Integration",
"version": "0.20", "version": "0.21",
"description": "Display/pull notifications, music, weather, and agenda from iOS devices", "description": "Display/pull notifications, music, weather, location, and agenda from iOS devices",
"icon": "app.png", "icon": "app.png",
"tags": "tool,system,ios,apple,messages,notifications", "tags": "tool,system,ios,apple,messages,notifications",
"dependencies": {"messages":"module"}, "dependencies": {"messages":"module"},
"dependencies" : {"mylocation":"app" },
"supports": ["BANGLEJS","BANGLEJS2"], "supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md", "readme": "README.md",
"storage": [ "storage": [

View File

@ -14,3 +14,4 @@
Log additional timestamp for trace log Log additional timestamp for trace log
0.11: Minor code improvements 0.11: Minor code improvements
0.12: Round monotonic percentage, rename to stable percentage/voltage 0.12: Round monotonic percentage, rename to stable percentage/voltage
0.13: Fix stable percentage not updating

View File

@ -3,7 +3,7 @@
require('Storage').readJSON("powermanager.default.json", true) || {}, require('Storage').readJSON("powermanager.default.json", true) || {},
require('Storage').readJSON("powermanager.json", true) || {} require('Storage').readJSON("powermanager.json", true) || {}
); );
if (settings.log) { if (settings.log) {
let logFile = require('Storage').open("powermanager.log","a"); let logFile = require('Storage').open("powermanager.log","a");
let def = require('Storage').readJSON("powermanager.def.json", true) || {}; let def = require('Storage').readJSON("powermanager.def.json", true) || {};
@ -12,7 +12,7 @@
let hw = require('Storage').readJSON("powermanager.hw.json", true) || {}; let hw = require('Storage').readJSON("powermanager.hw.json", true) || {};
if (!hw.start) hw.start = Date.now(); if (!hw.start) hw.start = Date.now();
if (!hw.power) hw.power = {}; if (!hw.power) hw.power = {};
const saveEvery = 1000 * 60 * 5; const saveEvery = 1000 * 60 * 5;
const TO_WRAP = ["GPS","Compass","Barometer","HRM","LCD"]; const TO_WRAP = ["GPS","Compass","Barometer","HRM","LCD"];
@ -28,9 +28,7 @@
require('Storage').writeJSON("powermanager.hw.json", hw); require('Storage').writeJSON("powermanager.hw.json", hw);
} }
} }
setInterval(save, saveEvery); setInterval(save, saveEvery);
E.on("kill", ()=>{ E.on("kill", ()=>{
@ -77,7 +75,6 @@
})(Bangle[functionName]); })(Bangle[functionName]);
} }
let wrapDeferred = ((o,t) => (a) => { let wrapDeferred = ((o,t) => (a) => {
if (a == eval || typeof a == "string") { if (a == eval || typeof a == "string") {
return o.apply(this, arguments); return o.apply(this, arguments);
@ -133,25 +130,19 @@
handleCharging(Bangle.isCharging()); handleCharging(Bangle.isCharging());
} }
var savedBatPercent=E.getBattery();
if (settings.forceMonoPercentage){ if (settings.forceMonoPercentage){
var newPercent =Math.round((E.getBattery()+E.getBattery()+E.getBattery()+E.getBattery()+E.getBattery()+E.getBattery())/6); var p = Math.round((E.getBattery()+E.getBattery()+E.getBattery()+E.getBattery())/4);
var op = E.getBattery;
E.getBattery = function() { E.getBattery = function() {
var current = Math.round((op()+op()+op()+op())/4);
if(Bangle.isCharging()){ if (Bangle.isCharging() && current > p) p = current;
if(newPercent > savedBatPercent) if (!Bangle.isCharging() && current < p) p = current;
savedBatPercent = newPercent; return p;
}else{ };
if(newPercent < savedBatPercent)
savedBatPercent = newPercent;
}
return savedBatPercent;
};
} }
if (settings.forceMonoVoltage){ if (settings.forceMonoVoltage){
var v = (NRF.getBattery()+NRF.getBattery()+NRF.getBattery()+NRF.getBattery()+NRF.getBattery()+NRF.getBattery())/6; var v = (NRF.getBattery()+NRF.getBattery()+NRF.getBattery()+NRF.getBattery())/4;
var ov = NRF.getBattery; var ov = NRF.getBattery;
NRF.getBattery = function() { NRF.getBattery = function() {
var current = (ov()+ov()+ov()+ov())/4; var current = (ov()+ov()+ov()+ov())/4;

View File

@ -2,7 +2,7 @@
"id": "powermanager", "id": "powermanager",
"name": "Power Manager", "name": "Power Manager",
"shortName": "Power Manager", "shortName": "Power Manager",
"version": "0.12", "version": "0.13",
"description": "Allow configuration of warnings for battery charging, stabilization of voltage, stabilization of battery percentage, and battery logging.", "description": "Allow configuration of warnings for battery charging, stabilization of voltage, stabilization of battery percentage, and battery logging.",
"icon": "app.png", "icon": "app.png",
"type": "bootloader", "type": "bootloader",

View File

@ -60,4 +60,5 @@
Lower accuracy of barometer data to ~1cm (saves about 15b/record) Lower accuracy of barometer data to ~1cm (saves about 15b/record)
0.47: Fix 'blip' on speed map on some recordings 0.47: Fix 'blip' on speed map on some recordings
Ensure Battery voltage is only stored to 0.01v Ensure Battery voltage is only stored to 0.01v
Add graphs for Steps+Battery Add graphs for Steps+Battery
0.48: Add ability to log average acceleration values

View File

@ -19,6 +19,7 @@ You can record
* **BAT** Battery percentage and voltage * **BAT** Battery percentage and voltage
* **Steps** Steps counted by the step counter * **Steps** Steps counted by the step counter
* **Baro** (Bangle.js 2) Using the built-in barometer to record Temperature, Pressure and Altitude * **Baro** (Bangle.js 2) Using the built-in barometer to record Temperature, Pressure and Altitude
* **Accel** Average acceleration values in X,Y and Z
* **Core** CoreTemp body temperature *if* you have a CoreTemp device and the https://banglejs.com/apps/?id=coretemp app installed * **Core** CoreTemp body temperature *if* you have a CoreTemp device and the https://banglejs.com/apps/?id=coretemp app installed
You can then start/stop recording from the Recorder app itself (and as long as widgets are You can then start/stop recording from the Recorder app itself (and as long as widgets are
@ -29,12 +30,29 @@ Some apps like the [Run app](https://banglejs.com/apps/?id=run) are able to auto
as well. They need to define a `foobar.recorder.js` file - see the `getRecorders` as well. They need to define a `foobar.recorder.js` file - see the `getRecorders`
function in `lib.js` for more information. function in `lib.js` for more information.
## Graphing ## Viewing and Downloading Data
You can download the information to the PC using [the App Loader](https://banglejs.com/apps/?id=recorder). Connect You can download and visualize the information using [the App Loader](https://banglejs.com/apps/?id=recorder). Connect
to your Bangle, then in `My Apps` click the disk icon next to the `Recorder` app to download data. to your Bangle, then in `My Apps` click the disk icon next to the `Recorder` app to access the download interface.
You can also view some information on the watch. ### Interactive Web Interface
The download interface provides individual track visualization with:
* **Interactive Leaflet maps** - Each GPS track gets its own map using OpenStreetMap tiles
* **Track statistics** - Distance, duration, and track points automatically calculated
* **Start/End markers** - Green circles mark track start, red circles mark end points
* **Interactive track points** - Click anywhere along the GPS track to see detailed data at that point
* **Data popups** - View available data for each point (time, heart rate, altitude, speed, steps, battery, barometer - if recorded)
* **Interactive charts** - Collapsible graphs for heart rate, battery, steps, elevation, speed, and barometer data with PNG export
* **Download options** - KML, GPX, and CSV formats for individual tracks or all at once
* **Settings** - Option to include/exclude entries without GPS coordinates
* **Unit preferences** - Choose between metric, imperial, or auto-detect based on your locale
* **Mobile responsive** - Works well on all devices
### On-Watch Visualization
You can also view some information on the watch:
* Tap `View Tracks` * Tap `View Tracks`
* Tap on the Track number you're interested in, and you'll see a page with information about that track... * Tap on the Track number you're interested in, and you'll see a page with information about that track...

File diff suppressed because it is too large Load Diff

View File

@ -114,6 +114,28 @@ exports.getRecorders = function() {
stop : () => {}, stop : () => {},
draw : (x,y) => g.reset().drawImage(atob("DAwBAAMMeeeeeeeecOMMAAMMMMAA"),x,y) draw : (x,y) => g.reset().drawImage(atob("DAwBAAMMeeeeeeeecOMMAAMMMMAA"),x,y)
}; };
},
accel:function() {
var ax=0,ay=0,az=0,n=0;
function onAccel(a) {
ax += a.x;
ay += a.y;
az += a.z;
n++;
}
return {
name : "Accel",
fields : ["Accel X", "Accel Y", "Accel Z"],
getValues : () => {
if (n<1) n=1;
var r = [(ax/n).toFixed(2), (ay/n).toFixed(2), (az/n).toFixed(2)];
n = ax = ay = az = 0;
return r;
},
start : () => { Bangle.on('accel', onAccel); },
stop : () => { Bangle.removeListener('accel', onAccel); },
draw : (x,y) => g.reset().drawImage(atob("DAwBAAMMeeeeeeeecOMMAAMMMMAA"),x,y)
};
} }
}; };
if (Bangle.getPressure){ if (Bangle.getPressure){

View File

@ -2,7 +2,7 @@
"id": "recorder", "id": "recorder",
"name": "Recorder", "name": "Recorder",
"shortName": "Recorder", "shortName": "Recorder",
"version": "0.47", "version": "0.48",
"description": "Record GPS position, heart rate and more in the background, then download to your PC.", "description": "Record GPS position, heart rate and more in the background, then download to your PC.",
"icon": "app.png", "icon": "app.png",
"tags": "tool,outdoors,gps,widget,clkinfo", "tags": "tool,outdoors,gps,widget,clkinfo",