Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save goldfarbalex/2fbb5970aebf4fabd6c412707faa86e1 to your computer and use it in GitHub Desktop.
Save goldfarbalex/2fbb5970aebf4fabd6c412707faa86e1 to your computer and use it in GitHub Desktop.
Date & Agenda & Weather Scriptable widget (german localization)
// this Scriptable Widget is coded by Slowlydev (aka r/Sl0wly-edits, r/Slowlydev) and adapted by @marco79
const DEV_MODE = false //for developer only
const DEV_PREVIEW = "medium" //for developer only (this script is specialy made for a medium sized widget)
const API_KEY = "" // enter your openweathermap.com api key
const FORECAST_HOURS = "3"
const UNITS = "metric" //metric for celsius and imperial for Fahrenheit
const CALENDAR_URL = "calshow://" //Apple Calendar App, if your favorite app does have a URL scheme feel free to change it
const WEATHER_URL = "" //there is no URL for the Apple Weather App, if your favorite app does feel free to add it
const widgetBackground = new Color("#D6D6D6") //Widget Background
const stackBackground = new Color("#FFFFFF") //Smaller Container Background
const calendarColor = new Color("#EA3323") //Calendar Color
const stackSize = new Size(0, 65) //0 means its automatic
if (config.runsInWidget || DEV_MODE) {
const date = new Date()
const dateNow = Date.now()
let df_Name = new DateFormatter()
let df_Month = new DateFormatter()
df_Name.dateFormat = "EEEE"
df_Month.dateFormat = "MMMM"
const dayName = df_Name.string(date)
const dayNumber = date.getDate().toString()
const monthName = df_Month.string(date)
// Option 1: uncomment this to use let the script locate you each time (which takes longer and needs more battery)
// let loc = await Location.current()
// let lat = loc["latitude"]
// let lon = loc["longitude"]
// Option 2: hard coded longitude/latitude
let lat = "50.95330938278102"
let lon = "6.915087611808545"
const weatherURL = `https://api.openweathermap.org/data/2.5/onecall?lat=${lat}&lon=${lon}&exclude=current,minutely,daily,alerts&units=${UNITS}&appid=${API_KEY}`
const weatherRequest = new Request(weatherURL)
const weaterData = await weatherRequest.loadJSON()
const hourlyForecasts = weaterData.hourly
let nextForecasts = []
for (const hourlyForecast of hourlyForecasts) {
if (nextForecasts.length == FORECAST_HOURS) { break }
let dt = removeDigits(dateNow, 3)
if (hourlyForecast.dt > dt) {
nextForecasts.push(hourlyForecast)
}
}
const events = await CalendarEvent.today([])
let futureEvents = []
for (const event of events) {
if (futureEvents.length == 2) { break }
if (event.startDate.getTime() > date.getTime() && !event.isAllDay) {
futureEvents.push(event)
}
}
let widget = new ListWidget()
widget.backgroundColor = widgetBackground
widget.setPadding(10, 10, 10, 10)
//Top Row (Date & Weather)
let topRow = widget.addStack()
topRow.layoutHorizontally()
//Top Row Date
let dateStack = topRow.addStack()
dateStack.layoutHorizontally()
dateStack.centerAlignContent()
dateStack.setPadding(7, 7, 7, 7)
dateStack.backgroundColor = stackBackground
dateStack.cornerRadius = 12
dateStack.size = stackSize
dateStack.addSpacer()
let dayNumberTxt = dateStack.addText(dayNumber + ".")
dayNumberTxt.font = Font.semiboldSystemFont(32)
dayNumberTxt.textColor = Color.black()
dateStack.addSpacer(7)
let dateTextStack = dateStack.addStack()
dateTextStack.layoutVertically()
let monthNameTxt = dateTextStack.addText(monthName.toUpperCase())
monthNameTxt.font = Font.boldSystemFont(10)
monthNameTxt.textColor = Color.black()
let dayNameTxt = dateTextStack.addText(dayName)
dayNameTxt.font = Font.boldSystemFont(12)
dayNameTxt.textColor = calendarColor
dateStack.addSpacer()
topRow.addSpacer()
widget.addSpacer()
//Top Row Weather
let weatherStack = topRow.addStack()
weatherStack.layoutHorizontally()
weatherStack.centerAlignContent()
weatherStack.setPadding(7, 7, 7, 7)
weatherStack.backgroundColor = stackBackground
weatherStack.cornerRadius = 12
weatherStack.size = stackSize
weatherStack.url = WEATHER_URL
for (const nextForecast of nextForecasts) {
const iconURL = "https://openweathermap.org/img/wn/" + nextForecast.weather[0].icon + "@2x.png"
let iconRequest = new Request(iconURL);
let icon = await iconRequest.loadImage();
weatherStack.addSpacer()
//Hour Forecast Stack
let hourStack = weatherStack.addStack()
hourStack.layoutVertically()
let hourTxt = hourStack.addText(formatAMPM(nextForecast.dt))
hourTxt.centerAlignText()
hourTxt.font = Font.systemFont(10)
hourTxt.textColor = Color.black()
hourTxt.textOpacity = 0.5
let weatherIcon = hourStack.addImage(icon)
weatherIcon.centerAlignImage()
weatherIcon.size = new Size(25, 25)
let tempTxt = hourStack.addText(" " + Math.round(nextForecast.temp) + "°")
tempTxt.centerAlignText()
tempTxt.font = Font.systemFont(10)
tempTxt.textColor = Color.black()
}
weatherStack.addSpacer()
//Bottom Row Events
let eventStack = widget.addStack()
eventStack.layoutHorizontally()
eventStack.centerAlignContent()
eventStack.setPadding(7, 7, 7, 7)
eventStack.backgroundColor = stackBackground
eventStack.cornerRadius = 12
eventStack.size = stackSize
let eventInfoStack
const font = Font.lightSystemFont(20)
let calendarSymbol = SFSymbol.named("calendar")
calendarSymbol.applyFont(font)
eventStack.addSpacer(8)
let eventIcon = eventStack.addImage(calendarSymbol.image)
eventIcon.imageSize = new Size(20, 20)
eventIcon.resizable = false
eventIcon.centerAlignImage()
eventStack.addSpacer(14)
eventStack.url = CALENDAR_URL
let eventItemsStack = eventStack.addStack()
eventItemsStack.layoutVertically()
if (futureEvents.length != 0) {
for (let i = 0; i < futureEvents.length; i++) {
let futureEvent = futureEvents[i]
const time = formatTime(futureEvent.startDate) + " - " + formatTime(futureEvent.endDate)
const eventColor = new Color("#" + futureEvent.calendar.color.hex)
eventInfoStack = eventItemsStack.addStack()
eventInfoStack.layoutVertically()
let eventTitle = eventItemsStack.addText(futureEvent.title)
eventTitle.font = Font.semiboldSystemFont(12)
eventTitle.textColor = eventColor
eventTitle.lineLimit = 1
let eventTime = eventItemsStack.addText(time + " Uhr")
eventTime.font = Font.semiboldMonospacedSystemFont(10)
eventTime.textColor = Color.black()
eventTime.textOpacity = 0.5
if (i == 0) {
eventItemsStack.addSpacer(3)
}
}
} else {
let nothingText = eventStack.addText("Heute hast du keine Termine!")
nothingText.font = Font.semiboldMonospacedSystemFont(12)
nothingText.textColor = Color.black()
nothingText.textOpacity = 0.5
}
eventStack.addSpacer()
Script.setWidget(widget)
if (DEV_MODE) {
if (DEV_PREVIEW == "small") { widget.presentSmall() }
if (DEV_PREVIEW == "medium") { widget.presentMedium() }
if (DEV_PREVIEW == "large") { widget.presentLarge() }
}
Script.complete()
}
function removeDigits(x, n) { return (x - (x % Math.pow(10, n))) / Math.pow(10, n) }
function formatAMPM(UNIX_timestamp) {
var date = new Date(UNIX_timestamp * 1000)
var hours = date.getHours()
// Option 1: uncomment this for am/pm time with hours from 0-12
// var ampm = hours >= 12 ? 'PM' : 'AM'
// hours = hours % 12
// hours = hours ? hours : 12
// var strTime = hours.toString() + ampm
// Option 2: german localisation
var strTime = hours.toString() + ":00"
return strTime
}
function formatTime(date) {
let df = new DateFormatter()
df.useNoDateStyle()
df.useShortTimeStyle()
return df.string(date)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment