Skip to content

Instantly share code, notes, and snippets.

@marco79cgn
Last active January 6, 2024 07:46
Show Gist options
  • Save marco79cgn/98616fcbb2dfdbd752b33a452208bcc8 to your computer and use it in GitHub Desktop.
Save marco79cgn/98616fcbb2dfdbd752b33a452208bcc8 to your computer and use it in GitHub Desktop.
A scriptable widget that shows what's playing on SONOS
// Variables used by Scriptable.
// These must be at the very top of the file. Do not edit.
// icon-color: cyan; icon-glyph: magic;
// the node sonos http api base url (running on your Pi for example)
let sonosBaseUrl = "http://192.168.178.10:5005"
// optional for cover art: the ip address of one of your Sonos speakers
let sonosPlayerUrl = "http://192.168.178.38:1400"
let param = args.widgetParameter
let nowPlaying = await loadNowPlaying()
let widget = await createWidget(nowPlaying)
Script.setWidget(widget)
Script.complete()
//widget.presentSmall()
async function createWidget(nowPlaying) {
let widget = new ListWidget()
widget.setPadding(5, 6, 0, 5)
// show album art if configured and available
let albumArtUrl = nowPlaying.currentTrack.albumArtUri
if(param === "cover" && albumArtUrl != null && albumArtUrl.length > 1) {
if(albumArtUrl.indexOf("http") > -1) {
let albumArt = await loadImage(albumArtUrl)
widget.backgroundImage = albumArt
} else {
let albumArt = await loadImage(sonosPlayerUrl + nowPlaying.currentTrack.albumArtUri)
widget.backgroundImage = albumArt
}
}
// set gradient background
let startColor = new Color("#da2f68e6")
let endColor = new Color("#c30645b3")
let gradient = new LinearGradient()
gradient.colors = [startColor, endColor]
gradient.locations = [0, 1]
widget.backgroundColor = new Color("#b00a0f")
widget.backgroundGradient = gradient
let sonosLogo = await loadImage("https://pisces.bbystatic.com/image2/BestBuy_US/Gallery/sonos_logo_white_2.png")
let image = widget.addImage(sonosLogo)
image.imageSize = new Size(35,15)
image.centerAlignImage()
widget.addSpacer(5)
let nowText = widget.addText("NOW PLAYING:")
nowText.font = Font.boldSystemFont(12)
nowText.textColor = Color.white()
nowText.centerAlignText()
nowText.textOpacity = 0.8
// add title and artist
let shortTitle = ""
if (nowPlaying.currentTrack.type === "radio" && albumArtUrl != null && albumArtUrl.indexOf("radioparadise") === -1) {
widget.addSpacer(15)
if(nowPlaying.currentTrack.title.indexOf("x-sonosapi") === -1) {
shortTitle = demasterRadio(nowPlaying.currentTrack.title)
}
} else {
shortTitle = demaster(nowPlaying.currentTrack.title)
}
let titleTxt = widget.addText(shortTitle)
titleTxt.font = Font.semiboldSystemFont(12)
titleTxt.textColor = Color.white()
titleTxt.centerAlignText()
let artistTxt = widget.addText(nowPlaying.currentTrack.artist)
artistTxt.font = Font.semiboldSystemFont(12)
artistTxt.textColor = Color.cyan()
artistTxt.centerAlignText()
if (nowPlaying.currentTrack.type !== "radio" || (albumArtUrl != null && albumArtUrl.indexOf("radioparadise") !== -1)) {
let separatorText = widget.addText("—")
separatorText.textColor = Color.white()
separatorText.centerAlignText()
separatorText.textOpacity = 0.7
let nextText = widget.addText("NEXT UP:")
nextText.font = Font.boldSystemFont(12)
nextText.textColor = Color.white()
nextText.centerAlignText()
nextText.textOpacity = 0.8
// add next title and artist
let shortNextTitle = demaster(nowPlaying.nextTrack.title)
let nextTitleTxt = widget.addText(shortNextTitle)
nextTitleTxt.textColor = Color.white()
nextTitleTxt.font = Font.semiboldSystemFont(12)
nextTitleTxt.centerAlignText()
let nextArtistTxt = widget.addText(nowPlaying.nextTrack.artist)
nextArtistTxt.textColor = Color.cyan()
nextArtistTxt.font = Font.semiboldSystemFont(12)
nextArtistTxt.centerAlignText()
}
if(config.runsInApp) {
let request = new Request(sonosBaseUrl + "/playpause")
request.allowInsecureRequest = true
let json = await request.loadJSON()
Safari.open("shortcuts://run-shortcut?name=SpringBoard")
}
widget.addSpacer()
return widget
}
// load and parse a restful json api
async function loadNowPlaying() {
let url = sonosBaseUrl + "/state"
let req = new Request(url)
req.allowInsecureRequest = true
let json = await req.loadJSON()
console.log(json)
return json
}
// download an image from a given url
async function loadImage(imgUrl) {
console.log(imgUrl)
let req = new Request(imgUrl)
req.allowInsecureRequest = true
let image = await req.loadImage()
return image
}
function demaster(trackName) {
let shortenedTrackName = trackName.split(" - ")
shortenedTrackName = shortenedTrackName[0].split(" (Live")
shortenedTrackName = shortenedTrackName[0].split(" (feat")
console.log("short name: " + shortenedTrackName[0])
return shortenedTrackName[0]
}
function demasterRadio(trackName) {
let shortenedTrackName = trackName.split(" |")
shortenedTrackName = shortenedTrackName[0].split(" --")
console.log("short name: " + shortenedTrackName[0])
return shortenedTrackName[0]
}
@marco79cgn
Copy link
Author

marco79cgn commented Sep 18, 2020

Intro

iOS 14 Custom Widget made with the help of the Scriptable app.
The widget shows what's currently playing on your Sonos system. It relies on the node-sonos-http-api which has to be running locally (e.g. on a Raspberry Pi). Once you tap on the widget it toggles the playback state (pause/play).

Requirements

  • at least one Sonos or IKEA Symfonisk speaker
  • iOS 14
  • Scriptable version 1.5 (or above)
  • The node-sonos-http-api. Insert your ip address in the script. If you want to display transparent cover art in the background, set "cover" as a widget parameter and make sure to insert the ip address of one of your Sonos speakers as well in the script.
  • The shortcut to return to the homescreen (springboard) which can be downloaded here.

Thanks

A big Thank you to @simonbs for making great apps like Scriptable, DataJar or Jayson.

Changelog

  • (18.09.2020) initial release
  • (19.09.2020)
    -- display proper information when playing radio
    -- showing transparent cover in the background
    -- cleanup song information (to avoid unneeded stuff like "Remastered Version" etc.)

@Token2K
Copy link

Token2K commented Jun 8, 2021

Hi Marco,

... vielleicht kannst du mit der Fehlermeldung mehr anfangen, und evtl. mir weiterhelfen?

2021-06-09 00:51:45: {"volume":13,"mute":false,"elapsedTimeFormatted":"00:00:00","currentTrack":{"artist":"Radio Brocken","trackUri":"x-sonosapi-stream:rpde_svc_74?sid=232&flags=32&sn=2","albumArtUri":"/getaa?s=1&u=x-sonosapi-stream%3arpde_svc_74%3fsid%3d232%26flags%3d32%26sn%3d2","absoluteAlbumArtUri":"http://192.168.2.115:1400/getaa?s=1&u=x-sonosapi-stream%3arpde_svc_74%3fsid%3d232%26flags%3d32%26sn%3d2","duration":0,"stationName":"Radio Brocken","type":"radio","uri":"x-sonosapi-stream:rpde_svc_74?sid=232&flags=32&sn=2"},"trackNo":1,"elapsedTime":0,"equalizer":{"nightMode":false,"treble":-1,"bass":2,"loudness":true,"speechEnhancement":false},"playbackState":"STOPPED","playMode":{"shuffle":false,"crossfade":false,"repeat":"none"},"nextTrack":{"duration":0,"uri":"","title":"","album":"","albumArtUri":"","artist":""}}
2021-06-09 00:51:45: https://pisces.bbystatic.com/image2/BestBuy_US/Gallery/sonos_logo_white_2.png
2021-06-09 00:51:45: Error on line 60:37: TypeError: undefined is not an object (evaluating 'nowPlaying.currentTrack.title.indexOf')

Danke

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment