From 8018c99b4e3d20d657eed57f328830c4ee8e14fe Mon Sep 17 00:00:00 2001 From: Andre Date: Wed, 4 Jun 2025 21:17:02 -0400 Subject: [PATCH 01/11] simplemusic: Initial commit! --- apps/simplemusic/ChangeLog | 1 + apps/simplemusic/README.md | 27 +++++ apps/simplemusic/app-icon.js | 1 + apps/simplemusic/app.js | 203 +++++++++++++++++++++++++++++++++ apps/simplemusic/app.png | Bin 0 -> 2289 bytes apps/simplemusic/metadata.json | 17 +++ 6 files changed, 249 insertions(+) create mode 100644 apps/simplemusic/ChangeLog create mode 100644 apps/simplemusic/README.md create mode 100644 apps/simplemusic/app-icon.js create mode 100644 apps/simplemusic/app.js create mode 100644 apps/simplemusic/app.png create mode 100644 apps/simplemusic/metadata.json diff --git a/apps/simplemusic/ChangeLog b/apps/simplemusic/ChangeLog new file mode 100644 index 000000000..e342e49ed --- /dev/null +++ b/apps/simplemusic/ChangeLog @@ -0,0 +1 @@ +0.01: First release! diff --git a/apps/simplemusic/README.md b/apps/simplemusic/README.md new file mode 100644 index 000000000..9d6859474 --- /dev/null +++ b/apps/simplemusic/README.md @@ -0,0 +1,27 @@ +# Simple Music Controls + +A small app for viewing and controlling music via Gadgetbridge on Android. This is a remix of [rigrig's Gadgetbridge Music App](https://banglejs.com/apps/?id=gbmusic&readme), only it adds on-screen buttons and doesn't run in the background. + +Requires [Gadgetbridge](https://www.espruino.com/Gadgetbridge). + +## Usage + +1. Connect your Bangle.js to Gadgetbridge. +2. Open a music player on your Android phone. +3. Open this app on your Bangle.js. + +## Features + +- Shows the current song title and album +- Provides buttons for changing or pausing the current track. +- Supports swiping + +## Controls + +Use the on-screen buttons to go back to the previous track, play/pause the current track, or skip to the next track. + +Swipe up/down to increase/decrease the volume, or swip left/right to navigate to the previous/next song. + +## Creator + +8bitbuddhist (https://github.com/8bitbuddhist) diff --git a/apps/simplemusic/app-icon.js b/apps/simplemusic/app-icon.js new file mode 100644 index 000000000..f0e006d45 --- /dev/null +++ b/apps/simplemusic/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEw4UA///vt9AYO7gQdRggLKgoLLoALWqALKqgLKqoLVh8NHhMPgY8Jh8ABZMHgEUQZSRJO4KRJBYMBQZQLKqkBMBB3C4ALTh/1oALJQYILJgHQR5SDJBYKDJBYKDJBYJ3JBYR3IBYMDBY0K0ALBgALFgWq0tPAoJrFhWqqtXBY8qBYKaBgHwBYmq1WVBYUwBY2pqo5BBYkC0tWEgILHquptQLCaY2qrWVBZFay2VqALHqumz4LGgIvB0ufEYwLCsoLJ0tVr6xHRoP//q8Hiv///8BY8XBYKYFGAYLBcBAkBqgHF")) \ No newline at end of file diff --git a/apps/simplemusic/app.js b/apps/simplemusic/app.js new file mode 100644 index 000000000..6b35ea9c8 --- /dev/null +++ b/apps/simplemusic/app.js @@ -0,0 +1,203 @@ +// For info on interfacing with Gadgetbridge, see https://www.espruino.com/Gadgetbridge +const Debug = false; // Set to true to show debugging into +const Layout = require("Layout"); +const PrimaryFont = "Vector:18"; + +const buttonPadding = 10; + +const Command = { + next: "next", + pause: "pause", + play: "play", + previous: "previous", + volumeup: "volumeup", + volumedown: "volumedown", +}; + +const PlaybackState = { + paused: "pause", + playing: "play" +}; + +/** + * Format elapsed time in minutes and seconds. + * @param {*} time Elapsed time + * @returns Time string + */ +function formatTime(time) { + let minute = 0, second = 0; + if (time) { + minute = Math.floor(time / 60); + second = time % 60; + } + let minuteStr = minute.toString(), secondStr = second.toString(); + + if (minute < 10) minuteStr = `0${minute}`; + if (second < 10) secondStr = `0${second}`; + + return `${minuteStr}:${secondStr}`; +} + +/** + * Global playback state tracker. + * Follows the syntax {t:"musicstate", state:"play/pause",position,shuffle,repeat} + */ +let appState = { t: "musicstate", state: PlaybackState.paused, position: 0, shuffle: 0, repeat: 0 }; + +/** + * Define the screen layout. + */ +let layout = new Layout({ + type: "v", c: [ + { type: "txt", id: "title", halign: -1, fillx: 0, col: g.fg, font: PrimaryFont, label: "Track N/A" }, + { type: "txt", id: "artist", halign: -1, fillx: 0, col: g.fg, font: PrimaryFont, label: "Artist N/A" }, + { + type: "h", c: [ + { type: "txt", id: "elapsed", halign: -1, fillx: 1, col: g.fg, font: PrimaryFont, label: formatTime(0) }, + { type: "txt", id: "timeSplitter", halign: 0, fillx: 1, col: g.fg, font: PrimaryFont, label: " - " }, + { type: "txt", id: "duration", halign: 1, fillx: 1, col: g.fg, font: PrimaryFont, label: formatTime(0) } + ] + }, + { + type: "h", c: [ + { type: "btn", id: Command.previous, font: PrimaryFont, col: g.fg2, bgCol: g.bg2, pad: buttonPadding, label: "|<<", cb: l => sendCommand(Command.previous, true) }, + { type: "btn", id: "playpause", font: PrimaryFont, col: g.fg2, bgCol: g.bg2, pad: buttonPadding, label: " > ", cb: l => sendCommand(appState.state === PlaybackState.paused ? Command.play : Command.pause, true) }, + { type: "btn", id: Command.next, font: PrimaryFont, col: g.fg2, bgCol: g.bg2, pad: buttonPadding, label: ">>|", cb: l => sendCommand(Command.next, true) } + ] + }, + ] +}, { lazy: true }); + +/// Set up the app +function initialize() { + // Detect whether we're using an emulator. + if (typeof Bluetooth === "undefined" || typeof Bluetooth.println === "undefined") { // emulator! + Bluetooth = { + println: (line) => { console.log("Bluetooth:", line); }, + }; + } + + // Set up listeners for swiping + Bangle.on('swipe', function (directionLR, directionUD) { + switch (directionLR) { + case -1: // Left + sendCommand(Command.previous, true); + break; + case 1: // Right + sendCommand(Command.next, true); + break; + } + + switch (directionUD) { + case -1: // Up + sendCommand(Command.volumeup, true); + break; + case 1: // Down + sendCommand(Command.volumedown, true); + break; + } + }); + + // Goad Gadgetbridge into sending us the current track info + sendCommand(Command.volumeup, false); + sendCommand(Command.volumedown, false); +} + +function draw() { + layout.render(); + Bangle.drawWidgets(); +} + +/** + * Send a command via Bluetooth back to Gadgetbridge. + * @param {Command} command Which command to execute + * @param {true|false} buzz Whether to vibrate the motor + */ +function sendCommand(command, buzz) { + if (buzz) Bangle.buzz(50); + Bluetooth.println(JSON.stringify({ t: "music", n: command })); + + switch (command) { + // If this is a play or pause command, display the track and artist + case Command.play: + updateState(PlaybackState.playing); + break; + case Command.pause: + updateState(PlaybackState.paused); + break; + // Reset the duration clock for new tracks + case Command.next: + case Command.previous: + layout.elapsed.label = formatTime(0); + break; + } +} + +/// Track how long the current song has been running. +let elapsedTimer; +let position = 0; +function updateTime() { + position++; + layout.elapsed.label = formatTime(position); + layout.render(); + + if (Debug) console.log("Tick"); +} + +/** + * Get info about the current playing song. + * @param {Object} info - Gadgetbridge musicinfo event + */ +function showTrackInfo(info) { + layout.title.label = info ? info.track : "Track N/A"; + layout.artist.label = info ? info.artist : "Artist N/A"; + layout.duration.label = info ? formatTime(info.dur) : formatTime(0); + draw(); + if (Debug) layout.debug(); +} + +/** + * Updates the current state of the app. + * Called when Gadgetbridge updates (see boot.js) + * @param {*} state + */ +function updateState(state) { + appState.state = state; + position = state.position; + + // Alternate between play and pause symbols + if (state === PlaybackState.playing) { + elapsedTimer = setInterval(updateTime, 1000); + layout.playpause.label = " || "; + } + else if (state === PlaybackState.paused) { + if (elapsedTimer) clearInterval(elapsedTimer); + layout.playpause.label = " > "; + } +} + +/** + * Listen for Gadgetbridge events + */ +setTimeout( + () => { + globalThis.GB = (_GB => e => { + switch (e.t) { + case "musicinfo": + return showTrackInfo(e); + case "musicstate": + return updateState(e); + default: + // pass on other events + if (_GB) setTimeout(_GB, 0, e); + } + })(globalThis.GB); + }, 1); + + +// Start the app +initialize(); + +// Render the screen +g.clear(); +draw(); \ No newline at end of file diff --git a/apps/simplemusic/app.png b/apps/simplemusic/app.png new file mode 100644 index 0000000000000000000000000000000000000000..40bd83ade54b2506e6c4aea8de237bcdf28dd3a7 GIT binary patch literal 2289 zcmVhC3 zTgyPLk5ROR+M%^N;~#BD+Zl#Zt5Q4D7K<&QGs*xm6d78mzJmXE}{xzPYn zaKnPfPQ9Kg)swj~4k8KL``3S(`2mpe3 z%`@sGe0&Lfj`Yqmz`=355LtDjL1B_5IdVfB`1QC@cY^_a$q&vZD6y(@wVK|XmUvUcrT(R==5Uv>*GB5f$y3Jz_rE6c@6Z={Ymr)=MbG`hy zG65iD($87qG25_g`r;eMlX6Z1IMAfhl0we@X1!N_@OwoC*WT-SeNr@utF1iElp@osnFGDLNe{JEP!Rh+TR_B-*@#f z6!DQkg~~8qD!h%tECv%ipt<(P3xx@j3-*VOo*rK7kV|ri*B_I@96mffE`!7Q~lwY4r9ZmK5M~yA1mJmjdtSK z@w@@RTIU({)e9l>Tz8+fX37*v1(WvX6#)2fda|v@`Xjp1c@1^mO1Nb7EJUhLdH1`y@{9YuL<|p zcfTiz$Vc*dqpj(gcZirG;}L@Dp$0G$QfyYzi|?iq(JXNM3@x%T0P4cCO79?{haF)a^d;JwFiW?M2!MYyrN_v!xDh8*rJF;Co#hn*V9MS5XosOUVZ-?z z0}|*sDFU$!0!he%^PWhBJ zRAsJq#(oy@x@s9#&i6xAu>fx!Nki-9p~ZC33@@e`1+6hrQv`4OP#=>GN$}hNKzr5! zU`4r&Q6-I$XEb6xlC)v#;S{`mEDh&ECiqbU-@VfZV}m?wKc04JHZ~~0tw9me3Q`CG zU>1mJIYAXb9H!P`zR2aQ15gY)kitn{?QX5l*bqxO(A{srrh`e?cUo~P(z1FVw1rK$ z&}+IInW_?j?RP=b**=(p#G@>1dj0iaM>WOnWF0_J0e3K|3}qmNlVNqA;|M&uC;&gb zzX+Dq`Cvz5no$j^%SD$~&D}#Z8spp*iwAxRcM|{a}EP6b86|#WU*o-vN8GpYDf0lhl^i9fv&@9;xHYsilnf~qOh+m zv8gLMVHue-Nsc}JjleS##~N=-{YH3Es3$A@|I!q?wmq|@|Ka9K#;>|#)|6z%ap}ZX zTJ%b8sbpuMl|g}iz1DN^$tC_@E}6aM Date: Wed, 4 Jun 2025 21:27:30 -0400 Subject: [PATCH 02/11] simplemusic: add clarifying note in README --- apps/simplemusic/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/simplemusic/README.md b/apps/simplemusic/README.md index 9d6859474..2cc5c80b0 100644 --- a/apps/simplemusic/README.md +++ b/apps/simplemusic/README.md @@ -10,6 +10,8 @@ Requires [Gadgetbridge](https://www.espruino.com/Gadgetbridge). 2. Open a music player on your Android phone. 3. Open this app on your Bangle.js. +Note: On startup, Simple Music will ping Gadgetbridge for track data by quickly turning the volume up, then down. This only happens once, and shouldn't be noticeable in most cases. + ## Features - Shows the current song title and album From 44a988f7df9d40acdd4f311e486f229dc7620c82 Mon Sep 17 00:00:00 2001 From: Andre Date: Thu, 5 Jun 2025 12:34:19 -0400 Subject: [PATCH 03/11] simplemusic: fix app icon --- apps/simplemusic/metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/simplemusic/metadata.json b/apps/simplemusic/metadata.json index db95d3c77..e9ca81460 100644 --- a/apps/simplemusic/metadata.json +++ b/apps/simplemusic/metadata.json @@ -3,7 +3,7 @@ "shortName":" Simple Music", "version":"0.01", "description": "Control the music on your Gadgetbridge-enabled phone using Bangle.js. This is a remixed version of rigrig's fantastic Gadgetbridge Music Controls app.", - "icon": "icon.png", + "icon": "app.png", "type": "app", "tags": "bluetooth,gadgetbridge,music,tools", "supports" : ["BANGLEJS2"], From ffc08f0d4ce31c2f829221ab9f9115f3b6928a29 Mon Sep 17 00:00:00 2001 From: Andre Date: Thu, 5 Jun 2025 13:48:55 -0400 Subject: [PATCH 04/11] simplemusic: add listener for button presses --- apps/simplemusic/README.md | 6 ++++++ apps/simplemusic/app.js | 31 +++++++++++++++++-------------- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/apps/simplemusic/README.md b/apps/simplemusic/README.md index 2cc5c80b0..7309f0cc0 100644 --- a/apps/simplemusic/README.md +++ b/apps/simplemusic/README.md @@ -20,6 +20,12 @@ Note: On startup, Simple Music will ping Gadgetbridge for track data by quickly ## Controls +### Button controls + +Press the side button to toggle playing or pausing the current track. + +### Touch controls + Use the on-screen buttons to go back to the previous track, play/pause the current track, or skip to the next track. Swipe up/down to increase/decrease the volume, or swip left/right to navigate to the previous/next song. diff --git a/apps/simplemusic/app.js b/apps/simplemusic/app.js index 6b35ea9c8..ff74d6e5b 100644 --- a/apps/simplemusic/app.js +++ b/apps/simplemusic/app.js @@ -179,21 +179,24 @@ function updateState(state) { /** * Listen for Gadgetbridge events */ -setTimeout( - () => { - globalThis.GB = (_GB => e => { - switch (e.t) { - case "musicinfo": - return showTrackInfo(e); - case "musicstate": - return updateState(e); - default: - // pass on other events - if (_GB) setTimeout(_GB, 0, e); - } - })(globalThis.GB); - }, 1); +setTimeout(() => { + globalThis.GB = (_GB => e => { + switch (e.t) { + case "musicinfo": + return showTrackInfo(e); + case "musicstate": + return updateState(e); + default: + // pass on other events + if (_GB) setTimeout(_GB, 0, e); + } + })(globalThis.GB); +}, 1); +// Toggle play/pause if the button is pressed +setWatch(function() { + sendCommand(appState.state === PlaybackState.paused ? Command.play : Command.pause, true); +}, BTN, {edge:"rising", debounce:50, repeat:true}); // Start the app initialize(); From d2ab040f314e21a092d71398132739305d6ba9f6 Mon Sep 17 00:00:00 2001 From: Andre Date: Fri, 6 Jun 2025 09:51:17 -0400 Subject: [PATCH 05/11] simplemusic: add note to use Messages lib instead of listening to GB directly --- apps/simplemusic/app.js | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/apps/simplemusic/app.js b/apps/simplemusic/app.js index ff74d6e5b..1dc30f2dc 100644 --- a/apps/simplemusic/app.js +++ b/apps/simplemusic/app.js @@ -1,3 +1,5 @@ +// FIXME: Messages collects music messages and puts them in a queue. Listen to that instead + // For info on interfacing with Gadgetbridge, see https://www.espruino.com/Gadgetbridge const Debug = false; // Set to true to show debugging into const Layout = require("Layout"); @@ -108,6 +110,17 @@ function draw() { Bangle.drawWidgets(); } +/// Track how long the current song has been running. +let elapsedTimer; +let position = 0; +function updateTime() { + position++; + layout.elapsed.label = formatTime(position); + layout.render(); + + if (Debug) console.log("Tick"); +} + /** * Send a command via Bluetooth back to Gadgetbridge. * @param {Command} command Which command to execute @@ -125,25 +138,14 @@ function sendCommand(command, buzz) { case Command.pause: updateState(PlaybackState.paused); break; - // Reset the duration clock for new tracks + // Reset the duration clock when switching tracks case Command.next: case Command.previous: - layout.elapsed.label = formatTime(0); + updateState(appState.state); break; } } -/// Track how long the current song has been running. -let elapsedTimer; -let position = 0; -function updateTime() { - position++; - layout.elapsed.label = formatTime(position); - layout.render(); - - if (Debug) console.log("Tick"); -} - /** * Get info about the current playing song. * @param {Object} info - Gadgetbridge musicinfo event From ee91c6a23ccaff89ff8257bd73afc7c64adad676 Mon Sep 17 00:00:00 2001 From: Andre Date: Fri, 6 Jun 2025 11:51:08 -0400 Subject: [PATCH 06/11] simplemusic: listen to events from the message queue instead of Gadgetbridge --- apps/simplemusic/app.js | 58 ++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 30 deletions(-) diff --git a/apps/simplemusic/app.js b/apps/simplemusic/app.js index 1dc30f2dc..446181473 100644 --- a/apps/simplemusic/app.js +++ b/apps/simplemusic/app.js @@ -1,5 +1,3 @@ -// FIXME: Messages collects music messages and puts them in a queue. Listen to that instead - // For info on interfacing with Gadgetbridge, see https://www.espruino.com/Gadgetbridge const Debug = false; // Set to true to show debugging into const Layout = require("Layout"); @@ -100,9 +98,30 @@ function initialize() { } }); + // Listen for music events + Bangle.on("music", (type, message)=>{ + switch (type) { + case "musicinfo": + showTrackInfo(message); + break; + case "musicstate": + updateState(message); + break; + }; + }); + + // Toggle play/pause if the button is pressed + setWatch(function() { + sendCommand(appState.state === PlaybackState.paused ? Command.play : Command.pause, true); + }, BTN, {edge:"rising", debounce:50, repeat:true}); + // Goad Gadgetbridge into sending us the current track info sendCommand(Command.volumeup, false); sendCommand(Command.volumedown, false); + + // Render the screen + g.clear(); + draw(); } function draw() { @@ -130,6 +149,7 @@ function sendCommand(command, buzz) { if (buzz) Bangle.buzz(50); Bluetooth.println(JSON.stringify({ t: "music", n: command })); + /* switch (command) { // If this is a play or pause command, display the track and artist case Command.play: @@ -144,6 +164,7 @@ function sendCommand(command, buzz) { updateState(appState.state); break; } + */ } /** @@ -169,40 +190,17 @@ function updateState(state) { // Alternate between play and pause symbols if (state === PlaybackState.playing) { + if (Debug) console.log("Playing"); elapsedTimer = setInterval(updateTime, 1000); layout.playpause.label = " || "; } else if (state === PlaybackState.paused) { + if (Debug) console.log("Paused"); if (elapsedTimer) clearInterval(elapsedTimer); layout.playpause.label = " > "; } + draw(); } -/** - * Listen for Gadgetbridge events - */ -setTimeout(() => { - globalThis.GB = (_GB => e => { - switch (e.t) { - case "musicinfo": - return showTrackInfo(e); - case "musicstate": - return updateState(e); - default: - // pass on other events - if (_GB) setTimeout(_GB, 0, e); - } - })(globalThis.GB); -}, 1); - -// Toggle play/pause if the button is pressed -setWatch(function() { - sendCommand(appState.state === PlaybackState.paused ? Command.play : Command.pause, true); -}, BTN, {edge:"rising", debounce:50, repeat:true}); - -// Start the app -initialize(); - -// Render the screen -g.clear(); -draw(); \ No newline at end of file +// Start the app and set up listeners +initialize(); \ No newline at end of file From 3ffbf6e395cf334a29f7e17f4fe64d04401b614c Mon Sep 17 00:00:00 2001 From: Andre Date: Fri, 6 Jun 2025 12:05:11 -0400 Subject: [PATCH 07/11] simplemusic: clean up event processing --- apps/simplemusic/app.js | 102 +++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 59 deletions(-) diff --git a/apps/simplemusic/app.js b/apps/simplemusic/app.js index 446181473..98e944e2a 100644 --- a/apps/simplemusic/app.js +++ b/apps/simplemusic/app.js @@ -98,15 +98,11 @@ function initialize() { } }); - // Listen for music events - Bangle.on("music", (type, message)=>{ - switch (type) { - case "musicinfo": - showTrackInfo(message); - break; - case "musicstate": - updateState(message); - break; + // Eat music events (๑ᵔ⤙ᵔ๑) + Bangle.on("message", (type, message)=>{ + if (type === "music" && !message.handled) { + processMusicEvent(message); + message.handled = true; }; }); @@ -125,8 +121,8 @@ function initialize() { } function draw() { + layout.update(); layout.render(); - Bangle.drawWidgets(); } /// Track how long the current song has been running. @@ -135,7 +131,7 @@ let position = 0; function updateTime() { position++; layout.elapsed.label = formatTime(position); - layout.render(); + draw(); if (Debug) console.log("Tick"); } @@ -148,59 +144,47 @@ function updateTime() { function sendCommand(command, buzz) { if (buzz) Bangle.buzz(50); Bluetooth.println(JSON.stringify({ t: "music", n: command })); - - /* - switch (command) { - // If this is a play or pause command, display the track and artist - case Command.play: - updateState(PlaybackState.playing); - break; - case Command.pause: - updateState(PlaybackState.paused); - break; - // Reset the duration clock when switching tracks - case Command.next: - case Command.previous: - updateState(appState.state); - break; - } - */ } -/** - * Get info about the current playing song. - * @param {Object} info - Gadgetbridge musicinfo event - */ -function showTrackInfo(info) { - layout.title.label = info ? info.track : "Track N/A"; - layout.artist.label = info ? info.artist : "Artist N/A"; - layout.duration.label = info ? formatTime(info.dur) : formatTime(0); +function processMusicEvent(event) { + if (Debug) console.log("State: " + event.state); + if (Debug) console.log("Position: " + event.position); + + if (event.position !== null) position = event.position; + + switch(event.state) { + case PlaybackState.playing: + if (Debug) console.log("Playing"); + appState.state = event.state; + elapsedTimer = setInterval(updateTime, 1000); + layout.playpause.label = " || "; + break; + case PlaybackState.paused: + if (Debug) console.log("Paused"); + appState.state = event.state; + clearInterval(elapsedTimer); + layout.playpause.label = " > "; + break; + case PlaybackState.previous: + case PlaybackState.next: + // Reset position + position = 0; + appState.state = PlaybackState.playing; + break; + } + + // Render track info on song change + if (event.track != layout.title.label) { + clearInterval(elapsedTimer); + elapsedTimer = setInterval(updateTime, 1000); + layout.title.label = event ? event.track : "Track N/A"; + layout.artist.label = event ? event.artist : "Artist N/A"; + layout.duration.label = formatTime(0); + }; + draw(); if (Debug) layout.debug(); } -/** - * Updates the current state of the app. - * Called when Gadgetbridge updates (see boot.js) - * @param {*} state - */ -function updateState(state) { - appState.state = state; - position = state.position; - - // Alternate between play and pause symbols - if (state === PlaybackState.playing) { - if (Debug) console.log("Playing"); - elapsedTimer = setInterval(updateTime, 1000); - layout.playpause.label = " || "; - } - else if (state === PlaybackState.paused) { - if (Debug) console.log("Paused"); - if (elapsedTimer) clearInterval(elapsedTimer); - layout.playpause.label = " > "; - } - draw(); -} - // Start the app and set up listeners initialize(); \ No newline at end of file From b29e7843e30041c8fbc26485caefd524d836090e Mon Sep 17 00:00:00 2001 From: Andre Date: Sun, 8 Jun 2025 16:59:44 -0400 Subject: [PATCH 08/11] simplemusic: Fix track duration tracking --- apps/simplemusic/app.js | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/apps/simplemusic/app.js b/apps/simplemusic/app.js index 98e944e2a..23765479c 100644 --- a/apps/simplemusic/app.js +++ b/apps/simplemusic/app.js @@ -100,16 +100,18 @@ function initialize() { // Eat music events (๑ᵔ⤙ᵔ๑) Bangle.on("message", (type, message)=>{ - if (type === "music" && !message.handled) { + if (type.includes("music") && !message.handled) { processMusicEvent(message); message.handled = true; }; }); // Toggle play/pause if the button is pressed + /* setWatch(function() { sendCommand(appState.state === PlaybackState.paused ? Command.play : Command.pause, true); }, BTN, {edge:"rising", debounce:50, repeat:true}); + */ // Goad Gadgetbridge into sending us the current track info sendCommand(Command.volumeup, false); @@ -125,7 +127,7 @@ function draw() { layout.render(); } -/// Track how long the current song has been running. +// Track how long the current song has been running. let elapsedTimer; let position = 0; function updateTime() { @@ -136,6 +138,14 @@ function updateTime() { if (Debug) console.log("Tick"); } +function clearTimer() { + position = 0; + if (elapsedTimer) { + clearInterval(elapsedTimer); + elapsedTimer = undefined; + }; +} + /** * Send a command via Bluetooth back to Gadgetbridge. * @param {Command} command Which command to execute @@ -143,14 +153,14 @@ function updateTime() { */ function sendCommand(command, buzz) { if (buzz) Bangle.buzz(50); - Bluetooth.println(JSON.stringify({ t: "music", n: command })); + Bangle.musicControl(command); } function processMusicEvent(event) { if (Debug) console.log("State: " + event.state); if (Debug) console.log("Position: " + event.position); - if (event.position !== null) position = event.position; + position = event.position; switch(event.state) { case PlaybackState.playing: @@ -162,7 +172,7 @@ function processMusicEvent(event) { case PlaybackState.paused: if (Debug) console.log("Paused"); appState.state = event.state; - clearInterval(elapsedTimer); + clearTimer(); layout.playpause.label = " > "; break; case PlaybackState.previous: @@ -173,13 +183,12 @@ function processMusicEvent(event) { break; } - // Render track info on song change + // Re-render track info on song change if (event.track != layout.title.label) { - clearInterval(elapsedTimer); - elapsedTimer = setInterval(updateTime, 1000); + position = 0; layout.title.label = event ? event.track : "Track N/A"; layout.artist.label = event ? event.artist : "Artist N/A"; - layout.duration.label = formatTime(0); + layout.duration.label = formatTime(event.dur); }; draw(); From c3acf511e04db82ea5caa0d3d47bb41158759bbd Mon Sep 17 00:00:00 2001 From: Andre Date: Mon, 9 Jun 2025 10:20:35 -0400 Subject: [PATCH 09/11] simplemusic: fix lint errors --- apps/simplemusic/app.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/simplemusic/app.js b/apps/simplemusic/app.js index 23765479c..f8112089f 100644 --- a/apps/simplemusic/app.js +++ b/apps/simplemusic/app.js @@ -103,7 +103,7 @@ function initialize() { if (type.includes("music") && !message.handled) { processMusicEvent(message); message.handled = true; - }; + } }); // Toggle play/pause if the button is pressed @@ -143,7 +143,7 @@ function clearTimer() { if (elapsedTimer) { clearInterval(elapsedTimer); elapsedTimer = undefined; - }; + } } /** @@ -189,7 +189,7 @@ function processMusicEvent(event) { layout.title.label = event ? event.track : "Track N/A"; layout.artist.label = event ? event.artist : "Artist N/A"; layout.duration.label = formatTime(event.dur); - }; + } draw(); if (Debug) layout.debug(); From e7c2c62b17fd5019380d85413be9cbff168da0ad Mon Sep 17 00:00:00 2001 From: Andre Date: Mon, 9 Jun 2025 10:40:18 -0400 Subject: [PATCH 10/11] simplemusic: re-add button controls --- apps/simplemusic/README.md | 4 +--- apps/simplemusic/app.js | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/apps/simplemusic/README.md b/apps/simplemusic/README.md index 7309f0cc0..b93342e6c 100644 --- a/apps/simplemusic/README.md +++ b/apps/simplemusic/README.md @@ -10,12 +10,10 @@ Requires [Gadgetbridge](https://www.espruino.com/Gadgetbridge). 2. Open a music player on your Android phone. 3. Open this app on your Bangle.js. -Note: On startup, Simple Music will ping Gadgetbridge for track data by quickly turning the volume up, then down. This only happens once, and shouldn't be noticeable in most cases. - ## Features - Shows the current song title and album -- Provides buttons for changing or pausing the current track. +- Provides on-screen buttons for changing or pausing the current track. - Supports swiping ## Controls diff --git a/apps/simplemusic/app.js b/apps/simplemusic/app.js index f8112089f..1cd0d3dbc 100644 --- a/apps/simplemusic/app.js +++ b/apps/simplemusic/app.js @@ -107,11 +107,9 @@ function initialize() { }); // Toggle play/pause if the button is pressed - /* setWatch(function() { sendCommand(appState.state === PlaybackState.paused ? Command.play : Command.pause, true); - }, BTN, {edge:"rising", debounce:50, repeat:true}); - */ + }, BTN, {edge: "falling", debounce: 50, repeat: true}); // Goad Gadgetbridge into sending us the current track info sendCommand(Command.volumeup, false); From f500a9cf0f1474eab7ffbae576971f93a9865e60 Mon Sep 17 00:00:00 2001 From: Andre Date: Mon, 9 Jun 2025 12:52:25 -0400 Subject: [PATCH 11/11] simplemusic: remove unused metadata --- apps/simplemusic/metadata.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/simplemusic/metadata.json b/apps/simplemusic/metadata.json index e9ca81460..e2622eafd 100644 --- a/apps/simplemusic/metadata.json +++ b/apps/simplemusic/metadata.json @@ -12,6 +12,5 @@ "storage": [ {"name":"simplemusic.app.js","url":"app.js"}, {"name":"simplemusic.img","url":"app-icon.js","evaluate":true} - ], - "data": [{"name":"simplemusic.json"},{"name":"simplemusic.load.json"}] + ] }