Widget Examples

2 posts were split to a new topic: Spotify Playlist

Here is my version, @realliyifei

This week’s upcoming events with date info on top. The date abbreviation is local.

image

(Shortcoming for some is that it only displays current week events only. I have to fix it so you can choose when next week starts to show.)

Edit: Cleaned up comments and variable names.

// Variables used by Scriptable.
// These must be at the very top of the file. Do not edit.
// icon-color: red; icon-glyph: calendar-alt;
// This widget is based on code by Max Zeryck @mzeryck
// GitHub: https://gist.github.com/mzeryck/4f9255224fe707ee74d86dc6465feea2
// Scriptable Forum post: https://talk.automators.fm/t/widget-examples/7994/83
// 
// Alterations and additional commenting made by Erkka
// The widget shows
// - date, weekday and week number on top
// - next event time and title
// - upcoming event titles
// - this should work in all widget sizes
// - note: the widget shows only current week


// TEST MODE SELECTOR
  // If true, tapping the script/widget 
  // shows a pop-up with refreshed widget
  // If false, it the calendar launches
  const TEST_MODE = true
  

// ------------------------
// User tweakable constants

  // Font sizes  
  const headerFont = 20
  const firstEvent = 14
  const listEvents = 10
  
  // Space before and after the 1st event  
  const firstPadding = 8
  // Space between other events
  const eventSpacer = 6
 
  // Max number of events shown 
  const maxEvents = 7

  // Date formatting:
    // Check Scriptable documentation: "DateFormat"
    // Used here:
    // E => short weekday, like "mon"
    // dd => day 01-31
    // MM => month 01-12
    // ww => week number
    // Note: You can use additional characters 
    // and spaces... "(yyyy)" => (2020)    
  
  // Header date  
  const headerDateFormat = "dd/MM  E/ww"
  // Prefix in event listing 
  const eventPrefix = "dd/ "

// ---------------
// Other constants

  // Let's store the current datetime
  const date = new Date()
    
// -----------------------------
// If the widget is running (and test mode is false)
// tapping the widget (or preas "play" here in the Scriptable editor) the Calendar opens...
if (!config.runsInWidget && !TEST_MODE) {
  // I'm not sure what is the logic here,
  // but it works
  const appleDate = new Date('2001/01/01')
  const timestamp = (date.getTime() - appleDate.getTime()) / 1000
  const callback = new CallbackURL("calshow:"+timestamp)
  callback.open()
  Script.complete()
  
// ...otherwise, create the widget.  
} else {


  // ---------------------------------
  // FIRST WE CREATE THE EVENT ARRAY  
  // Get calendar events into an array
  // scope used: thisWeek  
  const events = await CalendarEvent.thisWeek([])
  
  
  // Setup an array   
  let futureEvents = []
  
  // Put all future events in the array  
  for (const event of events) {
      if (event.startDate.getTime() > date.getTime()){
          futureEvents.push(event)  
      }
  }
  
  //  Count the events in the array
  //  We'll use this later 
  let eventsTotal = futureEvents.length
  
  // ARRAY STUFF IS NOW READY  
  // ----------------------------
  // NEXT: Let's build the widget  


  // -----------------
  // THE WIDGET BEGINS
  let widget = new ListWidget()
  
   
  // FIRST: THE HEADER
  // (The top row with current date) 
  
  
  
  // A new date formatter for the hader date.
  // Format set in a const at the beginning. 
  let dfA = new DateFormatter()
  dfA.dateFormat = headerDateFormat
  
  // Print the date header: Take formatted date, 
  // make it an uppercased string
  let headerText = widget.addText(dfA.string(date).toUpperCase())
  // ...and now we can STYLIZE it.
  // Check Scriptable documentation ("Font")
  // to find a list of available fonts    
  // Font size can be tweaked at the beginning 
  headerText.font = Font.mediumSystemFont(headerFont)
  headerText.textColor = Color.red()
 
  // Store today's date so we can compare it later
  dfA.dateFormat = "ddMMyyyy"
  let todayIs = dfA.string(date)


  // HEADER STUFF IS NOW DONE  
  // -------------------------
  // NEXT: THE EVENT LIST
  
  
  // We'll need another date formatter, 
  // This will be used in all events
  let dfB = new DateFormatter()
   
  // Store 1st event's date so we can compare it later
  dfB.dateFormat = "ddMMyyyy"
  let firstIs = dfB.string(futureEvents[0].startDate)
  dfB.dateFormat = eventPrefix
   
  // CREATE: 1st EVENT 
  // First we have to check:
  // do we even have any events... 
  
  if (eventsTotal < 1) {
    
    // If there are no events,
    // we add a spacer and call it a day.
    // You could print something nice here :)
    widget.addSpacer()
    
    // But if there are events...
    
   } else {
    
    // ...let's define how the 1st entry looks.  
    // It is a special one and uses 2 lines.
    
    // First some padding. Tweak the value
    // at the beginning (const firstPadding)  
    widget.addSpacer(firstPadding)
    
    // Store 1st event's day and time.    
    // If the first event is today, 
    // we won't use a date prefix.
    let firstDate
    if(todayIs === firstIs){
      firstDate = ''
      } else {  
      // Event day number  
      firstDate = dfB.string(futureEvents[0].startDate) + ' '
      }
    
    // Event time span values
    let firstStarts = formatTime(futureEvents[0].startDate)
    let firstEnds = formatTime(futureEvents[0].endDate)
    
    
    // PRINT 1st line: 1st event's (date and) time
    let firstLine = widget.addText(firstDate + firstStarts + '-' + firstEnds)
    
    // Add some space vetween the lines   
    widget.addSpacer(5)
    
    // PRINT 2nd line: 1st event's title    
    let secondLine = widget.addText(futureEvents[0].title)
    
    // STYLIZE: 1st/2nd line font & color
    // Font size const (firstEvent) 
    // was defined at the beginning   
    firstLine.font = Font.lightSystemFont(firstEvent)
    firstLine.textColor = Color.white()
    secondLine.font = Font.mediumSystemFont(firstEvent)
    secondLine.textColor = Color.red()
    
    // If there's more than 3 events, limit the title
    // lines to 1, else limit to 2 
    if(eventsTotal > 3){ secondLine.lineLimit = 1} else {secondLine.lineLimit = 2}
    
    // Add same amount of padding as we 
    // did before the first entry   
    widget.addSpacer(firstPadding)
    
    
    // FIRST EVENT IS NOW DONE
    // -----------------------   
    // NEXT: EVENTS 2-7 etc.
    
    // Again: We have to check if we have more events.         
    // If there was only 1...
    if (eventsTotal < 2) {
          
      // ...if so, we a spacer...                 
      widget.addSpacer()
      
      // ...otherwise we go on!   
    } else {
    
      // Now we check how many events we're 
      // in total (we already printed one)
      // Setup a variable        
      var eventsToList
      // We defined the max const at the beginning.
      // Now compare that to the event array.        
      // If we have less than max, value is that
      // If we have same as max or more, value is max        
      if (eventsTotal < maxEvents){eventsToList = eventsTotal} else {eventsToList = maxEvents}
      
      
      // PRINT the remaining events    
      var i
      // We have already peinted 1 event
      // so array index i is set as 1
      for(i = 1; i < eventsToList; i++){
         
        // Store day number and title         
        let eventDateprefix = dfB.string(futureEvents[i].startDate)
        let eventTitle = futureEvents[i].title
        
        // Print day + title 
        let listEvent = widget.addText(eventDateprefix + eventTitle)
        
        // STYLIZE: event line font & color
        // Font size const (listEvent) 
        // was set at the beginning   
        listEvent.font = Font.regularSystemFont(listEvents)
        listEvent.textColor = Color.white() 
        
        // Limit lines to 1 per event
        listEvent.lineLimit = 1
        
        // Add spacer, const was set at the beginning 
        widget.addSpacer(eventSpacer)
          
        
      // For-loop ends here!
      }
         
    
    // The "else" after
    // if-we-have-more-than-1-event 
    // ends here:   
    }
    // ---------------------------------

    
  // The "else" after
  // if-we-have-more-than-0-events 
  // ends here:   
  }
  // ---------------------------------

    
  // A spacer to even things out
  widget.addSpacer()
  
  

// ------------------------  
// Finalize widget settings
  widget.setPadding(15,15,15,0)
  widget.spacing = -3
  
  Script.setWidget(widget)
  widget.presentSmall()
  Script.complete() 
}

// BOOM. DONE.
// ---------------------------
// FUNCTIONS ARE DEFINED BELOW


// ----------------------------------
// Function: formatTime
// TIME FORMATTER
// Is used to format the event times
function formatTime(date) {
    let df = new DateFormatter()
    df.useNoDateStyle()
    df.useShortTimeStyle()
    return df.string(date)
}
// ----------------------------------


2 Likes

Similar to my next game widget (using an API): next concert widget (using my calendar)

(Don’t get jealous fellow U2 fans; fake data…)

3 Likes

And continuing the use of my calendar, but adding the new stacks: displaying the magazines that appear in the next week.

1 Like

@simonbs could you consider closing or deconsolidating this thread? It’s getting hard to follow and track down posts.

I’m trying to find the example you mentioned elsewhere re: stacks with no success.

9 posts were split to a new topic: Invisible Widget Generator

Updated version of my next match widget, with team logos, now that we have stacks:

3 Likes

AQI (Air Quality Index) widgets using data from fire.airnow.gov and PurpleAir.

An earlier version during the worst of the fires here, and why I created this in the first place:

Hello, i made here a widget that displays the currently playing song on spotify using the Last.fm API.


Just create a Last.fm account, connect it to Spotify, create a developer account, and get an API key.
You’ll have to paste you user name and api key in the code.
I couldnt make the widget update more frecuently, if someone can just let me know.
//Replace "USER" with you last.fm user and "APIKEY" with the api key for your user.

let url = "http://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=USER&api_key=APIKEY&format=json&limit=1" 
let req = new Request(url)
async function createWidget(nowPlaying) {
 
  let widget = new ListWidget()
    // load image
  const coverArt = await loadImage(nowPlaying.recenttracks.track[0].image[3]["#text"])
     widget.backgroundImage = coverArt
     
  widget.addSpacer()
      // set gradient background
  let startColor = new Color("#1c1c1c19")
  let endColor = new Color("#1c1c1cb4")
  let gradient = new LinearGradient()
  gradient.colors = [startColor, endColor]
  gradient.locations = [0.0, 1]
  widget.backgroundGradient = gradient
    widget.backgroundColor = new Color("1c1c1c")
  // add title and artist
    let title = nowPlaying.recenttracks.track[0].name.toLowerCase()
    // capitalize every first character 
    title = title.replace(/\b\w/g, function(c) {
      return c.toUpperCase();
    });
  let titleTxt = widget.addText(title)
  titleTxt.font = Font.boldSystemFont(12)
  titleTxt.textColor = Color.white()
  titleTxt.leftAlignText()
  widget.addSpacer(2)
  
    let artist = nowPlaying.recenttracks.track[0].artist["#text"]
    // capitalize every first character 
    artist = artist.replace(/\b\w/g, function(c) {
      return c.toUpperCase();
    });
  let artistTxt = widget.addText(artist)
  artistTxt.font = Font.systemFont(10)
  artistTxt.textColor = Color.yellow()
  artistTxt.textOpacity = 1
  artistTxt.leftAlignText()
  
  widget.setPadding(8, 15, 10, 5)
  widget.url = nowPlaying.recenttracks.track[0].url
  return widget
}
  
// helper function to load and parse a restful json api
async function loadNowPlaying(coverArt) {
  const req = new Request(url)
  const json = await req.loadJSON()
  
  return json
}
// helper function to download an image from a given url
async function loadImage(imgUrl) {
  const url = imgUrl !== null ? imgUrl : placeholder;
  const req = new Request(url)
  const image = await req.loadImage()
  
  return image
}
const nowPlaying = await loadNowPlaying()
const widget = await createWidget(nowPlaying)
Script.setWidget(widget)
Script.complete()
widget.presentSmall()

5 Likes

Can share your code?

To test the stacks feature I built a Spotify Now Playing widget. It uses the more complex Spotify Authorization Code Flow which is needed to access a user‘s playback information. The widget automatically refreshes expired access tokens. I also built a Siri Shortcut to simplify the initial setup.

Gist and setup instructions:

Caveat:
Even though it’s been a lot of work this widget is more a proof of concept. It will lag behind since it‘s not possible to force an update of widgets and only iOS alone will decide when to refresh.

6 Likes

The Unsplash calendar widget don’t work any more with the pictures.

I think something is wrong with the Unsplash API. If I change the query from “nature” to “nature,water”, it works now.

2 posts were split to a new topic: Calendar & Reminders

Hi, I’m really new to this sorry!
Could anyone write a script for something like this?

I think there was probably some problems hours ago with the API. It’s normal now for me.

If you use the script I posted earlier, there’s a spot where you can add your own code. I used @riverwolf’s greeting code for this:

// Get the current date
const date = new Date()

// Format the greeting (thank you riverwolf)
let greeting = "Good "
if (date.getHours() < 6) {
	greeting = greeting + "night."
} else if (date.getHours() < 12) {
	greeting = greeting + "morning."
} else if (date.getHours() < 17) {
	greeting = greeting + "afternoon."
} else if (date.getHours() < 21) {
	greeting = greeting + "evening."
} else {
	greeting = greeting + "night."
}

// Format the date
let df = new DateFormatter()
df.dateFormat = "EEEE, MMMM d"

// Format the widget
widget.addSpacer(40)

let greetingText = widget.addText(greeting)
greetingText.font = Font.boldSystemFont(36)
greetingText.textColor = Color.white()

let dateText = widget.addText(df.string(date))
dateText.font = Font.regularSystemFont(18)
dateText.textColor = Color.white()

widget.addSpacer()

I think it looks pretty similar:

4 Likes

A post was merged into an existing topic: Radio Paradise Now Playing

Hi, I realise I have been a little quiet on here and missed some replies / questions.

I am working on a couple of ideas, in the meantime I have updated my reddit widget. The standard uses the old method, the new uses stacks and has links to open the post too.

Here is a screenshot of the two side by side:

Code can be found here:

RedditViewer:

RedditViewerStacks:

The parameter is as follows:
apollo@scriptable@1@pic@4
The client to open @ the subreddit @ placeholder - this was for debugging @ whether to show pics (this is to be coded) @ How many entries - this will depend on font sizes ‘ verbosity of posts.

1 Like