-
-
Save marco79cgn/23ce08fd8711ee893a3be12d4543f2d2 to your computer and use it in GitHub Desktop.
// dm Klopapier Widget | |
// | |
// Copyright (C) 2020 by marco79 <marco79cgn@gmail.com> | |
// | |
// Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. | |
// | |
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL | |
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, | |
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |
// IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE | |
// OF THIS SOFTWARE. | |
// | |
// Toilet paper icon made by boettges | |
let country = 'de' // für Österreich bitte 'at' verwenden | |
let storeId = 251 | |
let param = args.widgetParameter | |
if (param != null && param.length > 0) { | |
storeId = param | |
} | |
const widget = new ListWidget() | |
const storeInfo = await fetchStoreInformation() | |
const storeCapacity = await fetchAmountOfPaper() | |
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() { | |
widget.addSpacer(4) | |
const logoImg = await getImage('dm-logo.png') | |
widget.setPadding(10, 10, 10, 10) | |
const titleFontSize = 12 | |
const detailFontSize = 36 | |
const logoStack = widget.addStack() | |
logoStack.addSpacer(86) | |
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(40, 40) | |
wimg.rightAlignImage() | |
widget.addSpacer() | |
const icon = await getImage('toilet-paper.png') | |
let row = widget.addStack() | |
row.layoutHorizontally() | |
row.addSpacer(2) | |
const iconImg = row.addImage(icon) | |
iconImg.imageSize = new Size(40, 40) | |
row.addSpacer(13) | |
let column = row.addStack() | |
column.layoutVertically() | |
const paperText = column.addText("KLOPAPIER") | |
paperText.font = Font.mediumRoundedSystemFont(13) | |
const packageCount = column.addText(storeCapacity.toString()) | |
packageCount.font = Font.mediumRoundedSystemFont(22) | |
if (storeCapacity < 30) { | |
packageCount.textColor = new Color("#E50000") | |
} else { | |
packageCount.textColor = new Color("#00CD66") | |
} | |
widget.addSpacer(4) | |
const row2 = widget.addStack() | |
row2.layoutVertically() | |
const street = row2.addText(storeInfo.address.street) | |
street.font = Font.regularSystemFont(11) | |
const zipCity = row2.addText(storeInfo.address.zip + " " + storeInfo.address.city) | |
zipCity.font = Font.regularSystemFont(11) | |
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 = row2.addText('Geöffnet') | |
shopStateText.textColor = new Color("#00CD66") | |
} else { | |
shopStateText = row2.addText('Geschlossen') | |
shopStateText.textColor = new Color("#E50000") | |
} | |
shopStateText.font = Font.mediumSystemFont(11) | |
} | |
// 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) { | |
if (apiResult.storeAvailabilities[i][0].stockLevel) { | |
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 | |
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 | |
// bitte bis zum Ende kopieren |
Hallo zusammen... Die Widgets sind richtig gut 👌🏻Ich habe eine Frage bzw. Bitte. Ich nutze den Dark Mode, würde mir die Widgets aber gerne in der Light Mode Version anzeigen lassen. Ist das möglich?
Kann lediglich über einfügen widget.backround etc. die Hintergrundfarbe ändern. Die Schriftfarbe jedoch nicht.
Kann man den automatischen Switch Dark/Light ändern? Bzw. abschalten?
Vielen Dank
DirkDoch genau, du müsstest den Hintergrund fix auf weiß setzen und sämtliche Texte auf schwarz. Das geht zum Beispiel so:
const blaText = widget.addText("Blubb") blaText.textColor = Color.black()
Wenn man die Textfarbe gar nicht setzt, dann wird automatisch im Light Mode schwarz genommen und im Dark Mode weiß.
Ok. Danke für die Rückmeldung! 🙋🏼♂️Werde es versuchen...
const blaText = widget.addText("Blubb")
blaText.textColor = Color.black()
Ich habe es fast hinbekommen! 😬Nur leider beim Ort weiß ich nicht was ich eingeben soll:
// Main stack for value and area name
let incidenceStack = textStack.addStack()
let valueStack = incidenceStack.addStack()
incidenceStack.layoutVertically()
let incidenceValueLabel = valueStack.addText(data.incidence + data.trend)
incidenceValueLabel.font = Font.boldSystemFont(24)
incidenceValueLabel.textColor = data.incidence >= 100 ? new Color("9e000a") : data.incidence >= 50 ? Color.red() : data.incidence >= 35 ? Color.yellow() : Color.green();
incidenceStack.addText(data.areaName)
Der Ort wird weiterhin in weiß angezeigt und verschwindet natürlich beim fix setzen des Hintergrundes auf weiß 🤦🏼♂️🤷♂️
const blaText = widget.addText("Blubb")
blaText.textColor = Color.black()Ich habe es fast hinbekommen! 😬Nur leider beim Ort weiß ich nicht was ich eingeben soll:
incidenceStack.addText(data.areaName)Der Ort wird weiterhin in weiß angezeigt und verschwindet natürlich beim fix setzen des Hintergrundes auf weiß 🤦🏼♂️🤷♂️
Das ist nicht mein Widget. Probier es so. Statt
incidenceStack.addText(data.areaName)
das hier
const areaNameText = incidenceStack.addText(data.areaName)
areaNameText.textColor = Color.black()
const blaText = widget.addText("Blubb")
blaText.textColor = Color.black()Ich habe es fast hinbekommen! 😬Nur leider beim Ort weiß ich nicht was ich eingeben soll:
incidenceStack.addText(data.areaName)
Der Ort wird weiterhin in weiß angezeigt und verschwindet natürlich beim fix setzen des Hintergrundes auf weiß 🤦🏼♂️🤷♂️Das ist nicht mein Widget. Probier es so. Statt
incidenceStack.addText(data.areaName)
das hierconst areaNameText = incidenceStack.addText(data.areaName) areaNameText.textColor = Color.black()
Krass 😳Das hat funktioniert! Vielen Dank 🙏🏼
Stark inspiriert von dem Klopapier Widget hab ich ein Impftermin-Widget gebaut:
freie Termine | keine Termine |
---|---|
Es zeigt an ob ihm lokalen Impfzentrum noch Termine verfügbar sind. Das ganze funktioniert aktuell in ["Baden-Württemberg", "Hamburg", "Hessen", "Nordrhein-Westfalen", "Sachsen-Anhalt"]
https://gist.github.com/not-a-feature/4e6dbbd9eb3bd927e50cae347b7e0486
Sehr geil, @not-a-feature , so erspart man sich selber für seine Eltern/Großeltern und natürlich auch denselbigen viele unnötige und vor allem erfolglose Anrufe und elend lange Warteschlangen 👍
Das Widget funktioniert leider nicht mehr. Ich bekomme anstatt der Anzahl nur noch „NaN“ angezeigt. Hat DM was an der Webseite geändert?
Das Impf-Widget hat nichts mit diesem Klopapier-Widget hier zu tun und verhindert wird bisher nichts bei dm. Da scheint sich an der Struktur des json was geändert zu haben. Schau ich mir an...
Bisschen verwirrend: Also aktuell funktioniert weder das DM Widget als auch das Impf Widget.
Update (20.04.2021): Skript funktioniert wieder
Einige Produkte sind nicht mehr im Sortiment und daher hat der Counter nicht mehr funktioniert. Bitte Skript neu kopieren.
das FFP2+Test Widget funktioniert auch nicht mehr. Dann hat DM ja heute viel rumgebastelt. Kannst du die Versionsnummer/Last Update noch ins Script mit schreiben. Danke
das FFP2+Test Widget funktioniert auch nicht mehr. Dann hat DM ja heute viel rumgebastelt.
Eigentlich hat dm gar nichts geändert. Man muss eben die Produktnummer im Skript aktuell halten. Bei so schnelllebigen Dingen wie Masken fliegen eben mal welche aus dem Sortiment und neue kommen hinzu. Zusätzlich war der Counter nicht robust, so dass ein ungültiges Produkt zu diesem NaN geführt hat. Ist jetzt auch im FFP Skript behoben.
Kannst du die Versionsnummer/Last Update noch ins Script mit schreiben.
Es ist sehr wenig Platz im Widget und ich persönlich finde diese Info nicht sehr sinnvoll. Aber du kannst sie gerne bei dir manuell ergänzen. Die Klopapier und FFP2 Skripte nutzen kein Caching (im Gegensatz zum Impffortschritt), so dass sie alle 5-10 Minuten aktualisiert werden. Daher ist Last Update imho auch eher unnötig.
Hallo,
kann ich das Skript auch für andere Produkte nutzen wenn ja wie genau?, ich würde gerne wissen ob im DM Babynahrung verfügbar ist?
Vielen Dank vorab.
Du brauchst zwei Dinge:
- IDs der Produkte, die du beobachten willst
- Eine Store-ID. Diese bekommst du zum Beispiel dort: https://softcreatr.github.io/dm-store-finder/
Du brauchst zwei Dinge:
- IDs der Produkte, die du beobachten willst
- Eine Store-ID. Diese bekommst du zum Beispiel dort: https://softcreatr.github.io/dm-store-finder/
Ok die Store-ID hab ich, wo füge ich die ID des Produktes ein?
Du brauchst zwei Dinge:
- IDs der Produkte, die du beobachten willst
- Eine Store-ID. Diese bekommst du zum Beispiel dort: https://softcreatr.github.io/dm-store-finder/
Ok die Store-ID hab ich, wo füge ich die ID des Produktes ein?
https://gist.github.com/marco79cgn/23ce08fd8711ee893a3be12d4543f2d2#file-dm-toilet-paper-js-L128
Hallo, vielen Dank für das Script! Ich habe eben mal versucht, es etwas anzupassen für ein anderes Produkt. Da es dort nicht geklappt hat, habe ich mir zur Kontrolle noch einmal eine einzelne Produkt-ID von einem in meiner Filiale lt website verfügbaren Klopapiers genommen, aber der counter spuckt dennoch 0 aus. Hat sich ggf im Stocklevel check seitens dm was verändert? Wenn ich den request im browser ausführe, kommt bspw folgender output:
{"tenant":"de","storeAvailabilities":{"4010355274656":[{"store":{"storeNumber":"1099"},"dan":"4010355274656","inStock":false,"stockLevel":0,"predictedStockLevel":0,"checkStatus":{"status":"ERROR","code":"STOCK_LEVEL_CHECK_FAILED"}}]}}
Was kann ich tun, um die Abfrage mit Erfolg ins Script zu kriegen? Und wäre es auf lange Sicht denkbar, dass die Store ID automatisch anhand des per Standort ermittelten DM Stores eingetragen wird? LG
Habe eben mal schnell im Browser drauf geschaut und an der Abfrage bezüglich des Stock-Levels hat sich nichts geändert.
Beispiel: Zewa Toilettenpapier Smart 3-lagig (4x300 Blatt), 4 St
URL: https://www.dm.de/zewa-toilettenpapier-smart-3-lagig-4-x-300-blatt-p7322541147741.html
dan: 853483
Api call: https://products.dm.de/store-availability/de/availability?dans=853483&storeNumbers=300
Ergebnis:
{
"tenant": "de",
"storeAvailabilities": {
"853483": [
{
"store": {
"storeNumber": "300"
},
"dan": "853483",
"inStock": true,
"stockLevel": 12,
"checkStatus": {
"status": "OK",
"code": "CHECK_SUCCESSFUL"
}
}
]
}
}
Deine dan sieht allerdings seltsam aus, sehr lang. Um welches Produkt handelt es sich denn? Und auf welches Produkt wolltest du es anpassen?
Habe eben mal schnell im Browser drauf geschaut und an der Abfrage bezüglich des Stock-Levels hat sich nichts geändert. Beispiel: Zewa Toilettenpapier Smart 3-lagig (4x300 Blatt), 4 St URL: https://www.dm.de/zewa-toilettenpapier-smart-3-lagig-4-x-300-blatt-p7322541147741.html dan: 853483 Api call: https://products.dm.de/store-availability/de/availability?dans=853483&storeNumbers=300 Ergebnis:
{ "tenant": "de", "storeAvailabilities": { "853483": [ { "store": { "storeNumber": "300" }, "dan": "853483", "inStock": true, "stockLevel": 12, "checkStatus": { "status": "OK", "code": "CHECK_SUCCESSFUL" } } ] } }
Deine dan sieht allerdings seltsam aus, sehr lang. Um welches Produkt handelt es sich denn? Und auf welches Produkt wolltest du es anpassen?
Hi, danke für die Antwort. Habe nochmal nachgesehen, es lag an der DAN… die hab ich falsch gesourced. Habe die aus der url einer Produktansicht genommen und das war der Fehler..
Ich habe einen Siri Shortcut gebaut, mit dem man anhand der PLZ eine Store ID ermitteln kann und anschließend anhand des Produktnamens die passende DAN finden.
Shortcut Download: click
Jetzt mal ganz doof gefragt, was kann man machen wenn der Availabilitycheck eines Artikels nur den Error ausspuckt? Einfach abwarten und hoffen dass es von DM wieder gefixt wird? Meist ist der Artikel nicht vorhanden aber dann wurde in der Vergangenheit immer brav "0" angezeigt. In der regulären Kartenansicht steht auch "Keine Anzeige möglich". Oder macht es DM vielleicht sogar extra bei Dingen die schnell ausverkauft sind?
Übrigens Riesendank für die ganzen Widgets, ich hab die wahrscheinlich auf die amateurhafteste Weise umgebaut aber für einige Zeit haben sie fantastisch funktioniert bis die Abfrage mit Fehlermeldungen angefangen hat.
Hey man, would it be possible to make it work in Czech republic? I tried, but I couldn’t make it work :(.
Thanks a lot for your reply!
M.
Ich glaube DM hat die Api geändert. Egal wie ich es versuche es kommen nur Fehlermeldungen.
Ich glaube DM hat die Api geändert. Egal wie ich es versuche es kommen nur Fehlermeldungen.
Bei mir funktioniert es nach wie vor. Hast du bei copy/paste auch alle Zeilen bis zum Ende des Skripts übertragen?
Ich habe alles so gelassen wie es war. Ich habe nur die Produkt IDs geändert und die Store ID, es wird immer 0 angezeigt und wenn ich versuche mit Postman eine Anfrage zu schicken kommt immer Fehlercode 404.
Klopapier:
url = 'https://products.dm.de/store-availability/DE/availabilities/tiles?dans=1136733,1533020,1587820,1617830,1618716,1626481,1628029,1650106,1673728,1673782,1673958,1674098&pickupStoreId=' + storeId
Mehl:
url = 'https://products.dm.de/store-availability/DE/availabilities/tiles?dans=1440266,1446463,146521,1494740,1639885&pickupStoreId=' + storeId
Kann sein, dass sich auch die StoreID geändert hat, die musst du rauskriegen. Meine ist jetzt zum Beispie D4EE.
Alles klar, jetzt gehts. Das Template aus der Scriptable Galerie hatte bei mir noch einen solchen Pfad drinne: "/store-availability/de/availability?dans=" damit ging es natürlich nicht. Danke!
Doch genau, du müsstest den Hintergrund fix auf weiß setzen und sämtliche Texte auf schwarz. Das geht zum Beispiel so:
Wenn man die Textfarbe gar nicht setzt, dann wird automatisch im Light Mode schwarz genommen und im Dark Mode weiß.