-
-
Save andreasRedeker/7fa4f1ea16c4ab66b238bfb9e971e96c to your computer and use it in GitHub Desktop.
// Variables used by Scriptable. | |
// These must be at the very top of the file. Do not edit. | |
// icon-color: deep-blue; icon-glyph: gas-pump; | |
// share-sheet-inputs: plain-text; | |
// Script by Andreas Redeker <[email protected]> | |
// Get your own API key from https://creativecommons.tankerkoenig.de/ | |
const apiKey = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" | |
const radius = 3; | |
const sort = "price"; | |
// set to true to use google maps instead of apple maps | |
const useGoogleMaps = false; | |
let fuelType = "e5"; | |
// set latitude and longitude for a fixed location | |
// example: let latitude = 52.516; let longitude = 13.376; | |
let latitude; | |
let longitude; | |
const fuelTypes = { | |
e5: "Super E5", | |
e10: "Super E10", | |
diesel: "Diesel", | |
}; | |
const primaryColor = Color.blue(); | |
const carIcon = SFSymbol.named("car.fill"); | |
if (args.widgetParameter && paramsValid(args.widgetParameter)) { | |
fuelType = args.widgetParameter; | |
} | |
const reqUrl = (location) => | |
`https://creativecommons.tankerkoenig.de/json/list.php?lat=${location.latitude.toFixed( | |
3 | |
)}&lng=${location.longitude.toFixed( | |
3 | |
)}&rad=${radius}&sort=${sort}&type=${fuelType}&apikey=${apiKey}`; | |
const station = await getPriceData(); | |
let widget = await createWidget(); | |
if (!config.runsInWidget) { | |
await widget.presentSmall(); | |
} | |
Script.setWidget(widget); | |
Script.complete(); | |
async function createWidget(items) { | |
let widget = new ListWidget(); | |
// cache data for at least 5 minutes | |
widget.refreshAfterDate = new Date(Date.now() + 300000); | |
if (station) { | |
let stationName; | |
if (station.brand) { | |
stationName = widget.addText(capitalize(station.brand)); | |
} else { | |
stationName = widget.addText(capitalize(station.name)); | |
} | |
stationName.font = Font.boldSystemFont(16); | |
stationName.textColor = primaryColor; | |
stationName.minimumScaleFactor = 0.5; | |
let street = widget.addText(capitalize(station.street)); | |
street.font = Font.mediumSystemFont(10); | |
street.textColor = Color.gray(); | |
let place = widget.addText(capitalize(station.place)); | |
place.font = Font.mediumSystemFont(10); | |
place.textColor = Color.gray(); | |
widget.url = useGoogleMaps ? getGoogleMapsUrl(station) : getAppleMapsUrl(station); | |
widget.addSpacer(2); | |
const row = widget.addStack(); | |
car = row.addImage(carIcon.image); | |
car.tintColor = Color.gray(); | |
car.imageSize = new Size(12, 12); | |
row.addSpacer(4); | |
const isOpen = station.isOpen ? "geöffnet" : "geschlossen"; | |
let dist = row.addText(station?.dist + " km • " + isOpen); | |
dist.font = Font.mediumSystemFont(10); | |
dist.textColor = Color.gray(); | |
widget.addSpacer(); | |
const gasType = widget.addText(fuelTypes[fuelType]); | |
gasType.font = Font.mediumSystemFont(12); | |
gasType.textColor = Color.gray(); | |
const priceStack = widget.addStack(); | |
let price = priceStack.addText(station.price.toLocaleString().slice(0, -1)); | |
price.font = Font.boldSystemFont(32); | |
price.textColor = primaryColor; | |
let eur = priceStack.addText(" €"); | |
eur.font = Font.boldSystemFont(32); | |
eur.textColor = primaryColor; | |
} else if (apiKey === "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx") { | |
car = widget.addImage(carIcon.image); | |
car.tintColor = primaryColor; | |
car.imageSize = new Size(32, 32); | |
car.centerAlignImage(); | |
widget.addSpacer(8); | |
const missingApiKey = widget.addText("Bitte Tankerkönig API Key einfügen"); | |
missingApiKey.font = Font.mediumSystemFont(12); | |
missingApiKey.textColor = Color.gray(); | |
missingApiKey.centerAlignText(); | |
} else { | |
car = widget.addImage(carIcon.image); | |
car.tintColor = primaryColor; | |
car.imageSize = new Size(32, 32); | |
car.centerAlignImage(); | |
widget.addSpacer(8); | |
const loading = widget.addText("Daten werden geladen"); | |
loading.font = Font.mediumSystemFont(12); | |
loading.textColor = Color.gray(); | |
loading.centerAlignText(); | |
} | |
return widget; | |
} | |
async function getPriceData() { | |
try { | |
const location = await getLocation(); | |
if (location) { | |
const data = await new Request(reqUrl(location)).loadJSON(); | |
if (data.ok) { | |
if (data.stations.length > 0) { | |
let openStations = data.stations.filter( | |
(s) => s.isOpen == true && s.price != null | |
); | |
if (openStations.length > 0) { | |
const station = openStations[0]; | |
return { | |
name: station.name, | |
brand: station.brand, | |
street: station.street + " " + station.houseNumber, | |
place: station.postCode + " " + station.place, | |
price: station.price, | |
dist: station.dist, | |
isOpen: station.isOpen, | |
lat: station.lat, | |
lng: station.lng, | |
}; | |
} else { | |
console.log(`no stations found in radius ${radius} with price`); | |
return null; | |
} | |
} else { | |
console.log(`no stations found in radius ${radius}`); | |
return null; | |
} | |
} else { | |
console.log("data not ok"); | |
return null; | |
} | |
} else { | |
console.log("no location found"); | |
return null; | |
} | |
} catch (e) { | |
console.log(e); | |
return null; | |
} | |
} | |
async function getLocation() { | |
if (latitude && longitude) { | |
return { latitude: latitude, longitude: longitude }; | |
} else { | |
try { | |
Location.setAccuracyToKilometer(); | |
return await Location.current(); | |
} catch (e) { | |
return null; | |
} | |
} | |
} | |
function paramsValid(input) { | |
return input in fuelTypes; | |
} | |
function capitalize(string) { | |
return string | |
.toLowerCase() | |
.replace(/\w\S*/g, (w) => w.replace(/^\w/, (c) => c.toUpperCase())); | |
} | |
function getGoogleMapsUrl(station) { | |
let destination = station.lat + "," + station.lng; | |
let url = `https://www.google.com/maps/dir/?api=1&destination=${destination}&travelmode=car`; | |
return url.toString(); | |
} | |
function getAppleMapsUrl(station) { | |
let destination = station.lat + ',' + station.lng | |
let url = `http://maps.apple.com/?q=${destination}` | |
return url.toString(); | |
} |
Ist es möglich das Widget auch ohne aktiven Standort zu benutzen über eine feste Location mit Geodaten?
Hi, wäre es möglich anstatt Google Maps auch Apple Maps zu nutzen ? Wenn ja wie würde der Code angepasst werden müssen ?
Das Script hat heute ein Update erhalten:
- Koordinaten können im Code für einen fixen Standort gesetzt werden (Zeile 18 & 19)
- Apple Maps ist nun der Standarddienst für die Route zur Tankstelle, im Code kann alternativ Google Maps aktiviert werden (Zeile 13)
- Fehler behoben, der auftrat wenn die von der API bereitgestellten Tankstelle keinen Preis (null) hatte
Vielen Dank für das Update !
Hi. Bei mir bleibt es ewig auf "Daten werden geladen". MUSS ich die Koordinaten angeben? Oder woran könnte es haken? Danke.
Hi, die Koordinaten sind optional, standardmäßig werden die anhand deines Standortes ermittelt.
Es könnte sein, dass in deiner Umgebung keine passende Tankstelle gefunden worden ist, versuche mal in Zeile 9 den Radius zu erhöhen (max. 25 km).
Hi @andreasRedeker,
Tolles Script von dir. Rein optisch hat mich damals dieses Script aus dem Netz angesprochen. Eventuell hast du Lust deins etwas anzupassen da in dem Script welches ich verwende auch der Durchschnittspreis errechnet wird und der Benzinpreis auch besser angezeigt wird. Würde mich freuen. ;-)
let callDataAmount = 40
let fm = FileManager.iCloud();
let p = await args.widgetParameter
let widgetSize = config.widgetFamily
if(p == 'local'){
fm = FileManager.local();
}
let dir = fm.documentsDirectory()
let historyFolderPath = fm.joinPath(dir, "FuelPriceNearestHistory")
let list = fm.listContents(historyFolderPath)
log("length: " + list.length)
let historyFilePath = fm.joinPath(historyFolderPath, "FuelPriceNearest" + (list.length - 1) + ".json")
let path = fm.joinPath(historyFolderPath, "FuelPriceNearest" + (list.length) + ".json")
let ic = true
let apiKey = 'XXXXXX-XXXXX-XXXX-XXXX-XXXXXXXX' // get API-Key from https://creativecommons.tankerkoenig.de/
let fixedLocation = false // set to true if you want a fixed location
let radius = 5 // radius in km, set it higher if the script throws an error, it's possible that there is no gas station near your location
const apiURL = (location, radius, apiKey) => `https://creativecommons.tankerkoenig.de/json/list.php?lat=${location.latitude.toFixed(3)}&lng=${location.longitude.toFixed(3)}&rad=${radius}&sort=dist&type=all&apikey=${apiKey}`
let station = await loadStation(apiKey, radius, fixedLocation)
log(dir)
let widget = await createWidget(station)
Script.setWidget(widget)
Script.complete()
function map(lat, lng, name) {
name = name.replace(" ", "%20")
name = name.replace(" ", "%20")
name = name.replace(" ", "%20")
name = name.replace(" ", "%20")
name = name.replace(" ", "%20")
var mapURL = "https://maps.apple.com/?t=h&z=12&ll=" + lat + "," + lng + "&q=" + name
return mapURL
}
function lineSep(){
//generate line separator
const context =new DrawContext()
let width = 93,h=0.00000000001
context.size=new Size(width, h)
context.opaque=false
context.respectScreenScale=true
const path = new Path()
path.move(new Point(18,h))
path.addLine(new Point(width,h))
context.addPath(path)
context.setStrokeColor(Color.blue())
context.strokePath()
return context.getImage()
}
async function getFromApi(){
let location
if (fixedLocation) {
location = myLocation
} else {
location = await Location.current()
}
const data = await new Request(apiURL(location, radius, apiKey)).loadJSON()
return data
}
function getFromFile(){
if (fm.fileExists(historyFilePath)){
data = JSON.parse(fm.readString(historyFilePath));
return data;
}
}
async function loadStation(apiKey, radius, fixedLocation) {
try{
data = await getFromApi();
}catch {
data = await getFromFile();
ic = false
}
log(ic)
log(data)
return data
}
function formatValue(value) {
let lastDigit = '⁹'
let price = value.toString().slice(0, -1)
return price + lastDigit
}
async function createWidget(data) {
var attr = data.stations[0]
log(attr.e10)
log("dataLength: " + data.stations.length)
data.stations.forEach(cheapest)
function cheapest(item, index) {
if ((item.e10 != null) && (item.e10 < attr.e10)){
attr = item
}
}
log(attr.e10)
log("address:\n" + attr.street + attr.houseNumber + attr.postCode + attr.place)
const widget = new ListWidget()
widget.url = await map(attr.lat, attr.lng, attr.name)
log(widget.url)
// header:
var header_stack = widget.addStack();
var symbol = SFSymbol.named('car.fill').image;
var symbol_image = header_stack.addImage(symbol);
symbol_image.imageSize = new Size(15, 15);
symbol_image.tintColor = Color.blue()
header_stack.addSpacer(3);
var title = header_stack.addText("Sprit Preise");
title.font = Font.boldRoundedSystemFont(13)
title.textColor = Color.blue()
console.log("title:\n" + title.text + "\n")
// line:
let lineImg = lineSep()
let line = widget.addImage(lineImg)
line.resizable=false
line.imageOpacity=1
widget.addSpacer(3)
// price:
var line2Stack = widget.addStack()
var line2 = line2Stack.addText(formatValue(attr.e10))
line2.font = Font.boldRoundedSystemFont(36)
var euroStack = line2Stack.addStack()
euroStack.layoutVertically()
euroStack.addSpacer(12)
var euro = euroStack.addText("€")
euro.font = Font.boldRoundedSystemFont(24)
if (fm.fileExists(historyFilePath) == false) {
fm.writeString(historyFilePath, JSON.stringify(data));
}
const dataPrev = JSON.parse(fm.readString(historyFilePath));
let currentPrice = Number(attr.e10)
var previousPriceAttr = dataPrev.stations[0]
dataPrev.stations.forEach(prevStation)
function prevStation(item, index) {
if ((item.e10 != null) && (String(item.lat + "_" + item.lng) == String(attr.lat + "_" + attr.lng))){
previousPriceAttr = item
}
}
let previousPrice = previousPriceAttr.e10
log(previousPrice)
var change = 0
let priceList = new Array()
var all = 0
var c = 0
list.forEach(myFunction)
function myFunction(item, index){
if (index < callDataAmount) {
let itemPath = fm.joinPath(historyFolderPath, "FuelPriceNearest" + (list.length - index - 1) + ".json")
const itemData = JSON.parse(fm.readString(itemPath))
if(itemData != null) {
itemData.stations.forEach(itemStation)
function itemStation(i, index) {
if ((i.e10 != null) && (String(i.lat + "_" + i.lng) == String(attr.lat + "_" + attr.lng))){
priceList[c] = i.e10
c = c + 1
}
}
}
}
}
priceList.forEach(allF)
function allF(item, index) {
all = all + Number(item)
}
log("all: " + all)
change = all / (priceList.length)
log("change: " + change)
let avrg = String(Math.round((change + Number.EPSILON) * 100) / 100)
let avrgC = avrg.replace(".","")
if (avrgC.length < 3) {
if (avrg.includes("1.")) {
avrg = avrg + "0"
if (avrgC.length == 1) {
avrg = avrg + "0"
}
}else if (avrg.includes("0.")) {
avrg = "0." + avrgC + "0"
}
}
log("avrg: " + avrg)
change = Number(avrg)
if (change != 0) {
if (change == currentPrice) {
line2.textColor = Color.orange()
euro.textColor = Color.orange()
}else if (change > currentPrice) {
line2.textColor = Color.green()
euro.textColor = Color.green()
}else if (change < currentPrice) {
line2.textColor = Color.red()
euro.textColor = Color.red()
}
}
if (list.length > callDataAmount) {
let amount = list.length - callDataAmount
list.forEach(deleteFunc)
function deleteFunc(item, index) {
if (index < amount) {
let itemPath = fm.joinPath(historyFolderPath, "FuelPriceNearest" + (amount - index - 1) + ".json")
// if (fm.fileExists(itemPath)) {
// fm.remove(itemPath)
// }
}
}
log(list.length)
}
if (currentPrice != previousPrice) {
fm.writeString(path, JSON.stringify(data))
}
var line3text = "Super E10"
if (avrg > 0) {
line3text = "Super E10 ⌀" + avrg
}
var line3 = widget.addText(line3text)
line3.font = Font.boldRoundedSystemFont(14)
line3.textColor = Color.blue()
widget.addSpacer(0)
let stationStack = widget.addStack()
stationStack.layoutHorizontally()
stationStack.setPadding(0, -4, 0, 0)
let stationOpen = stationStack.addText("|")
stationOpen.font = Font.boldRoundedSystemFont(35)
stationOpen.textColor = Color.red()
if (attr.isOpen == true) {
stationOpen.textColor = Color.green()
}
let nameStack = stationStack.addStack()
nameStack.layoutVertically()
nameStack.addSpacer(5)
log(attr.brand)
let stationName = nameStack.addText(attr.brand + " → " + attr.dist + " km")
stationName.font = Font.boldRoundedSystemFont(10)
let place = nameStack.addText(attr.place)
place.font = Font.boldRoundedSystemFont(10)
let street = nameStack.addText(attr.street)
street.font = Font.boldRoundedSystemFont(10)
return widget
}
Benzinpreis Widget
Das Widget zeigt den aktuellen Benzinpreis der günstigsten Tankstelle in der Nähe an
Tankerkönig API Key
Die Daten stammen von der Tankerkönig API.
Um das Widget Nutzen zu können, bitte bei Tankerkönig einen kostenlosen API Key anfordern. Den Key erhält man sofort nach Eingabe und Bestätigung der E-Mail Adresse. Anschließend den Key anstelle des Platzhalter Keys im Script einfügen.
Nutzung
Changelog
2021-03-18
2022-03-17
Mehr
Webcam Widget