initial commit
This commit is contained in:
		
							
								
								
									
										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
 | 
				
			||||||
		Reference in New Issue
	
	Block a user