Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save ParoTheParrot/0ecb58ecfd87544ba58f44b0b5398d37 to your computer and use it in GitHub Desktop.
Save ParoTheParrot/0ecb58ecfd87544ba58f44b0b5398d37 to your computer and use it in GitHub Desktop.
Scriptable iOS widget that shows the amount of both toilet paper and wheat flour which is available at your next dm drugstore #scriptable #corona
// Wheat flour icon made by Freepik from www.flaticon.com and modified by achisto
// Toilet paper icon made by boettges
let country = 'de' // replace with 'at' for shops in Austria
let storeId = 251
let param = args.widgetParameter
if (param != null && param.length > 0) {
storeId = param
}
const widget = new ListWidget()
const storeInfo = await fetchStoreInformation()
const storeCapacityPaper = await fetchAmountOfPaper()
const storeCapacityFlour = await fetchAmountOfWheatFlour()
await createWidget()
// used for debugging if script runs inside the app
if (!config.runsInWidget) {
await widget.presentSmall()
}
Script.setWidget(widget)
Script.complete()
// build the content of the widget
async function createWidget() {
const logoImg = await getImage('dm-logo.png')
widget.setPadding(6,6,6,6)
const titleFontSize = 12
const detailFontSize = 36
const logoStack = widget.addStack()
const shopStateStack = logoStack.addStack()
shopStateStack.layoutVertically()
shopStateStack.addSpacer(10)
let currentTime = new Date().toLocaleTimeString('de-DE', { hour: "numeric", minute: "numeric" })
let currentDay = new Date().getDay()
let isOpen
if (currentDay > 0) {
const todaysOpeningHour = storeInfo.openingHours[currentDay-1].timeRanges[0].opening
const todaysClosingHour = storeInfo.openingHours[currentDay-1].timeRanges[0].closing
const range = [todaysOpeningHour, todaysClosingHour];
isOpen = isInRange(currentTime, range)
} else {
isOpen = false
}
let shopStateText
if (isOpen) {
shopStateText = shopStateStack.addText('GEÖFFNET')
shopStateText.textColor = new Color("#00CD66")
logoStack.addSpacer(36)
} else {
shopStateText = shopStateStack.addText('GESCHLOSSEN')
shopStateText.textColor = new Color("#E50000")
logoStack.addSpacer(10)
}
shopStateText.font = Font.boldRoundedSystemFont(12)
const logoImageStack = logoStack.addStack()
logoStack.layoutHorizontally()
logoImageStack.backgroundColor = new Color("#ffffff", 1.0)
logoImageStack.cornerRadius = 8
const wimg = logoImageStack.addImage(logoImg)
wimg.imageSize = new Size(32,32)
wimg.rightAlignImage()
widget.addSpacer(6)
// toilet paper
const toiletPaperIcon = await getImage('toilet-paper.png')
let row = widget.addStack()
row.layoutHorizontally()
row.addSpacer(2)
const toiletPaperImg = row.addImage(toiletPaperIcon)
toiletPaperImg.imageSize = new Size(30,30)
row.addSpacer(13)
let column = row.addStack()
column.layoutVertically()
const paperText = column.addText("KLOPAPIER")
paperText.font = Font.mediumRoundedSystemFont(11)
const packageCount = column.addText(storeCapacityPaper.toString())
packageCount.font = Font.mediumRoundedSystemFont(18)
if (storeCapacityPaper < 30) {
packageCount.textColor = new Color("#E50000")
} else {
packageCount.textColor = new Color("#00CD66")
}
widget.addSpacer(4)
// wheat flour
const flourIcon = await getImage('wheat-flour.png')
let row2= widget.addStack()
row2.layoutHorizontally()
row2.addSpacer(3)
const flourImg = row2.addImage(flourIcon)
flourImg.imageSize = new Size(30,30)
row2.addSpacer(12)
let column2 = row2.addStack()
column2.layoutVertically()
const flourText = column2.addText("MEHL")
flourText.font = Font.mediumRoundedSystemFont(11)
const flourPackageCount = column2.addText(storeCapacityFlour.toString())
flourPackageCount.font = Font.mediumRoundedSystemFont(18)
if (storeCapacityFlour < 30) {
flourPackageCount.textColor = new Color("#E50000")
} else {
flourPackageCount.textColor = new Color("#00CD66")
}
widget.addSpacer(4)
// shop info
const row3 = widget.addStack()
row3.layoutVertically()
const street = row3.addText(storeInfo.address.street)
street.font = Font.regularSystemFont(10)
const zipCity = row3.addText(storeInfo.address.zip + " " + storeInfo.address.city)
zipCity.font = Font.regularSystemFont(10)
}
// fetches the amount of toilet paper packages
async function fetchAmountOfPaper() {
let url
let counter = 0
if (country.toLowerCase() === 'at') {
// Austria
const array = ["156754", "180487", "194066", "188494", "194144", "273259", "170237", "232201", "170425", "283216", "205873", "205874", "249881", "184204"]
for (var i = 0; i < array.length; i++) {
let currentItem = array[i]
url = 'https://products.dm.de/store-availability/AT/products/dans/' + currentItem + '/stocklevel?storeNumbers=' + storeId
let req = new Request(url)
let apiResult = await req.loadJSON()
if (req.response.statusCode == 200) {
counter += apiResult.storeAvailability[0].stockLevel
}
}
} else {
// Germany
url = 'https://products.dm.de/store-availability/DE/availability?dans=595420,708997,137425,28171,485698,799358,863567,452740,610544,846857,709006,452753,879536,452744,485695,853483,594080,504606,593761,525943,842480,535981,127048,524535&storeNumbers=' + storeId
const req = new Request(url)
const apiResult = await req.loadJSON()
for (var i in apiResult.storeAvailabilities) {
counter += apiResult.storeAvailabilities[i][0].stockLevel
}
}
return counter
}
// fetches the amount of wheat flour packages
async function fetchAmountOfWheatFlour() {
let url
let counter = 0
if (country.toLowerCase() === 'at') {
// Austria
const array = ["156754", "180487", "194066", "188494", "194144", "273259", "170237", "232201", "170425", "283216", "205873", "205874", "249881", "184204"]
for (var i = 0; i < array.length; i++) {
let currentItem = array[i]
url = 'https://products.dm.de/store-availability/AT/products/dans/' + currentItem + '/stocklevel?storeNumbers=' + storeId
let req = new Request(url)
let apiResult = await req.loadJSON()
if (req.response.statusCode == 200) {
counter += apiResult.storeAvailability[0].stockLevel
}
}
} else {
// Germany
url = 'https://products.dm.de/store-availability/DE/availability?dans=488334,468120,706590,531500,468121,459912,468178&storeNumbers=' + storeId
const req = new Request(url)
const apiResult = await req.loadJSON()
for (var i in apiResult.storeAvailabilities) {
counter += apiResult.storeAvailabilities[i][0].stockLevel
}
}
return counter
}
// fetches information of the configured store, e.g. opening hours, address etc.
async function fetchStoreInformation() {
let url
if (country.toLowerCase() === 'at') {
url = 'https://store-data-service.services.dmtech.com/stores/item/at/' + storeId
widget.url = 'https://www.dm.at/search?query=toilettenpapier&searchType=product'
} else {
url = 'https://store-data-service.services.dmtech.com/stores/item/de/' + storeId
widget.url = 'https://www.dm.de/search?query=toilettenpapier&searchType=product'
}
let req = new Request(url)
let apiResult = await req.loadJSON()
return apiResult
}
// checks whether the store is currently open or closed
function isInRange(value, range) {
return value >= range[0] && value <= range[1];
}
// get images from local filestore or download them once
async function getImage(image) {
let fm = FileManager.local()
let dir = fm.documentsDirectory()
let path = fm.joinPath(dir, image)
if (fm.fileExists(path)) {
return fm.readImage(path)
} else {
// download once
let imageUrl
switch (image) {
case 'dm-logo.png':
imageUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/5/50/Dm_Logo.svg/300px-Dm_Logo.svg.png"
break
case 'toilet-paper.png':
imageUrl = "https://i.imgur.com/Uv1qZGV.png"
break
case 'wheat-flour.png':
// imageUrl = "https://i.imgur.com/6f5kubZ.png"
imageUrl = "https://i.imgur.com/gwWtMWn.png"
// imageUrl = "https://i.imgur.com/NSK7Bz3.png"
// imageUrl = "https://cdn.icon-icons.com/icons2/1398/PNG/128/flour_96838.png"
break
default:
console.log(`Sorry, couldn't find ${image}.`);
}
let iconImage = await loadImage(imageUrl)
fm.writeImage(path, iconImage)
return iconImage
}
}
// helper function to download an image from a given url
async function loadImage(imgUrl) {
const req = new Request(imgUrl)
return await req.loadImage()
}
// End of script
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment