274 lines
11 KiB
JavaScript
274 lines
11 KiB
JavaScript
// Variables used by Scriptable.
|
|
// These must be at the very top of the FileManager. Do not edit.
|
|
// icon-color: deep-green; icon-glyph: power-off;
|
|
|
|
// API-URLs für Mumble- und Space-Status
|
|
const mumbleApiUrl = "https://status.chaospott.de/chaospott_mumble.json";
|
|
const spaceApiUrl = "https://status.chaospott.de/status.json";
|
|
|
|
// URLs für das Logo und lokale Dateinamen für Caching
|
|
const logoUrl = "https://chaospott.de/images/logo.png";
|
|
const logoLocalFilename = "chaospott_logo.png";
|
|
const mumbleLocalFilename = "chaospott_mumble.json";
|
|
const spaceLocalFilename = "chaospott_space.json";
|
|
|
|
// Titel und Untertitel des Widgets
|
|
const title = "Chaospott";
|
|
const subTitle = "Essen";
|
|
|
|
// Variablen für die Farbzustände der Räume
|
|
var colorSpaceClosed;
|
|
var colorSpaceOpen;
|
|
|
|
// Widget-Erstellung und Anzeige
|
|
var widget = await createWidget();
|
|
|
|
// Wenn das Skript nicht im Widget-Kontext läuft, präsentiere das Widget als kleines Fenster
|
|
if (!config.runsInWidget) {
|
|
await widget.presentSmall();
|
|
}
|
|
|
|
// Setze das Widget
|
|
Script.setWidget(widget);
|
|
Script.complete();
|
|
|
|
// Funktion zur Erstellung des Widgets
|
|
async function createWidget() {
|
|
// Definiere Farben für verschiedene Status
|
|
const colorOpenFresh = Color.green();
|
|
const colorOpenStale = new Color("#00ff00", 0.3);
|
|
const colorClosedFresh = new Color("#ff0000", 1.0);
|
|
const colorClosedStale = new Color("#ff0000", 0.3);
|
|
const colorLonelyFresh = new Color("#ff8800", 1.0);
|
|
const colorLonelyStale = new Color("#ff8800", 0.3);
|
|
|
|
// Setze Standardfarben für den Widget-Hintergrund
|
|
var colorBorderOpen = colorOpenFresh;
|
|
var colorBorderClosed = colorClosedFresh;
|
|
var colorBorderLonely = colorLonelyFresh;
|
|
var colorMumbleOpen;
|
|
var colorMumbleClosed;
|
|
var colorMumbleLonely;
|
|
|
|
// Erstelle ein neues Widget
|
|
const widget = new ListWidget();
|
|
|
|
// Versuche, die JSON-Daten von den APIs abzurufen und im Cache zu speichern
|
|
try {
|
|
var [mumbleStatus, mumbleFresh] = await getJSONandCache(mumbleLocalFilename, mumbleApiUrl);
|
|
var [spaceStatus, spaceFresh] = await getJSONandCache(spaceLocalFilename, spaceApiUrl);
|
|
} catch (err) {
|
|
// Bei Fehlern zeige eine Fehlermeldung an
|
|
const errorList = new ListWidget();
|
|
errorList.addText("Please enable internet for initial execution.");
|
|
return errorList;
|
|
}
|
|
|
|
// Setze die Farben basierend auf den aktuellen Mumble-Daten
|
|
if (mumbleFresh) {
|
|
colorMumbleOpen = colorOpenFresh;
|
|
colorMumbleClosed = colorClosedFresh;
|
|
colorMumbleLonely = colorLonelyFresh;
|
|
} else {
|
|
colorMumbleOpen = colorOpenStale;
|
|
colorMumbleClosed = colorClosedStale;
|
|
colorMumbleLonely = colorLonelyStale;
|
|
colorBorderOpen = colorOpenStale;
|
|
colorBorderClosed = colorClosedStale;
|
|
colorBorderLonely = colorLonelyStale;
|
|
}
|
|
|
|
// Setze die Farben basierend auf den aktuellen Space-Daten
|
|
if (spaceFresh) {
|
|
colorSpaceOpen = colorOpenFresh;
|
|
colorSpaceClosed = colorClosedFresh;
|
|
} else {
|
|
colorSpaceOpen = colorOpenStale;
|
|
colorSpaceClosed = colorClosedStale;
|
|
colorBorderOpen = colorOpenStale;
|
|
colorBorderClosed = colorClosedStale;
|
|
colorBorderLonely = colorLonelyStale;
|
|
}
|
|
|
|
// Bestimme die Hintergrundfarbe des Widgets basierend auf dem Status des Raums und der Benutzeranzahl
|
|
if (spaceStatus.state.open) {
|
|
widget.backgroundColor = colorBorderOpen;
|
|
} else {
|
|
switch (mumbleStatus.connected_users) {
|
|
case 0:
|
|
widget.backgroundColor = colorBorderClosed;
|
|
break;
|
|
case 1:
|
|
widget.backgroundColor = colorBorderLonely;
|
|
break;
|
|
default:
|
|
widget.backgroundColor = colorBorderOpen;
|
|
}
|
|
}
|
|
|
|
// Setze Padding für das Widget
|
|
widget.setPadding(0, 5, 0, 5);
|
|
canvasStack = widget.addStack();
|
|
canvasStack.setPadding(8, 15, 8, 15);
|
|
canvasStack.cornerRadius = 15; // Runde die Ecken des Widgets
|
|
canvasStack.layoutVertically(); // Vertikale Anordnung der Elemente
|
|
canvasStack.backgroundColor = Color.dynamic(Color.white(), Color.black());
|
|
|
|
// Header-Stack für Titel und Logo
|
|
const headerStack = canvasStack.addStack();
|
|
const titleStack = headerStack.addStack();
|
|
|
|
titleStack.layoutVertically(); // Vertikale Anordnung für Titel und Untertitel
|
|
const titleText = titleStack.addText(title); // Titel hinzufügen
|
|
titleText.font = Font.regularSystemFont(16); // Schriftart für Titel
|
|
|
|
const subTitleText = titleStack.addText(subTitle); // Untertitel hinzufügen
|
|
subTitleText.font = Font.mediumSystemFont(10); // Schriftart für Untertitel
|
|
|
|
headerStack.addSpacer(5); // Abstand hinzufügen
|
|
|
|
// Logo hinzufügen
|
|
let logo = await getCachedImage(logoLocalFilename, logoUrl);
|
|
const logoImage = headerStack.addImage(logo);
|
|
logoImage.imageSize = new Size(30, 30); // Größe des Logos
|
|
|
|
canvasStack.addSpacer(5); // Abstand hinzufügen
|
|
|
|
// Mittelreihe für den Raumstatus
|
|
const middleRow = canvasStack.addStack();
|
|
spaceStatus.sensors.door_locked.forEach((obj, i, arr) => {
|
|
spaceView(middleRow, obj.location, obj.value); // Raumansicht für jeden Sensor
|
|
if (i !== arr.length - 1) { middleRow.addSpacer(); } // Abstand zwischen den Sensoren
|
|
});
|
|
|
|
// Letzte Aktualisierung des Raums anzeigen
|
|
const spaceLastUpdate = new Date(spaceStatus.state.lastchange * 1000);
|
|
const spaceLastUpdateStack = canvasStack.addStack();
|
|
let spaceLastUpdateLabel = spaceLastUpdateStack.addDate(spaceLastUpdate);
|
|
spaceLastUpdateLabel.font = Font.mediumSystemFont(6); // Schriftart für das Datum
|
|
spaceLastUpdateLabel.applyRelativeStyle(); // Relatives Datum anwenden
|
|
|
|
// Untere Reihe für Mumble-Status
|
|
const bottomRow = canvasStack.addStack();
|
|
bottomRow.useDefaultPadding(); // Standard-Padding verwenden
|
|
bottomRow.centerAlignContent(); // Inhalte zentrieren
|
|
const mumbleLabelStack = bottomRow.addStack();
|
|
mumbleLabelStack.layoutVertically(); // Vertikale Anordnung für Mumble-Label
|
|
|
|
const labelMumble = mumbleLabelStack.addText("M"); // Mumble-Label hinzufügen
|
|
labelMumble.font = Font.regularSystemFont(12); // Schriftart für Mumble-Label
|
|
|
|
// Letzte Aktualisierung des Mumble-Servers anzeigen
|
|
let mumbleLastUpdate = new Date(mumbleStatus.last_update * 1000);
|
|
const labelMumbleUpdated = mumbleLabelStack.addDate(mumbleLastUpdate);
|
|
labelMumbleUpdated.font = Font.mediumSystemFont(6); // Schriftart für das Datum
|
|
labelMumbleUpdated.applyTimeStyle(); // Zeitstil anwenden
|
|
|
|
bottomRow.addSpacer(3); // Abstand hinzufügen
|
|
|
|
// Anzahl der verbundenen Benutzer anzeigen
|
|
const mumbleValueStack = bottomRow.addStack();
|
|
const labelMumbleUser = mumbleValueStack.addText(mumbleStatus.connected_users.toString(10)); // Benutzeranzahl hinzufügen
|
|
labelMumbleUser.font = Font.boldSystemFont(23); // Schriftart für Benutzeranzahl
|
|
|
|
// Benutzeranzahl-Farbe basierend auf dem Status festlegen
|
|
switch (mumbleStatus.connected_users) {
|
|
case 0:
|
|
labelMumbleUser.textColor = colorMumbleClosed; // Rot für geschlossen
|
|
break;
|
|
case 1:
|
|
labelMumbleUser.textColor = colorMumbleLonely; // Orange für einsam
|
|
break;
|
|
default:
|
|
labelMumbleUser.textColor = colorMumbleOpen; // Grün für offen
|
|
}
|
|
|
|
bottomRow.addSpacer(10); // Abstand hinzufügen
|
|
|
|
// Abstand zwischen den Elementen
|
|
canvasStack.addSpacer(4);
|
|
|
|
// Datum und Uhrzeit hinzufügen
|
|
dateStack = canvasStack.addStack();
|
|
dateStack.layoutHorizontally(); // Horizontale Anordnung für Datum
|
|
dateStack.bottomAlignContent(); // Inhalte am unteren Rand ausrichten
|
|
dateStack.addSpacer(41); // Abstand hinzufügen
|
|
const now = new Date(); // Aktuelle Zeit abrufen
|
|
const timeLabel = dateStack.addDate(now); // Uhrzeit hinzufügen
|
|
timeLabel.font = Font.mediumSystemFont(10); // Schriftart für die Uhrzeit
|
|
timeLabel.centerAlignText(); // Uhrzeit zentrieren
|
|
timeLabel.applyTimeStyle(); // Zeitstil anwenden
|
|
timeLabel.textColor = Color.darkGray(); // Schriftfarbe für die Uhrzeit
|
|
|
|
return widget; // Das erstellte Widget zurückgeben
|
|
}
|
|
|
|
// Funktion zur Anzeige des Raumstatus
|
|
function spaceView(widget, space, lockStatus) {
|
|
const viewStack = widget.addStack(); // Neuen Stack für den Raumstatus hinzufügen
|
|
viewStack.layoutVertically(); // Vertikale Anordnung
|
|
viewStack.centerAlignContent(); // Inhalte zentrieren
|
|
|
|
// Raumname formatieren
|
|
const spaceName = space.charAt(0).toUpperCase() + space.slice(1);
|
|
const label = viewStack.addText(spaceName); // Raumname hinzufügen
|
|
label.font = Font.regularSystemFont(14); // Schriftart für Raumname
|
|
|
|
// Schloss-Symbol hinzufügen, abhängig vom Lock-Status
|
|
const lock = SFSymbol.named("lock." + (lockStatus ? "" : "open.") + "fill");
|
|
lock.applyFont(Font.systemFont(20)); // Schriftart für das Symbol
|
|
const lockImage = viewStack.addImage(lock.image); // Schlossbild hinzufügen
|
|
lockImage.resizable = false; // Größe des Bildes festlegen
|
|
lockImage.imageSize = new Size(25, 25); // Größe des Schlosses
|
|
|
|
// Färbe das Schloss basierend auf dem Lock-Status
|
|
if (lockStatus) {
|
|
lockImage.tintColor = colorSpaceClosed; // Rot für geschlossen
|
|
} else {
|
|
lockImage.tintColor = colorSpaceOpen; // Grün für offen
|
|
}
|
|
}
|
|
|
|
// Funktion zum Abrufen und Cachen von Bildern
|
|
async function getCachedImage(localFilename, url) {
|
|
let fm = FileManager.local(); // Lokalen FileManager abrufen
|
|
let dir = fm.cacheDirectory(); // Cache-Verzeichnis abrufen
|
|
let path = fm.joinPath(dir, localFilename); // Pfad zur lokalen Datei erstellen
|
|
if (fm.fileExists(path)) {
|
|
return fm.readImage(path); // Bild aus dem Cache lesen, wenn vorhanden
|
|
} else {
|
|
let r = new Request(url); // Anfrage an die URL erstellen
|
|
try {
|
|
let returnImage = await r.loadImage(); // Bild von der URL laden
|
|
fm.writeImage(path, returnImage); // Bild im Cache speichern
|
|
return returnImage; // Das geladene Bild zurückgeben
|
|
} catch (err) {
|
|
// Bei Fehlern ein Platzhalterbild zurückgeben
|
|
return SFSymbol.named("photo").image;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Funktion zum Abrufen und Cachen von JSON-Daten
|
|
async function getJSONandCache(localFilename, url) {
|
|
let fm = FileManager.local(); // Lokalen FileManager abrufen
|
|
let dir = fm.cacheDirectory(); // Cache-Verzeichnis abrufen
|
|
let path = fm.joinPath(dir, localFilename); // Pfad zur lokalen Datei erstellen
|
|
let r = new Request(url); // Anfrage an die URL erstellen
|
|
try {
|
|
var data = await r.loadJSON(); // JSON-Daten von der URL laden
|
|
fm.writeString(path, JSON.stringify(data, null, 2)); // Daten im Cache speichern
|
|
var fresh = true; // Daten sind aktuell
|
|
} catch (err) {
|
|
// Wenn der Abruf fehlschlägt, versuche, die zwischengespeicherte Version zu lesen
|
|
if (fm.fileExists(path)) {
|
|
data = JSON.parse(fm.readString(path), null); // Daten aus dem Cache lesen
|
|
fresh = false; // Daten sind nicht aktuell
|
|
} else {
|
|
throw "no data"; // Keine Daten vorhanden
|
|
}
|
|
}
|
|
return [data, fresh]; // Die Daten und die Aktualität der Daten zurück
|
|
}
|
|
|