Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
A Scriptable widget that shows the amount of people who have received the corona vaccination in Germany
// Version 1.3.0
// 27.11.2021
//
// Variables used by Scriptable.
// These must be at the very top of the file. Do not edit.
// icon-color: red; icon-glyph: notes-medical;
// Mit Caching und Fallback
const cacheMinutes = 60; // 60 min
const today = new Date();
const neededTotalVaccinations = 83200000;
let result;
let resultDe;
let width = 100;
const h = 5;
const colorLightGreen = new Color("#47b881")
const colorDarkGreen = new Color("#00783e")
let widget = new ListWidget();
widget.setPadding(6, 12, 8, 12);
widget.url =
"https://interaktiv.morgenpost.de/corona-impfungen-deutschland-bundeslaender-weltweit/";
await getNumbers();
await createWidget();
Script.setWidget(widget);
Script.complete();
if (config.runsInApp) {
widget.presentSmall();
}
async function createWidget() {
widget.addSpacer();
const amountFirstPerCent = resultDe.vaccinations1_quota;
const amountSecondPerCent = resultDe.vaccinations2_quota;
const amountBoosterPerCent = resultDe.vaccinations3_quota;
let firstDoseStack = widget.addStack()
firstDoseStack.layoutHorizontally()
let amountFirstText = firstDoseStack.addText("Erstimpfung");
amountFirstText.font = Font.boldSystemFont(12);
firstDoseStack.addSpacer()
let percentFirstText = firstDoseStack.addText(amountFirstPerCent.toLocaleString() + "%")
percentFirstText.font = Font.boldSystemFont(12);
widget.addSpacer(2);
let progressStack = widget.addStack();
progressStack.layoutVertically();
let progressNumberStack = widget.addStack();
progressNumberStack.layoutHorizontally();
const progressText = progressNumberStack.addText((resultDe.vaccinations1).toLocaleString());
progressText.font = Font.mediumSystemFont(10);
progressStack.addImage(createProgress((resultDe.vaccinations1), colorLightGreen));
widget.addSpacer(3)
let secondDoseStack = widget.addStack()
secondDoseStack.layoutHorizontally()
let amountSecondText = secondDoseStack.addText("Vollständig");
amountSecondText.font = Font.boldSystemFont(12);
secondDoseStack.addSpacer()
let percentSecondText = secondDoseStack.addText(amountSecondPerCent.toLocaleString() + "%")
percentSecondText.font = Font.boldSystemFont(12);
widget.addSpacer(2)
let progressStack2 = widget.addStack();
progressStack2.layoutVertically();
let progressNumberStack2 = widget.addStack();
progressNumberStack2.layoutHorizontally();
const progressText2 = progressNumberStack2.addText(resultDe.vaccinations2.toLocaleString());
progressText2.font = Font.mediumSystemFont(10);
progressStack2.addImage(createProgress(resultDe.vaccinations2, colorLightGreen));
widget.addSpacer(3);
let boosterStack = widget.addStack()
boosterStack.layoutHorizontally()
let amountBoosterText = boosterStack.addText("Booster");
amountBoosterText.font = Font.boldSystemFont(12);
boosterStack.addSpacer()
let percentBoosterText = boosterStack.addText(amountBoosterPerCent.toLocaleString() + "%")
percentBoosterText.font = Font.boldSystemFont(12);
widget.addSpacer(2)
let progressStack3 = widget.addStack();
progressStack3.layoutVertically();
let progressNumberStack3 = widget.addStack();
progressNumberStack3.layoutHorizontally();
const progressText3 = progressNumberStack3.addText(resultDe.vaccinations3.toLocaleString());
progressText3.font = Font.mediumSystemFont(10);
progressStack3.addImage(createProgress(resultDe.vaccinations3, colorLightGreen));
widget.addSpacer(6)
let calendarStack = widget.addStack();
const lastUpdateDate = new Date(resultDe.date);
let description = widget.addText("Stand: " + lastUpdateDate.toLocaleDateString());
description.centerAlignText();
description.font = Font.mediumSystemFont(9);
// widget.addSpacer(8)
}
// get images from local storage 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 "vac-logo.png":
imageUrl = "https://i.imgur.com/ZsBNT8E.png";
break;
case "calendar-icon.png":
imageUrl = "https://i.imgur.com/Qp8CEFf.png";
break;
default:
console.log(`Sorry, couldn't find ${image}.`);
}
let req = new Request(imageUrl);
let loadedImage = await req.loadImage();
fm.writeImage(path, loadedImage);
return loadedImage;
}
}
async function getNumbers() {
// Set up the file manager.
const files = FileManager.local();
// Set up cache
const cachePath = files.joinPath(
files.cacheDirectory(),
"api-cache-covid-vaccination-numbers-mopo"
);
const cacheExists = files.fileExists(cachePath);
const cacheDate = cacheExists ? files.modificationDate(cachePath) : 0;
// Get Data
try {
// If cache exists and it's been less than 60 minutes since last request, use cached data.
if (
cacheExists &&
today.getTime() - cacheDate.getTime() < cacheMinutes * 60 * 1000
) {
console.log("Get from Cache");
result = JSON.parse(files.readString(cachePath));
} else {
console.log("Get from API");
const req2 = new Request(
"https://interaktiv.morgenpost.de/data/corona/rki-vaccination-latest.json"
);
result = await req2.loadJSON();
console.log("Write Data to Cache");
try {
files.writeString(cachePath, JSON.stringify(result));
} catch (e) {
console.log("Creating Cache failed!");
console.log(e);
}
}
} catch (e) {
console.error(e);
if (cacheExists) {
console.log("Get from Cache");
result = JSON.parse(files.readString(cachePath));
} else {
console.log("No fallback to cache possible. Due to missing cache.");
}
}
await setTotalVacNoForGermany(result);
}
async function setTotalVacNoForGermany(result) {
for (var i = result.length - 1; i >= 0; i--) {
let currentItem = result[i];
if (currentItem["id"] === "de") {
resultDe = currentItem;
}
}
}
function createProgress(vacNo, color) {
const context = new DrawContext();
context.size = new Size(width, h);
context.opaque = false;
context.respectScreenScale = true;
context.setFillColor(new Color("#d2d2d7"));
const path = new Path();
path.addRoundedRect(new Rect(0, 0, width, h), 3, 2);
context.addPath(path);
context.fillPath();
context.setFillColor(color);
const path1 = new Path();
const path1width =
(width * vacNo) / neededTotalVaccinations > width
? width
: (width * vacNo) / neededTotalVaccinations;
path1.addRoundedRect(new Rect(0, 0, path1width, h), 3, 2);
context.addPath(path1);
context.fillPath();
return context.getImage();
}
//
// Bitte bis zum Ende kopieren
//
@rafaelmaeuer
Copy link

rafaelmaeuer commented May 26, 2022

Na klar

@Chaos53925
Copy link

Chaos53925 commented May 30, 2022

Vorschlag für Zeile 34:

  // Add background color
if (!Device.isUsingDarkAppearance()) {
  widget.backgroundColor = new Color("ffffff")
} else {
  widget.backgroundColor = new Color("1c1c1c")
}

Bringt auch den Darkmode rein und prüft auch ob der Darkmode aktiv ist oder nicht

@rafaelmaeuer
Copy link

rafaelmaeuer commented May 30, 2022

👍🏻 Werd ich mal testen und ggfs. übernehmen

@Chaos53925
Copy link

Chaos53925 commented May 30, 2022

Habe mich noch ein bisschen mehr reingehangen, da mir die fehlende Automatische Reaktion auf den Wechsel zwischen Dark und Light nicht in ruhe lies:

    // Add background color
widget.backgroundColor = new Color("ffffff");
widget.backgroundColor = Color.dynamic(widget.backgroundColor, new Color("1c1c1c"));

Du kannst mit diesem Code sogar eine Einstellung machen, die den User mittels eines Parameters sogar die Abschaltung des Wechsels zwischen Dark und Light ermöglicht.

@rafaelmaeuer
Copy link

rafaelmaeuer commented May 31, 2022

Variante 1 funktionierte leider nicht unter macOS, Variante 2 dafür schon - gefällt mir gut: habe ich in v1.4.3 übernommen

@thorstenleidl
Copy link

thorstenleidl commented Jun 16, 2022

Danke @rafaelmaeuer, habe dank deinem tsv parsing meinen fork wiederbeleben können 👍🏻

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