Merge branch 'espruino:master' into master

pull/3132/head
shansou504 2023-12-17 20:36:50 -05:00 committed by GitHub
commit 161e6dbf5d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 314 additions and 165 deletions

View File

@ -5,3 +5,4 @@
0.05: Show calendar colors & improved all day events.
0.06: Improved multi-line locations & titles
0.07: Buzz 30, 15 and 1 minute before an event
0.08: No buzz during quiet hours & tweaked 30-minute buzz

View File

@ -119,10 +119,12 @@ function fullRedraw() {
function buzzForEvents() {
let nextEvent = next[0]; if (!nextEvent) return;
if (nextEvent.allDay) return;
// No buzz for all day events or events before 7am
// TODO: make this configurable
if (nextEvent.allDay || (new Date(nextEvent.timestamp * 1000)).getHours() < 7) return;
let minToEvent = Math.round((nextEvent.timestamp - getTime()) / 60.0);
switch (minToEvent) {
case 30: require("buzz").pattern(","); break;
case 30: require("buzz").pattern(":"); break;
case 15: require("buzz").pattern(", ,"); break;
case 1: require("buzz").pattern(": : :"); break;
}

View File

@ -2,7 +2,7 @@
"id": "calclock",
"name": "Calendar Clock",
"shortName": "CalClock",
"version": "0.07",
"version": "0.08",
"description": "Show the current and upcoming events synchronized from Gadgetbridge",
"icon": "calclock.png",
"type": "clock",

View File

@ -1,2 +1,3 @@
0.01: New App!
0.02: Bugfixes
0.03: Use Bangle.setBacklight()

View File

@ -6,7 +6,6 @@ Bangle.loadWidgets(); // load before first appRect call
const FIELD_WIDTH = Bangle.appRect.w/8;
const FIELD_HEIGHT = Bangle.appRect.h/8;
const SETTINGS_FILE = "chess.json";
const DEFAULT_TIMEOUT = Bangle.getOptions().lockTimeout;
const ICON_SIZE=45;
const ICON_BISHOP = require("heatshrink").decompress(atob("lstwMB/4Ac/wFE4IED/kPAofgn4FDGon8j4QEBQgQE4EHBQcACwfAgF/BQYWD8EAHAX+NgI4C+AQEwAQDDYIhDDYMDCAQKBGQQsHHogKDCAJODCAI3CHoQKCHoIQDHoIQCFgoQBFgfgIQYmBEIQECKgIrCBYQKDC4OBg/8iCvEAC+AA="));
const ICON_PAWN = require("heatshrink").decompress(atob("lstwMB/4At/AFEGon4h4FDwE/AgX8CAngCAkAv4bDgYbECAf4gAhD4AhD/kAg4mDCAkACAYbBEIYQBG4gbDEII9DFhXAgEfBQYWDEwJUC/wKBGQXwCAgEBE4RCBCAYmBCAQmCCAQmBCAbdCCAIbCQ4gAYwA="));
@ -192,6 +191,7 @@ Bangle.drawWidgets();
// drag selected field
Bangle.on('drag', (ev) => {
if (showmenu) return;
const newx = curfield[0]+ev.dx;
const newy = curfield[1]+ev.dy;
if (newx >= 0 && newx <= 7*FIELD_WIDTH) {
@ -230,7 +230,7 @@ Bangle.on('touch', (button, xy) => {
drawSelectedField();
if (!finished) {
// do computer move
Bangle.setLCDTimeout(0.1); // this can take some time, turn off to save power
Bangle.setBacklight(false); // this can take some time, turn off to save power
showMessage(/*LANG*/"Calculating..");
setTimeout(() => {
const compMove = state.findmove(settings.computer_level+1);
@ -240,15 +240,15 @@ Bangle.on('touch', (button, xy) => {
}
Bangle.setLCDPower(true);
Bangle.setLocked(false);
Bangle.setLCDTimeout(DEFAULT_TIMEOUT/1000); // restore
Bangle.setBacklight(true);
if (!showmenu) {
showAlert(result.string);
}
}, 200); // execute after display update
}, 300); // execute after display update
}
};
move(posFrom, posTo,cb);
}, 200); // execute after display update
}, 100); // execute after display update
} // piece_sel === 0
startfield[0] = startfield[1] = undefined;
piece_sel = 0;
@ -277,7 +277,9 @@ setWatch(() => {
E.showMenu({
"" : { title : /*LANG*/"Chess settings" },
"< Back" : () => closeMenu(),
/*LANG*/"Exit" : () => load(),
/*LANG*/"New Game" : () => {
finished = false;
state = engine.p4_fen2state(engine.P4_INITIAL_BOARD);
writeSettings();
closeMenu();
@ -296,6 +298,5 @@ setWatch(() => {
writeSettings();
}
},
/*LANG*/"Exit" : () => load(),
});
}, BTN, { repeat: true, edge: "falling" });

View File

@ -2,7 +2,7 @@
"id": "chess",
"name": "Chess",
"shortName": "Chess",
"version": "0.02",
"version": "0.03",
"description": "Chess game based on the [p4wn engine](https://p4wn.sourceforge.net/). Drag on the touchscreen to move the green cursor onto a piece, select it with a single touch and drag the now red cursor around. Release the piece with another touch to finish the move. The button opens a menu.",
"icon": "app.png",
"tags": "game",

View File

@ -29,3 +29,4 @@
0.26: Implement API for activity fetching
0.27: Fix typo in daily summary graph code causing graph not to load
Fix daily summaries for 31st of the month
0.28: Calculate distance from steps if myprofile is installed and stride length is set

View File

@ -13,7 +13,7 @@ To view data, run the `Health` app from your watch.
Stores:
* Heart rate
* Step count
* Step count (can calculate distance if myprofile is installed and stride length is set)
* Movement
## Settings

View File

@ -1,3 +1,6 @@
let settings;
const myprofile = require("Storage").readJSON("myprofile.json",1)||{};
function menuMain() {
E.showMenu({
"": { title: /*LANG*/"Health Tracking" },
@ -5,16 +8,31 @@ function menuMain() {
/*LANG*/"Step Counting": () => menuStepCount(),
/*LANG*/"Movement": () => menuMovement(),
/*LANG*/"Heart Rate": () => menuHRM(),
/*LANG*/"Settings": () => eval(require("Storage").read("health.settings.js"))(()=>menuMain())
/*LANG*/"Settings": () => eval(require("Storage").read("health.settings.js"))(()=>{loadSettings();menuMain();})
});
}
function menuStepCount() {
E.showMenu({
const menu = {
"": { title:/*LANG*/"Steps" },
/*LANG*/"< Back": () => menuMain(),
/*LANG*/"per hour": () => stepsPerHour(),
/*LANG*/"per day": () => stepsPerDay()
/*LANG*/"per hour": () => stepsPerHour(menuStepCount),
/*LANG*/"per day": () => stepsPerDay(menuStepCount)
};
if (myprofile.strideLength) {
menu[/*LANG*/"distance"] = () => menuDistance();
}
E.showMenu(menu);
}
function menuDistance() {
const distMult = 1*require("locale").distance(myprofile.strideLength, 2); // hackish: this removes the distance suffix, e.g. 'm'
E.showMenu({
"": { title:/*LANG*/"Distance" },
/*LANG*/"< Back": () => menuStepCount(),
/*LANG*/"per hour": () => stepsPerHour(menuDistance, distMult),
/*LANG*/"per day": () => stepsPerDay(menuDistance, distMult)
});
}
@ -36,23 +54,35 @@ function menuHRM() {
});
}
function stepsPerHour() {
function stepsPerHour(back, mult) {
E.showMessage(/*LANG*/"Loading...");
current_selection = "stepsPerHour";
var data = new Uint16Array(24);
require("health").readDay(new Date(), h=>data[h.hr]+=h.steps);
setButton(menuStepCount);
barChart(/*LANG*/"HOUR", data);
if (mult !== undefined) {
// Calculate distance from steps
data.forEach((d, i) => data[i] = d*mult+0.5);
}
setButton(back, mult);
barChart(/*LANG*/"HOUR", data, mult);
}
function stepsPerDay() {
function stepsPerDay(back, mult) {
E.showMessage(/*LANG*/"Loading...");
current_selection = "stepsPerDay";
var data = new Uint16Array(32);
require("health").readDailySummaries(new Date(), h=>data[h.day]+=h.steps);
setButton(menuStepCount);
barChart(/*LANG*/"DAY", data);
drawHorizontalLine(settings.stepGoal);
// Include data for today
if (data[(new Date()).getDate()] === 0) {
data[(new Date()).getDate()] = Bangle.getHealthStatus("day").steps;
}
if (mult !== undefined) {
// Calculate distance from steps
data.forEach((d, i) => data[i] = d*mult+0.5);
}
setButton(back, mult);
barChart(/*LANG*/"DAY", data, mult);
drawHorizontalLine(settings.stepGoal * (mult || 1));
}
function hrmPerHour() {
@ -64,7 +94,7 @@ function hrmPerHour() {
data[h.hr]+=h.bpm;
if (h.bpm) cnt[h.hr]++;
});
data.forEach((d,i)=>data[i] = d/cnt[i]);
data.forEach((d,i)=>data[i] = d/cnt[i]+0.5);
setButton(menuHRM);
barChart(/*LANG*/"HOUR", data);
}
@ -78,7 +108,7 @@ function hrmPerDay() {
data[h.day]+=h.bpm;
if (h.bpm) cnt[h.day]++;
});
data.forEach((d,i)=>data[i] = d/cnt[i]);
data.forEach((d,i)=>data[i] = d/cnt[i]+0.5);
setButton(menuHRM);
barChart(/*LANG*/"DAY", data);
}
@ -92,7 +122,7 @@ function movementPerHour() {
data[h.hr]+=h.movement;
cnt[h.hr]++;
});
data.forEach((d,i)=>data[i] = d/cnt[i]);
data.forEach((d,i)=>data[i] = d/cnt[i]+0.5);
setButton(menuMovement);
barChart(/*LANG*/"HOUR", data);
}
@ -106,7 +136,7 @@ function movementPerDay() {
data[h.day]+=h.movement;
cnt[h.day]++;
});
data.forEach((d,i)=>data[i] = d/cnt[i]);
data.forEach((d,i)=>data[i] = d/cnt[i]+0.5);
setButton(menuMovement);
barChart(/*LANG*/"DAY", data);
}
@ -180,7 +210,7 @@ function drawHorizontalLine(value) {
g.setColor(g.theme.fg).drawLine(0, top ,g.getWidth(), top);
}
function setButton(fn) {
function setButton(fn, mult) {
Bangle.setUI({mode:"custom",
back:fn,
swipe:(lr,ud) => {
@ -194,12 +224,16 @@ function setButton(fn) {
}
drawBarChart();
if (current_selection == "stepsPerDay") {
drawHorizontalLine(settings.stepGoal);
drawHorizontalLine(settings.stepGoal * (mult || 1));
}
}});
}
function loadSettings() {
settings = require("Storage").readJSON("health.json",1)||{};
}
Bangle.loadWidgets();
Bangle.drawWidgets();
var settings = require("Storage").readJSON("health.json",1)||{};
loadSettings();
menuMain();

View File

@ -2,7 +2,7 @@
"id": "health",
"name": "Health Tracking",
"shortName": "Health",
"version": "0.27",
"version": "0.28",
"description": "Logs health data and provides an app to view it",
"icon": "app.png",
"tags": "tool,system,health",

View File

@ -8,6 +8,7 @@
function setSettings() {
require("Storage").writeJSON("health.json", settings);
}
E.showMenu({
"": { title: /*LANG*/"Health Tracking" },
@ -39,6 +40,7 @@
setSettings();
}
},
/*LANG*/"Step Goal Notification": {
value: "stepGoalNotification" in settings ? settings.stepGoalNotification : false,
format: () => (settings.stepGoalNotification ? 'Yes' : 'No'),
@ -46,6 +48,6 @@
settings.stepGoalNotification = !settings.stepGoalNotification;
setSettings();
}
}
},
});
})

18
apps/myprofile/README.md Normal file
View File

@ -0,0 +1,18 @@
# My Profile
Configure your personal profile. All settings are optional and are only stored on the watch.
## Available settings
| Setting | Description | Displayed in | Stored in | Default value | How to measure |
| ------------- | ------------------------------- | ------------------- | ------------ | ------------- | ----------------------------------------------------------------- |
| Birthday | Used to calculate age | year, month, day | 'YYYY-MM-DD' | 01.01.1970 | - |
| HR max | maximum heart rate | BPM | BPM | 60 | Use maximum value when exercising.<br/> If unsure set to 220-age. |
| HR min | minimum heart rate | BPM | BPM | 200 | Measure your heart rate after waking up |
| Height | Body height | local length unit | meter | 0 (=not set) | - |
| Stride length | distance traveled with one step | local length unit | meter | 0 (=not set) | Walk 10 steps and divide the travelled distance by 10 |
## Developer notes
- Feel free to add additional settings.
- For values without reasonable defaults never assume that a value is set. Always check the value before using it.

BIN
apps/myprofile/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,18 @@
{ "id": "myprofile",
"name": "My Profile",
"shortName":"My Profile",
"icon": "app.png",
"type": "settings",
"version":"0.01",
"description": "Configure your personal profile. All settings are optional and only stored on the watch.",
"readme": "README.md",
"tags": "tool,utility",
"supports": ["BANGLEJS", "BANGLEJS2"],
"storage": [
{"name":"myprofile.settings.js","url":"settings.js"}
],
"data": [
{"name":"myprofile.json"}
]
}

138
apps/myprofile/settings.js Normal file
View File

@ -0,0 +1,138 @@
(function(back) {
const FILE = "myprofile.json";
const myprofile = Object.assign({
minHrm: 60,
maxHrm: 200,
strideLength: 0, // 0 = not set
birthday: '1970-01-01',
height: 0, // 0 = not set
}, require('Storage').readJSON(FILE, true) || {});
function writeProfile() {
require('Storage').writeJSON(FILE, myprofile);
}
const ageMenu = () => {
const date = new Date(myprofile.birthday);
E.showMenu({
"" : { "title" : /*LANG*/"Birthday" },
"< Back" : () => {
if (date != new Date(myprofile.birthday)) {
// Birthday changed
if (date > new Date()) {
E.showPrompt(/*LANG*/"Birthday must not be in future!", {
buttons : {"Ok":true},
}).then(() => ageMenu());
} else {
const age = (new Date()).getFullYear() - date.getFullYear();
const newMaxHRM = 220-age;
E.showPrompt(/*LANG*/`Set HR max to ${newMaxHRM} calculated from age?`).then(function(v) {
myprofile.birthday = date.getFullYear() + "-" + (date.getMonth() + 1).toString().padStart(2, '0') + "-" + date.getDate().toString().padStart(2, '0');
if (v) {
myprofile.maxHrm = newMaxHRM;
}
writeProfile();
mainMenu();
});
}
} else {
mainMenu();
}
},
/*LANG*/"Day": {
value: date ? date.getDate() : null,
min: 1,
max: 31,
wrap: true,
onchange: v => {
date.setDate(v);
}
},
/*LANG*/"Month": {
value: date ? date.getMonth() + 1 : null,
format: v => require("date_utils").month(v),
onchange: v => {
date.setMonth((v+11)%12);
}
},
/*LANG*/"Year": {
value: date ? date.getFullYear() : null,
min: 1900,
max: (new Date()).getFullYear(),
onchange: v => {
date.setFullYear(v);
}
},
});
};
const mainMenu = () => {
E.showMenu({
"" : { "title" : /*LANG*/"My Profile" },
"< Back" : () => back(),
/*LANG*/"Birthday" : () => ageMenu(),
/*LANG*/'Height': {
value: myprofile.height,
min: 0, max: 300,
step:0.01,
format: v => v ? require("locale").distance(v, 2) : '-',
onchange: v => {
if (v !== myprofile.height) {
// height changed
myprofile.height = v;
setTimeout(() => {
const newStrideLength = myprofile.height * 0.414;
E.showPrompt(/*LANG*/`Set Stride length to ${require("locale").distance(newStrideLength, 2)} calculated from height?`).then(function(v) {
if (v) {
myprofile.strideLength = newStrideLength;
}
writeProfile();
mainMenu();
});
}, 1);
}
}
},
/*LANG*/'HR max': {
format: v => /*LANG*/`${v} BPM`,
value: myprofile.maxHrm,
min: 30, max: 220,
onchange: v => {
myprofile.maxHrm = v;
writeProfile();
}
},
/*LANG*/'HR min': {
format: v => /*LANG*/`${v} BPM`,
value: myprofile.minHrm,
min: 30, max: 220,
onchange: v => {
myprofile.minHrm = v;
writeProfile();
}
},
/*LANG*/"Stride length": {
value: myprofile.strideLength,
min:0.00,
step:0.01,
format: v => v ? require("locale").distance(v, 2) : '-',
onchange: v => {
myprofile.strideLength=v;
writeProfile();
},
},
});
};
mainMenu();
})

View File

@ -9,3 +9,4 @@ when fastloading.
0.08: Issue newline before GB commands (solves issue with console.log and ignored commands)
0.09: Don't send the gadgetbridge wake command twice. Once should do since we
issue newline before GB commands.
0.10: Some refactoring to shorten the code.

View File

@ -1,6 +1,6 @@
{
/*
Bluetooth.println(JSON.stringify({t:"intent", action:"", flags:["flag1", "flag2",...], categories:["category1","category2",...], mimetype:"", data:"", package:"", class:"", target:"", extra:{someKey:"someValueOrString"}}));
* Bluetooth.println(JSON.stringify({t:"intent", target:"", action:"", flags:["flag1", "flag2",...], categories:["category1","category2",...], package:"", class:"", mimetype:"", data:"", extra:{someKey:"someValueOrString", anotherKey:"anotherValueOrString",...}}));
*/
let R;
@ -133,6 +133,17 @@ let backToGfx = function() {
The functions for interacting with Android and the Spotify app
*/
let createCommand = function(o) {
return ()=>{
Bluetooth.println("");
Bluetooth.println(JSON.stringify(o));
};
};
let assembleSearchString = function() {
return (artist=="" ? "":("artist:\""+artist+"\"")) + ((artist!="" && track!="") ? " ":"") + (track=="" ? "":("track:\""+track+"\"")) + (((artist!="" && album!="") || (track!="" && album!="")) ? " ":"") + (album=="" ? "":(" album:\""+album+"\""));
};
simpleSearch = "";
let simpleSearchTerm = function() { // input a simple search term without tags, overrides search with tags (artist and track)
require("textinput").input({text:simpleSearch}).then(result => {simpleSearch = result;}).then(() => {E.showMenu(searchMenu);});
@ -153,97 +164,44 @@ let albumSearchTerm = function() { // input album to search for
require("textinput").input({text:album}).then(result => {album = result;}).then(() => {E.showMenu(searchMenu);});
};
let searchPlayWOTags = function() {//make a spotify search and play using entered terms
searchString = simpleSearch;
Bluetooth.println("");
Bluetooth.println(JSON.stringify({t:"intent", action:"android.media.action.MEDIA_PLAY_FROM_SEARCH", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", target:"activity", extra:{query:searchString}, flags:["FLAG_ACTIVITY_NEW_TASK"]}));
};
let searchPlayWOTags = createCommand({t:"intent", action:"android.media.action.MEDIA_PLAY_FROM_SEARCH", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", target:"activity", extra:{query:simpleSearch}, flags:["FLAG_ACTIVITY_NEW_TASK"]});
let searchPlayWTags = function() {//make a spotify search and play using entered terms
searchString = (artist=="" ? "":("artist:\""+artist+"\"")) + ((artist!="" && track!="") ? " ":"") + (track=="" ? "":("track:\""+track+"\"")) + (((artist!="" && album!="") || (track!="" && album!="")) ? " ":"") + (album=="" ? "":(" album:\""+album+"\""));
Bluetooth.println("");
Bluetooth.println(JSON.stringify({t:"intent", action:"android.media.action.MEDIA_PLAY_FROM_SEARCH", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", target:"activity", extra:{query:searchString}, flags:["FLAG_ACTIVITY_NEW_TASK"]}));
};
let searchPlayWTags = createCommand({t:"intent", action:"android.media.action.MEDIA_PLAY_FROM_SEARCH", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", target:"activity", extra:{query:assembleSearchString()}, flags:["FLAG_ACTIVITY_NEW_TASK"]});
let playVreden = function() {//Play the track "Vreden" by Sara Parkman via spotify uri-link
Bluetooth.println("");
Bluetooth.println(JSON.stringify({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:track:5QEFFJ5tAeRlVquCUNpAJY:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]}));
};
let playVreden = createCommand({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:track:5QEFFJ5tAeRlVquCUNpAJY:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"]});
let playVredenAlternate = function() {//Play the track "Vreden" by Sara Parkman via spotify uri-link
Bluetooth.println("");
Bluetooth.println(JSON.stringify({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:track:5QEFFJ5tAeRlVquCUNpAJY:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK"]}));
};
let playVredenAlternate = createCommand({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:track:5QEFFJ5tAeRlVquCUNpAJY:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK"]});
let searchPlayVreden = function() {//Play the track "Vreden" by Sara Parkman via search and play
Bluetooth.println("");
Bluetooth.println(JSON.stringify({t:"intent", action:"android.media.action.MEDIA_PLAY_FROM_SEARCH", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", target:"activity", extra:{query:'artist:"Sara Parkman" track:"Vreden"'}, flags:["FLAG_ACTIVITY_NEW_TASK"]}));
};
let searchPlayVreden = createCommand({t:"intent", action:"android.media.action.MEDIA_PLAY_FROM_SEARCH", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", target:"activity", extra:{query:'artist:"Sara Parkman" track:"Vreden"'}, flags:["FLAG_ACTIVITY_NEW_TASK"]});
let openAlbum = function() {//Play EP "The Blue Room" by Coldplay
Bluetooth.println("");
Bluetooth.println(JSON.stringify({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:album:3MVb2CWB36x7VwYo5sZmf2", target:"activity", flags:["FLAG_ACTIVITY_NEW_TASK"]}));
};
let openAlbum = createCommand({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:album:3MVb2CWB36x7VwYo5sZmf2", target:"activity", flags:["FLAG_ACTIVITY_NEW_TASK"]});
let searchPlayAlbum = function() {//Play EP "The Blue Room" by Coldplay via search and play
Bluetooth.println("");
Bluetooth.println(JSON.stringify({t:"intent", action:"android.media.action.MEDIA_PLAY_FROM_SEARCH", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", target:"activity", extra:{query:'album:"The blue room" artist:"Coldplay"', "android.intent.extra.focus":"vnd.android.cursor.item/album"}, flags:["FLAG_ACTIVITY_NEW_TASK"]}));
};
let searchPlayAlbum = createCommand({t:"intent", action:"android.media.action.MEDIA_PLAY_FROM_SEARCH", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", target:"activity", extra:{query:'album:"The blue room" artist:"Coldplay"', "android.intent.extra.focus":"vnd.android.cursor.item/album"}, flags:["FLAG_ACTIVITY_NEW_TASK"]});
let spotifyWidget = function(action) {
Bluetooth.println("");
Bluetooth.println(JSON.stringify({t:"intent", action:("com.spotify.mobile.android.ui.widget."+action), package:"com.spotify.music", target:"broadcastreceiver"}));
};
let gadgetbridgeWake = function() {
Bluetooth.println("");
Bluetooth.println(JSON.stringify({t:"intent", target:"activity", flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_CLEAR_TASK", "FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS", "FLAG_ACTIVITY_NO_ANIMATION"], package:"gadgetbridge", class:"nodomain.freeyourgadget.gadgetbridge.activities.WakeActivity"}));
};
let gadgetbridgeWake = createCommand({t:"intent", target:"activity", flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_CLEAR_TASK", "FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS", "FLAG_ACTIVITY_NO_ANIMATION"], package:"gadgetbridge", class:"nodomain.freeyourgadget.gadgetbridge.activities.WakeActivity"});
let spotifyPlaylistDW = function() {
Bluetooth.println("");
Bluetooth.println(JSON.stringify({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:user:spotify:playlist:37i9dQZEVXcRfaeEbxXIgb:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]}));
};
let spotifyPlaylistDW = createCommand({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:user:spotify:playlist:37i9dQZEVXcRfaeEbxXIgb:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]});
let spotifyPlaylistDM1 = function() {
Bluetooth.println("");
Bluetooth.println(JSON.stringify({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:user:spotify:playlist:37i9dQZF1E365VyzxE0mxF:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]}));
};
let spotifyPlaylistDM1 = createCommand({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:user:spotify:playlist:37i9dQZF1E365VyzxE0mxF:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]});
let spotifyPlaylistDM2 = function() {
Bluetooth.println("");
Bluetooth.println(JSON.stringify({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:user:spotify:playlist:37i9dQZF1E38LZHLFnrM61:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]}));
};
let spotifyPlaylistDM2 = createCommand({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:user:spotify:playlist:37i9dQZF1E38LZHLFnrM61:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]});
let spotifyPlaylistDM3 = function() {
Bluetooth.println("");
Bluetooth.println(JSON.stringify({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:user:spotify:playlist:37i9dQZF1E36RU87qzgBFP:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]}));
};
let spotifyPlaylistDM3 = createCommand({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:user:spotify:playlist:37i9dQZF1E36RU87qzgBFP:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]});
let spotifyPlaylistDM4 = function() {
Bluetooth.println("");
Bluetooth.println(JSON.stringify({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:user:spotify:playlist:37i9dQZF1E396gGyCXEBFh:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]}));
};
let spotifyPlaylistDM4 = createCommand({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:user:spotify:playlist:37i9dQZF1E396gGyCXEBFh:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]});
let spotifyPlaylistDM5 = function() {
Bluetooth.println("");
Bluetooth.println(JSON.stringify({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:user:spotify:playlist:37i9dQZF1E37a0Tt6CKJLP:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]}));
};
let spotifyPlaylistDM5 = createCommand({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:user:spotify:playlist:37i9dQZF1E37a0Tt6CKJLP:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]});
let spotifyPlaylistDM6 = function() {
Bluetooth.println("");
Bluetooth.println(JSON.stringify({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:user:spotify:playlist:37i9dQZF1E36UIQLQK79od:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]}));
};
let spotifyPlaylistDM6 = createCommand({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:user:spotify:playlist:37i9dQZF1E36UIQLQK79od:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]});
let spotifyPlaylistDD = function() {
Bluetooth.println("");
Bluetooth.println(JSON.stringify({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:user:spotify:playlist:37i9dQZF1EfWFiI7QfIAKq:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]}));
};
let spotifyPlaylistDD = createCommand({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:user:spotify:playlist:37i9dQZF1EfWFiI7QfIAKq:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]});
let spotifyPlaylistRR = function() {
Bluetooth.println("");
Bluetooth.println(JSON.stringify({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:user:spotify:playlist:37i9dQZEVXbs0XkE2V8sMO:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]}));
};
let spotifyPlaylistRR = createCommand({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:user:spotify:playlist:37i9dQZEVXbs0XkE2V8sMO:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]});
// Spotify Remote Menu
let spotifyMenu = {
@ -256,11 +214,14 @@ let spotifyMenu = {
"Exit Spotify Remote" : ()=>{load();}
};
let menuBackFunc = ()=>{
if (backToMenu) E.showMenu(spotifyMenu);
if (!backToMenu) backToGfx();
};
let controlMenu = {
"" : { title : " Controls ",
back: () => {if (backToMenu) E.showMenu(spotifyMenu);
if (!backToMenu) backToGfx();} },
back: menuBackFunc },
"Play" : ()=>{Bangle.musicControl("play");},
"Pause" : ()=>{Bangle.musicControl("pause");},
"Previous" : ()=>{spotifyWidget("PREVIOUS");},
@ -271,32 +232,30 @@ let controlMenu = {
let searchMenu = {
"" : { title : " Search ",
back: () => {if (backToMenu) E.showMenu(spotifyMenu);
if (!backToMenu) backToGfx();} },
"Search term w/o tags" : ()=>{simpleSearchTerm();},
"Execute search and play w/o tags" : ()=>{searchPlayWOTags();},
"Search term w tag \"artist\"" : ()=>{artistSearchTerm();},
"Search term w tag \"track\"" : ()=>{trackSearchTerm();},
"Search term w tag \"album\"" : ()=>{albumSearchTerm();},
"Execute search and play with tags" : ()=>{searchPlayWTags();},
back: menuBackFunc },
"Search term w/o tags" : simpleSearchTerm,
"Execute search and play w/o tags" : searchPlayWOTags,
"Search term w tag \"artist\"" : artistSearchTerm,
"Search term w tag \"track\"" : trackSearchTerm,
"Search term w tag \"album\"" : albumSearchTerm,
"Execute search and play with tags" : searchPlayWTags,
};
let savedMenu = {
"" : { title : " Saved ",
back: () => {if (backToMenu) E.showMenu(spotifyMenu);
if (!backToMenu) backToGfx();} },
"Play Discover Weekly" : ()=>{spotifyPlaylistDW();},
"Play Daily Mix 1" : ()=>{spotifyPlaylistDM1();},
"Play Daily Mix 2" : ()=>{spotifyPlaylistDM2();},
"Play Daily Mix 3" : ()=>{spotifyPlaylistDM3();},
"Play Daily Mix 4" : ()=>{spotifyPlaylistDM4();},
"Play Daily Mix 5" : ()=>{spotifyPlaylistDM5();},
"Play Daily Mix 6" : ()=>{spotifyPlaylistDM6();},
"Play Daily Drive" : ()=>{spotifyPlaylistDD();},
"Play Release Radar" : ()=>{spotifyPlaylistRR();},
"Play \"Vreden\" by Sara Parkman via uri-link" : ()=>{playVreden();},
"Open \"The Blue Room\" EP (no autoplay)" : ()=>{openAlbum();},
"Play \"The Blue Room\" EP via search&play" : ()=>{searchPlayAlbum();},
back: menuBackFunc },
"Play Discover Weekly" : spotifyPlaylistDW,
"Play Daily Mix 1" : spotifyPlaylistDM1,
"Play Daily Mix 2" : spotifyPlaylistDM2,
"Play Daily Mix 3" : spotifyPlaylistDM3,
"Play Daily Mix 4" : spotifyPlaylistDM4,
"Play Daily Mix 5" : spotifyPlaylistDM5,
"Play Daily Mix 6" : spotifyPlaylistDM6,
"Play Daily Drive" : spotifyPlaylistDD,
"Play Release Radar" : spotifyPlaylistRR,
"Play \"Vreden\" by Sara Parkman via uri-link" : playVreden,
"Open \"The Blue Room\" EP (no autoplay)" : openAlbum,
"Play \"The Blue Room\" EP via search&play" : searchPlayAlbum,
};
Bangle.loadWidgets();

View File

@ -1,7 +1,7 @@
{
"id": "spotrem",
"name": "Remote for Spotify",
"version": "0.09",
"version": "0.10",
"description": "Control spotify on your android device.",
"readme": "README.md",
"type": "app",

2
apps/widhrzone/ChangeLog Normal file
View File

@ -0,0 +1,2 @@
0.01: New App!
0.02: Read maximum HRM from myprofile

View File

@ -2,7 +2,7 @@
"id": "widhrzone",
"name": "Heart rate zone widget",
"shortName": "HRzone widget",
"version": "0.01",
"version": "0.02",
"description": "Widget that displays the current out of five heart rate training zones 1. HEALTH (50-60% of max. HR, Recovery, grey), 2. FAT-B (60-70% of max. HR, burns fat, blue), 3. AROBIC (70-80% of max. HR, Endurance, green), 4. ANAROB (80-90% of max. HR, Speed, yellow), 5. MAX (90-100% of max. HR, red). Only visible when heart rate monitor is active and inside one of the zones. Requires to set the maximum heart rate in settings (if unsure set to 220-age).",
"icon": "widget.png",
"type": "widget",
@ -10,8 +10,7 @@
"supports": ["BANGLEJS","BANGLEJS2"],
"screenshots" : [ { "url":"screenshot.png" } ],
"storage": [
{"name":"widhrzone.wid.js","url":"widget.js"},
{"name":"widhrzone.settings.js","url":"settings.js"}
{"name":"widhrzone.wid.js","url":"widget.js"}
],
"data": [{"name":"widhrzone.json"}]
"dependencies": {"myprofile":"app"}
}

View File

@ -1,26 +0,0 @@
(function(back) {
const CONFIGFILE = "widhrzone.json";
// Load settings
const settings = Object.assign({
maxHrm: 200,
}, require("Storage").readJSON(CONFIGFILE,1) || {});
function writeSettings() {
require('Storage').writeJSON(CONFIGFILE, settings);
}
// Show the menu
E.showMenu({
"" : { "title" : "HRzone widget" },
"< Back" : () => back(),
/*LANG*/'HR max': {
format: v => v,
value: settings.maxHrm,
min: 30, max: 220,
onchange: v => {
settings.maxHrm = v;
writeSettings();
}
},
});
});

View File

@ -1,20 +1,18 @@
(() => {
const config = Object.assign({
maxHrm: 200,
}, require("Storage").readJSON("widhrzone.json",1) || {});
const myprofile = require("Storage").readJSON("myprofile.json",1)||{};
require("FontTeletext5x9Ascii").add(Graphics);
const calczone = (bpm) => {
if (bpm <= config.maxHrm*0.5) {
if (bpm <= myprofile.maxHrm*0.5) {
return 0;
} else if (bpm <= config.maxHrm*0.60) {
} else if (bpm <= myprofile.maxHrm*0.60) {
return 1;
} else if (bpm <= config.maxHrm*0.70) {
} else if (bpm <= myprofile.maxHrm*0.70) {
return 2;
} else if (bpm <= config.maxHrm*0.80) {
} else if (bpm <= myprofile.maxHrm*0.80) {
return 3;
} else if (bpm <= config.maxHrm*0.90) {
} else if (bpm <= myprofile.maxHrm*0.90) {
return 4;
} else { // > 0.9
return 5;