forked from apoc/chaospott_mumble
initial commit
This commit is contained in:
commit
1466bf2c8e
229
chaospott_mumble.js
Normal file
229
chaospott_mumble.js
Normal file
@ -0,0 +1,229 @@
|
||||
// Variables used by Scriptable.
|
||||
// These must be at the very top of the file. Do not edit.
|
||||
// icon-color: yellow; icon-glyph: magic;
|
||||
const mumbleApiUrl = "https://apoc.uber.space/chaospott_mumble.json";
|
||||
const spaceApiUrl = "https://status.chaospott.de/status.json";
|
||||
const logoUrl = "https://chaospott.de/images/logo.png";
|
||||
const logoLocalFilename = "chaospott_logo.png";
|
||||
const mumbleLocalFilename = "chaospott_mumble.json";
|
||||
const spaceLocalFilename = "chaospott_space.json";
|
||||
|
||||
const title = "Chaospott";
|
||||
const subTitle = "Essen";
|
||||
|
||||
var colorSpaceClosed;
|
||||
var colorSpaceOpen;
|
||||
|
||||
var widget = await createWidget();
|
||||
|
||||
if (!config.runsInWidget) {
|
||||
await widget.presentSmall();
|
||||
}
|
||||
Script.setWidget(widget);
|
||||
Script.complete();
|
||||
|
||||
|
||||
async function createWidget(){
|
||||
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);
|
||||
var colorBorderOpen = colorOpenFresh;
|
||||
var colorBorderClosed = colorClosedFresh;
|
||||
var colorBorderLonely = colorLonelyFresh;
|
||||
var colorMumbleOpen;
|
||||
var colorMumbleClosed;
|
||||
var colorMumbleLonely;
|
||||
|
||||
const widget = new ListWidget();
|
||||
|
||||
try {
|
||||
var [mumbleStatus, mumbleFresh] = await getJSONandCache(mumbleLocalFilename, mumbleApiUrl);
|
||||
var [spaceStatus, spaceFresh] = await getJSONandCache(spaceLocalFilename, spaceApiUrl);
|
||||
} catch(err) {
|
||||
const errorList = new ListWidget();
|
||||
errorList.addText("Please enable internet for initial execution.");
|
||||
return errorList;
|
||||
}
|
||||
|
||||
if (mumbleFresh){
|
||||
colorMumbleOpen = colorOpenFresh;
|
||||
colorMumbleClosed = colorClosedFresh;
|
||||
colorMumbleLonely = colorLonelyFresh;
|
||||
} else {
|
||||
colorMumbleOpen = colorOpenStale;
|
||||
colorMumbleClosed = colorClosedStale;
|
||||
colorMumbleLonely = colorLonelyStale;
|
||||
colorBorderOpen = colorOpenStale;
|
||||
colorBorderClosed = colorClosedStale;
|
||||
colorBorderLonely = colorLonelyStale;
|
||||
}
|
||||
|
||||
if (spaceFresh){
|
||||
colorSpaceOpen = colorOpenFresh;
|
||||
colorSpaceClosed = colorClosedFresh;
|
||||
} else {
|
||||
colorSpaceOpen = colorOpenStale;
|
||||
colorSpaceClosed = colorClosedStale;
|
||||
colorBorderOpen = colorOpenStale;
|
||||
colorBorderClosed = colorClosedStale;
|
||||
colorBorderLonely = colorLonelyStale;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
widget.setPadding(0, 5, 0, 5);
|
||||
canvasStack = widget.addStack();
|
||||
canvasStack.setPadding(5, 15, 5, 15);
|
||||
canvasStack.cornerRadius = 15;
|
||||
canvasStack.layoutVertically();
|
||||
canvasStack.backgroundColor = Color.dynamic(Color.white(), Color.black());
|
||||
|
||||
const headerStack = canvasStack.addStack();
|
||||
const titleStack = headerStack.addStack();
|
||||
|
||||
titleStack.layoutVertically();
|
||||
const titleText = titleStack.addText(title);
|
||||
titleText.font = Font.regularSystemFont(16);
|
||||
|
||||
const subTitleText = titleStack.addText(subTitle);
|
||||
subTitleText.font = Font.mediumSystemFont(10);
|
||||
|
||||
headerStack.addSpacer(5);
|
||||
|
||||
let logo = await getCachedImage(logoLocalFilename, logoUrl);
|
||||
const logoImage = headerStack.addImage(logo);
|
||||
logoImage.imageSize = new Size(30, 30);
|
||||
|
||||
canvasStack.addSpacer(5);
|
||||
|
||||
const middleRow = canvasStack.addStack();
|
||||
spaceStatus.sensors.door_locked.forEach((obj,i,arr) => {
|
||||
spaceView(middleRow, obj.location, obj.value);
|
||||
if(i !== arr.length - 1){middleRow.addSpacer();}
|
||||
});
|
||||
|
||||
const spaceLastUpdate = new Date(spaceStatus.state.lastchange*1000);
|
||||
const spaceLastUpdateStack = canvasStack.addStack();
|
||||
let spaceLastUpdateLabel = spaceLastUpdateStack.addDate(spaceLastUpdate);
|
||||
spaceLastUpdateLabel.font = Font.mediumSystemFont(6);
|
||||
spaceLastUpdateLabel.applyRelativeStyle();
|
||||
|
||||
const bottomRow = canvasStack.addStack();
|
||||
bottomRow.useDefaultPadding();
|
||||
bottomRow.centerAlignContent();
|
||||
const mumbleLabelStack = bottomRow.addStack();
|
||||
mumbleLabelStack.layoutVertically();
|
||||
const labelMumble = mumbleLabelStack.addText("Mumble");
|
||||
labelMumble.font = Font.regularSystemFont(14);
|
||||
|
||||
let mumbleLastUpdate = new Date(mumbleStatus.last_update * 1000 );
|
||||
const labelMumbleUpdated = mumbleLabelStack.addDate(mumbleLastUpdate);
|
||||
labelMumbleUpdated.font = Font.mediumSystemFont(6);
|
||||
labelMumbleUpdated.applyTimeStyle();
|
||||
|
||||
bottomRow.addSpacer(23);
|
||||
const mumbleValueStack = bottomRow.addStack();
|
||||
const labelMumbleUser = mumbleValueStack.addText(mumbleStatus.connected_users.toString(10));
|
||||
labelMumbleUser.font = Font.boldSystemFont(30);
|
||||
switch(mumbleStatus.connected_users){
|
||||
case 0:
|
||||
labelMumbleUser.textColor = colorMumbleClosed;
|
||||
break;
|
||||
case 1:
|
||||
labelMumbleUser.textColor = colorMumbleLonely;
|
||||
break;
|
||||
default:
|
||||
labelMumbleUser.textColor = colorMumbleOpen;
|
||||
}
|
||||
|
||||
canvasStack.addSpacer(4)
|
||||
|
||||
dateStack = canvasStack.addStack();
|
||||
dateStack.layoutHorizontally();
|
||||
dateStack.bottomAlignContent();
|
||||
dateStack.addSpacer(41);
|
||||
const now = new Date();
|
||||
const timeLabel = dateStack.addDate(now)
|
||||
timeLabel.font = Font.mediumSystemFont(10)
|
||||
timeLabel.centerAlignText()
|
||||
timeLabel.applyTimeStyle()
|
||||
timeLabel.textColor = Color.darkGray()
|
||||
|
||||
return widget;
|
||||
}
|
||||
|
||||
function spaceView(widget, space, lockStatus) {
|
||||
const viewStack = widget.addStack();
|
||||
viewStack.layoutVertically();
|
||||
viewStack.centerAlignContent();
|
||||
|
||||
const spaceName = space.charAt(0).toUpperCase() + space.slice(1)
|
||||
const label = viewStack.addText(spaceName);
|
||||
label.font = Font.regularSystemFont(14);
|
||||
|
||||
const lock = SFSymbol.named("lock." + (lockStatus ? "" : "open.") + "fill");
|
||||
lock.applyFont(Font.systemFont(20));
|
||||
const lockImage = viewStack.addImage(lock.image);
|
||||
lockImage.resizable = false;
|
||||
lockImage.imageSize = new Size(25, 25);
|
||||
if(lockStatus){
|
||||
lockImage.tintColor = colorSpaceClosed;
|
||||
} else {
|
||||
lockImage.tintColor = colorSpaceOpen;
|
||||
}
|
||||
}
|
||||
|
||||
async function getCachedImage(localFilename, url) {
|
||||
let fm = FileManager.local();
|
||||
let dir = fm.cacheDirectory();
|
||||
let path = fm.joinPath(dir, localFilename);
|
||||
if (fm.fileExists(path)) {
|
||||
return fm.readImage(path);
|
||||
} else {
|
||||
let r = new Request(url);
|
||||
try {
|
||||
let returnImage = await r.loadImage();
|
||||
fm.writeImage(path, returnImage);
|
||||
return returnImage;
|
||||
} catch (err) {
|
||||
// return placeholder
|
||||
return SFSymbol.named("photo").image;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function getJSONandCache(localFilename, url){
|
||||
let fm = FileManager.local()
|
||||
let dir = fm.cacheDirectory()
|
||||
let path = fm.joinPath(dir, localFilename)
|
||||
let r = new Request(url)
|
||||
try {
|
||||
var data = await r.loadJSON()
|
||||
fm.writeString(path, JSON.stringify(data, null, 2))
|
||||
var fresh = true;
|
||||
} catch (err) {
|
||||
if (fm.fileExists(path)) {
|
||||
data = JSON.parse(fm.readString(path), null)
|
||||
fresh = false;
|
||||
} else {
|
||||
throw "no data";
|
||||
}
|
||||
}
|
||||
return [data, fresh];
|
||||
}
|
29
chaospott_mumble.py
Executable file
29
chaospott_mumble.py
Executable file
@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8
|
||||
from struct import *
|
||||
import socket, time, datetime, sys, json
|
||||
|
||||
try:
|
||||
host = "mumble.chaospott.de"
|
||||
port = 64738
|
||||
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
s.settimeout(1)
|
||||
|
||||
buf = pack(">iQ", 0, datetime.datetime.now().microsecond)
|
||||
s.sendto(buf, (host, port))
|
||||
|
||||
try:
|
||||
data, addr = s.recvfrom(1024)
|
||||
except socket.timeout:
|
||||
print("%d:NaN:NaN" % (time.time()))
|
||||
sys.exit()
|
||||
|
||||
r = unpack(">bbbbQiii", data)
|
||||
|
||||
version = "%d.%d.%d" % (r[1], r[2], r[3])
|
||||
last_update = int(time.time())
|
||||
output = { "server_version": version, "connected_users": r[5], "max_users": r[6], "bandwidth": r[7], "last_update": last_update}
|
||||
print(json.dumps(output))
|
||||
except:
|
||||
pass
|
Loading…
Reference in New Issue
Block a user