Skip to content

Instantly share code, notes, and snippets.

@mzeryck
Last active August 29, 2021 14:40
Show Gist options
  • Select an option

  • Save mzeryck/4f9255224fe707ee74d86dc6465feea2 to your computer and use it in GitHub Desktop.

Select an option

Save mzeryck/4f9255224fe707ee74d86dc6465feea2 to your computer and use it in GitHub Desktop.
A Scriptable widget that shows upcoming calendar events to mimic the built-in Calendar widget, with some modifications.
// 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 was created by Max Zeryck @mzeryck
// Note: before using this script in a widget, change const TEST_MODE to true and run it in the Scriptable app.
// The app will prompt you to give calendar access, and then show a preview of the widget.
// Make sure to change it back to const TEST_MODE = false prior to adding it to a widget. Happy coding!
const IMAGE_SOURCE = "Unsplash"
const TERMS = "nature,water"
const FORCE_IMAGE_UPDATE = false
const TEST_MODE = false
// Store current datetime
const date = new Date()
// If we're running the script normally, go to the Calendar.
if (!config.runsInWidget && !TEST_MODE) {
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 {
let widget = new ListWidget()
// Format the date info
let df = new DateFormatter()
df.dateFormat = "EEEE"
let dayOfWeek = widget.addText(df.string(date).toUpperCase())
let dateNumber = widget.addText(date.getDate().toString())
dayOfWeek.font = Font.semiboldSystemFont(13)
dateNumber.font = Font.lightSystemFont(34)
// Find future events that aren't all day and aren't canceled
const events = await CalendarEvent.today([])
let futureEvents = []
for (const event of events) {
if (event.startDate.getTime() > date.getTime() && !event.isAllDay && !event.title.startsWith("Canceled:")) {
futureEvents.push(event)
}
}
// If there is at least one future event today
if (futureEvents.length != 0) {
dayOfWeek.textColor = Color.red()
widget.addSpacer()
let titleOne = widget.addText(futureEvents[0].title)
titleOne.font = Font.mediumSystemFont(14)
widget.addSpacer(7)
let timeOne = widget.addText(formatTime(futureEvents[0].startDate))
timeOne.font = Font.regularSystemFont(14)
timeOne.textColor = Color.gray()
// If we have multiple future events, show the following one
if (futureEvents.length > 1) {
// We only have room for single-line event names
titleOne.lineLimit = 1
widget.addSpacer(12)
let titleTwo = widget.addText(futureEvents[1].title)
titleTwo.font = Font.mediumSystemFont(14)
titleTwo.lineLimit = 1
widget.addSpacer(7)
let timeTwo = widget.addText(formatTime(futureEvents[1].startDate))
timeTwo.font = Font.regularSystemFont(14)
timeTwo.textColor = Color.gray()
}
// If there are no future events today
} else {
dayOfWeek.textColor = Color.white()
dateNumber.textColor = Color.white()
let files = FileManager.local()
const path = files.joinPath(files.documentsDirectory(), "mz_calendar_widget.jpg")
const modificationDate = files.modificationDate(path)
// Download image if it doesn't exist, wasn't created today, or update is forced
if (!modificationDate || !sameDay(modificationDate,date) || FORCE_IMAGE_UPDATE) {
try {
let img = await provideImage(IMAGE_SOURCE,TERMS)
files.writeImage(path,img)
widget.backgroundImage = img
} catch {
widget.backgroundImage = files.readImage(path)
}
} else {
widget.backgroundImage = files.readImage(path)
}
// Add overlay to image
let gradient = new LinearGradient()
gradient.colors = [new Color("#000000",0.5), new Color("#000000",0)]
gradient.locations = [0, 0.5]
widget.backgroundGradient = gradient
widget.addSpacer()
}
// Finalize widget settings
widget.setPadding(16,16,16,0)
widget.spacing = -3
Script.setWidget(widget)
widget.presentSmall()
Script.complete()
}
// Helper function to interpret sources and terms
async function provideImage(source,terms) {
if (source == "Bing") {
const url = "http://www.bing.com/HPImageArchive.aspx?format=js&idx=0&n=1&mkt=en-US"
const req = new Request(url)
const json = await req.loadJSON()
const imgURL = "http://bing.com" + json.images[0].url
const img = await downloadImage(imgURL)
const rect = new Rect(-78,0,356,200)
return cropImage(img, rect)
} else if (source == "Unsplash") {
const img = await downloadImage("https://source.unsplash.com/featured/500x500/?"+terms)
return img
}
}
// Helper function to download images
async function downloadImage(url) {
const req = new Request(url)
return await req.loadImage()
}
// Crop an image into a rect
function cropImage(img,rect) {
let draw = new DrawContext()
draw.respectScreenScale = true
draw.drawImageInRect(img,rect)
return draw.getImage()
}
// Formats the times under each event
function formatTime(date) {
let df = new DateFormatter()
df.useNoDateStyle()
df.useShortTimeStyle()
return df.string(date)
}
// Determines if two dates occur on the same day
function sameDay(d1, d2) {
return d1.getFullYear() === d2.getFullYear() &&
d1.getMonth() === d2.getMonth() &&
d1.getDate() === d2.getDate()
}
@italoboy
Copy link
Copy Markdown

italoboy commented Nov 2, 2020

Possible to make it localized?!
Add locale code.

Copy link
Copy Markdown

ghost commented Nov 8, 2020

@ mzeryck

Hey Max, your widget is really good!!! However, I have problems at "weather calendar widget builder” and always get the following hint:

2020-11-08 17:09:55: Error on line 769:51: TypeError: undefined is not an object (evaluating 'weatherDataRaw.current.temp')

Can you help meines me?

I’m looking forward to a feedback.

@FijianCoconut
Copy link
Copy Markdown

@ mzeryck

Hey Max, your widget is really good!!! However, I have problems at "weather calendar widget builder” and always get the following hint:

2020-11-08 17:09:55: Error on line 769:51: TypeError: undefined is not an object (evaluating 'weatherDataRaw.current.temp')

Can you help meines me?

I’m looking forward to a feedback.

Ditto.

@mzeryck
Copy link
Copy Markdown
Author

mzeryck commented Nov 21, 2020

@Augustus88 @FijianCoconut In order for the widget to work, you need an API key from OpenWeather. You just need to sign up for a free account using this link. Then, you'll get the API key (it looks like a bunch of random letters and numbers). Depending on which version of the script you're using, you'll see apiKey = "" at the top - just paste the key in between the quotation marks, like this: apiKey = "abcdefgh"

@baelfire33
Copy link
Copy Markdown

Hi @mzerck, thanks for this code but do you know if we can change the temperature units and go from ° F to ° C? And how not to put a background to see that the wallpaper ? thank you so much

@mzeryck
Copy link
Copy Markdown
Author

mzeryck commented Nov 25, 2020

@baelfire33 I'm guessing you are using Weather Cal, rather than this calendar script? In Weather Cal, you can change the units to "metric" (instead of imperial) to get ° C. For a transparent background, use Widget Blur to export an image, and then use that image as the background of the widget.

@baelfire33
Copy link
Copy Markdown

thank you so much ! Do you have a solution for switching from light mode to dark mode with the image behind, even without dynamic image change?

@mzeryck
Copy link
Copy Markdown
Author

mzeryck commented Dec 21, 2020

@baelfire33 Sorry I always forget to check comments on my Gists! I don't have a solution yet, but I'm hoping that during my vacation this week I can work on it.

@Luk141888
Copy link
Copy Markdown

Hi, how to show TODAY all day events?

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