joa dateien geadded

This commit is contained in:
Malte Münch 2020-02-12 01:37:09 +01:00
parent 8cda43f92f
commit 0788b8eafc
10 changed files with 921 additions and 0 deletions

15
create_map/README.md Normal file
View File

@ -0,0 +1,15 @@
# create_map
Dieses kleine Repo beinhaltet ein Pythonskript welches aus dem Spacedirectory
spaces auswählt und sie dann 1. auf einer SVG-Karte darstellt die man dann
schön Lasercuttern kann und 2. in eine conf.yml tut die dann vom
spacepanel_aggregator genutzt werden kann.
Wenn ich meinen Code noch halbwegs richtig verstehe läuft das Programm
folgendermaßen:
1. Alle Spaces aus dem Spacedirectory runterladen
2. Check auf Erreichbarkeit des API Endpoints
3. Im API-Output checken ob der Doorstatus sinnvolle Dinge ausgibt
4. Checken ob der Space in Germany liegt
5. Spaces die zu nah aneinander liegen werden automatically gemerged (zum
Beispiel in Berlin oder Hamburg)

142
create_map/get_coordinates.py Executable file
View File

@ -0,0 +1,142 @@
#!/usr/bin/python
import requests
import json
import pickle
import os.path
import svgwrite
import math
import sys
import concurrent.futures
URL = "https://directory.spaceapi.io/"
NORTHERNMOST = 55.05
EASTERNMOST = 15.033333
SOUTHERNMOST = 47.270108
WESTERNMOST = 5.866667
threshold = 0.10
YSPAN = NORTHERNMOST - SOUTHERNMOST
XSPAN = EASTERNMOST - WESTERNMOST
locations = {}
ignorelist = ["Chaostreff Salzburg", "DevLoL", "CCC Basel", "Chaostreff Zürich",
"ChaosStuff", "Level2", "Bastli", "Maakplek", "TkkrLab", "Hack42",
"Hackerspace Nijmegen", "TDvenlo", "ACKspace"]
def dist(n1, n2):
y = n1[0] - n2[0]
x = n1[1] - n2[1]
return math.sqrt(math.pow(x, 2) + math.pow(y, 2))
def conflict(targetlist, node):
returner = None
for element in targetlist:
if dist(node, targetlist[element]) < threshold:
returner = element
continue
return returner
def merge(n1, n2):
lat = (n1[0] + n2[0]) / 2
lon = (n1[1] + n2[1]) / 2
returner = []
returner.append(lat)
returner.append(lon)
for i in range(2, len(n1)):
returner.append(n1[i])
for i in range(2, len(n2)):
returner.append(n2[i])
return returner
def get_space_location(space):
try:
spacerequest = requests.get(url=data[space], timeout=1)
spacedata = spacerequest.json()
except requests.exceptions.RequestException as _:
return
except json.JSONDecodeError as _:
return
if "location" in spacedata:
if "lat" in spacedata["location"]:
lat = spacedata["location"]["lat"]
lon = spacedata["location"]["lon"]
return [float(lat), float(lon), data[space]]
if os.path.isfile('locations.bin'):
print("using offline data...", file=sys.stderr)
with open("locations.bin", "rb") as f:
locations = pickle.load(f)
else:
print("offline data not available, downloading...", file=sys.stderr)
r = requests.get(url=URL)
data = r.json()
with concurrent.futures.ThreadPoolExecutor(max_workers=16) as executor:
futures = {}
for space in data:
futures[space] = executor.submit(get_space_location, space)
for space in data:
location = futures[space].result()
if location:
locations[space] = location
print("done loading space data", file=sys.stderr)
for space in locations:
print(space + " " + str(locations[space]))
with open("locations.bin", "wb") as f:
pickle.dump(locations, f, pickle.HIGHEST_PROTOCOL)
print("Removing non-german points...", file=sys.stderr)
german_locations = locations.copy()
for space in locations:
if locations[space][0] > NORTHERNMOST:
del german_locations[space]
elif locations[space][0] < SOUTHERNMOST:
del german_locations[space]
elif locations[space][1] < WESTERNMOST:
del german_locations[space]
elif locations[space][1] > EASTERNMOST:
del german_locations[space]
for space in ignorelist:
del german_locations[space]
finallist = {}
while german_locations:
n1 = next(iter(german_locations))
conflictnode = conflict(finallist, german_locations[n1])
if conflictnode == None:
finallist.update({n1: german_locations[n1]})
del german_locations[n1]
else:
mergenode = merge(german_locations[n1], finallist[conflictnode])
del german_locations[n1]
del finallist[conflictnode]
german_locations.update(
{"MERGED: " + n1 + " " + conflictnode: mergenode})
for space in finallist:
print(str(finallist[space][0]) + " " + str(finallist[space][1]) + " {" +
space + "}")
dwg = svgwrite.Drawing('dots.svg', profile='tiny')
dwg.add(svgwrite.image.Image(href="Karte_Deutschland.svg", size=(586, 793)))
for space in finallist:
ypoint = (793 - (((finallist[space][0] - SOUTHERNMOST) / YSPAN) * 793))
xpoint = ((finallist[space][1] - WESTERNMOST) / XSPAN) * 586
dwg.add(dwg.circle(center=(xpoint, ypoint), r=5, fill='green'))
dwg.add(dwg.text(space, insert=(xpoint + 5, ypoint), font_size=5))
dwg.save()
ledconf = open("conf.yml", "w")
i = 0
for space in finallist:
ledconf.write("#led" + str(i) + ":\n-\n")
for u in range(2, len(finallist[space])):
ledconf.write(" - " + finallist[space][u] + "\n")
i = i + 1

79
show_leds/show_leds.ino Normal file
View File

@ -0,0 +1,79 @@
#include <ArduinoJson.h>
#include <Adafruit_NeoPixel.h>
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#define PIN 2
#define NUMPIXELS 12
const char* ssid = "CTDO-LEGACY";
const char* password = "******";
const char* apiEndpoint = "http://spacepanel.stablerock.de/leds";
const int pollInterval = 10000;
HTTPClient http;
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
int delayval = 500;
void setLED(int i, String value) {
String value_short = value.substring(1);
char charbuf[8];
value_short.toCharArray(charbuf,8);
long int rgb = strtol(charbuf,0,16); //=>rgb=0x001234FE;
byte r=(byte)(rgb>>16);
byte g=(byte)(rgb>>8);
byte b=(byte)(rgb);
Serial.print(value_short);
Serial.print("-> ");
Serial.print(r);
Serial.print(", ");
Serial.print(g);
Serial.print(", ");
Serial.println(b);
pixels.setPixelColor(i, pixels.Color(r/255,g/255,b/255));
pixels.show();
}
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
Serial.println(WiFi.macAddress());
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting...");
}
Serial.println("Connected.");
pixels.begin();
}
void loop() {
Serial.println("Sending request...");
http.begin(apiEndpoint);
int httpCode = http.GET();
Serial.println("Success.");
if (httpCode == 200) {
String json = http.getString();
const size_t capacity = JSON_ARRAY_SIZE(NUMPIXELS) + 510;
DynamicJsonDocument doc(capacity);
deserializeJson(doc, json);
for (int i = 0; i < NUMPIXELS; i++) {
Serial.print("LED" + String(i) + ": ");
const char* element = doc[i];
String value = String(element);
setLED(i, value);
Serial.print("LED" );
Serial.print(i);
Serial.print("Color: ");
Serial.println(value);
}
} else {
Serial.println("Error: Statuscode" + httpCode);
}
delay(pollInterval);
}

View File

@ -0,0 +1,9 @@
module repos.ctdo.de/mamu/spacepanel_aggregator
go 1.13
require (
github.com/gin-gonic/gin v1.5.0
github.com/valyala/fastjson v1.4.5
gopkg.in/yaml.v2 v2.2.8
)

View File

@ -0,0 +1,47 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.5.0 h1:fi+bqFAx/oLK54somfCtEZs9HeH1LHVoEPUgARpTqyc=
github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do=
github.com/go-playground/locales v0.12.1 h1:2FITxuFt/xuCNP1Acdhv62OzaCiviiE4kotfhkmOqEc=
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
github.com/go-playground/universal-translator v0.16.0 h1:X++omBR/4cE2MNg91AoC3rmGrCjJ8eAeUP/K/EKx4DM=
github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/leodido/go-urn v1.1.0 h1:Sm1gr51B1kKyfD2BlRcLSiEkffoG96g6TPv6eRoEiB8=
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/valyala/fastjson v1.4.5 h1:uSuLfXk2LzRtzwd3Fy5zGRBe0Vs7zhs11vjdko32xb4=
github.com/valyala/fastjson v1.4.5/go.mod h1:nV6MsjxL2IMJQUoHDIrjEI7oLyeqK6aBD7EFWPsvP8o=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
gopkg.in/go-playground/validator.v9 v9.29.1 h1:SvGtYmN60a5CVKTOzMSyfzWDeZRxRuGvRQyEAKbw1xc=
gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -0,0 +1,76 @@
package spacepanel_aggregator
import (
"crypto/tls"
"errors"
"fmt"
"io/ioutil"
"net/http"
"time"
"github.com/valyala/fastjson"
)
func PollWorker(url string, led int) {
for {
err := Worker(url)
if err != nil {
SetSpaceState(url, Unknown)
fmt.Println("LED", led, "URL:", url, err.Error())
}
time.Sleep(sleeptime)
}
}
var (
insecureTransport = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client = &http.Client{
Transport: insecureTransport,
}
)
func Worker(url string) error {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return err
}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
value, err := fastjson.ParseBytes(data)
if err != nil {
return err
}
if !value.Exists("state", "open") {
return errors.New("no space state existing")
}
state := value.GetBool("state", "open")
if state {
SetSpaceState(url, Open)
} else {
SetSpaceState(url, Close)
}
return nil
}
func SetSpaceState(url string, s State) {
lock.Lock()
spacestates[url] = s
lock.Unlock()
}

View File

@ -0,0 +1,86 @@
package spacepanel_aggregator
import (
"fmt"
"github.com/gin-gonic/gin"
yaml "gopkg.in/yaml.v2"
io "io/ioutil"
"net/http"
"sync"
"time"
)
var listen = ":8080"
var conffile = "conf.yml"
var sleeptime time.Duration = 60000000000 // nanoseconds
var leds [][]string
var spacestates map[string]State
var lock = sync.RWMutex{}
func SetConf(s string) {
conffile = s
}
func SetIf(s string) {
listen = s
}
func Start() {
fmt.Println("Welcome to Spacepanel Aggregator!\n")
fmt.Println("Listen Interface: ", listen)
fmt.Println("Config-File: ", conffile)
bytes, err := io.ReadFile(conffile)
if err != nil {
ce(err)
}
err = yaml.Unmarshal(bytes, &leds)
if err != nil {
ce(err)
panic("An error occured while parsing the conffile, quitting...")
}
spacestates = make(map[string]State)
for i := 0; i < len(leds); i++ {
for j := 0; j < len(leds[i]); j++ {
spacestates[leds[i][j]] = Unknown
go PollWorker(leds[i][j], i)
}
}
fmt.Println("Loaded", len(leds), "LED-configs and", len(spacestates), "spaces.")
r := setupRouter()
_ = r.Run(listen)
}
func getBestState(states []string) State {
returner := Unknown
for i := 0; i < len(states); i++ {
if spacestates[states[i]] < returner {
returner = spacestates[states[i]]
}
}
return returner
}
func getLedStates(c *gin.Context) {
returner := make([]string, len(leds))
for i := 0; i < len(leds); i++ {
returner[i] = colors[getBestState(leds[i])]
}
c.JSON(http.StatusOK, returner)
}
func setupRouter() *gin.Engine {
gin.SetMode(gin.ReleaseMode)
r := gin.Default()
// Ping test
//r.GET("/leds", getLedStates())
r.GET("/leds", getLedStates)
return r
}
func ce(err error) {
if err != nil {
fmt.Println("ERROR: ", err.Error())
}
}

View File

@ -0,0 +1,44 @@
package main
import (
"fmt"
"os"
sa "repos.ctdo.de/mamu/spacepanel_aggregator"
)
const helptext string = "spacepanel_aggregator usage: \n\n -l IF:port default: *:8080 \n -f config-file default: conf.yml \n -h print help and exit\n"
func main() {
conf := true
for i := 1; i < len(os.Args); i++ {
switch os.Args[i] {
case "-l":
i++
if i < len(os.Args) {
sa.SetIf(os.Args[i])
} else {
conf = false
}
case "-f":
i++
if i < len(os.Args) {
sa.SetConf(os.Args[i])
} else {
conf = false
}
default:
conf = false
i = len(os.Args)
}
}
if conf {
sa.Start()
} else {
printHelp()
}
}
func printHelp() {
fmt.Printf(helptext)
}

View File

@ -0,0 +1,184 @@
#led0:
-
- https://www.devtal.de/api/
#led1:
-
- https://www.binary-kitchen.de/spaceapi.php
#led2:
-
- https://status.aachen.ccc.de/spaceapi
#led3:
-
- https://schalter.ccchb.de/spaceapi.json
#led4:
-
- https://api.koeln.ccc.de
#led5:
-
- https://www.ccc-mannheim.de/spaceapi/spaceapi.json
#led6:
-
- http://cccfr.de/status/spaceapi.py
#led7:
-
- http://chaos-consulting.de/api/space.api
#led8:
-
- https://status.chaospott.de/status.json
#led9:
-
- https://chaoschemnitz.de/chch.json
#led10:
-
- https://www.ccc-p.org/spaceapi.json
#led11:
-
- http://doorstatus.c3re.de/status/json
#led12:
-
- http://status.ctdo.de/api/spaceapi/v13
#led13:
-
- https://status.diyww.de/status.json
#led14:
-
- http://club.entropia.de/spaceapi
#led15:
-
- https://fablab.fau.de/spaceapi/
#led16:
-
- https://spaceapi.futev.de/spaceapi.json
#led17:
-
- https://freieslabor.org/api/info
#led18:
-
- https://hackerspace-bielefeld.de/spacestatus/status.json
#led19:
-
- https://hacklabor.de/api/space/v1/
#led20:
-
- http://spaceapi.hacksaar.de/status.json
#led21:
-
- https://status.hasi.it/spaceapi
#led22:
-
- https://status.kraut.space/api
#led23:
-
- http://status.leinelab.org/api/spaceapi.json
#led24:
-
- http://status.mainframe.io/api/spaceInfo
#led25:
-
- http://spaceapi.n39.eu/json
#led26:
-
- http://netzladen.org/api/status.json
#led27:
-
- https://api.nerd2nerd.org/status.json
#led28:
-
- https://cccgoe.de/spaceapi.php
#led29:
-
- http://api.openlab-augsburg.de/data.json
#led30:
-
- https://werkraum.freiraumzittau.de/spaceapi/13/
#led31:
-
- https://spaceapi.reaktor23.org
#led32:
-
- http://status.stratum0.org/status.json
#led33:
-
- https://api.warpzone.ms/spaceapi
#led34:
-
- https://hsmr.cc/spaceapi.json
#led35:
-
- https://status.bckspc.de/spacestatus.php
#led36:
-
- http://stats.bytewerk.org/status.json
#led37:
-
- https://api.flipdot.org/
#led38:
-
- https://spaceapi.hackzogtum-coburg.de
#led39:
-
- https://state.maglab.space/spaceapi.json
#led40:
-
- http://nobreakspace.org/status/spaceapi.json
#led41:
-
- https://bodensee.space/spaceapi/see-base.json
#led42:
-
- https://api.shackspace.de/v1/spaceapi
#led43:
-
- https://verschwoerhaus.de/feed/spaceapi
#led44:
-
- https://vspace.one/spaceapi.json
#led45:
-
- https://keinanschluss.un-hack-bar.de/spaceapi.json
#led46:
-
- https://www.hackerspace-sw.de/spaceapi.json
#led47:
-
- https://hamburg.ccc.de/dooris/status.json
- http://blog.attraktor.org/spaceapi/spaceapi.json
#led48:
-
- https://status.makerspace-erfurt.de/status.json
- http://status.bytespeicher.org/status.json
#led49:
-
- http://status.munichmakerlab.de/spaceapi.php
- http://api.muc.ccc.de/spaceapi.json
#led50:
-
- http://api.terminal21.de
- http://api.terminal21.de/status_ebk.json
#led51:
-
- https://fnord.istsystemrelevant.de/spaceapi.json
- https://chaosdorf.de/space_api.json
#led52:
-
- http://spaceapi.nordlab-ev.de
- https://api.chaostreff-flensburg.de/
#led53:
-
- http://www.space-left.org/spaceapi13.json
- https://das-labor.org/status/api
#led54:
-
- http://www.turmlabor.de/spaces.api
- https://www.c3d2.de/spaceapi.json
#led55:
-
- http://spaceapi.k4cg.org/spaceapi.json
- http://api.fablab-nuernberg.de/spaceapi.php
- https://status.nerdberg.de/api/space
#led56:
-
- https://x-hain.de/spaceapi-0.13.json
- http://www.c-base.org/status.json
- https://spaceapi.motionlab.berlin/
- https://spaceapi.afra-berlin.de/v1/status.json

View File

@ -0,0 +1,239 @@
package spacepanel_aggregator
type State int
const (
Open = State(iota)
Close
Outdated
Unknown
)
var colors = [4]string{"#00ff00", "#ff0000", "#0000ff", "#000000"}
type V13 struct {
API string `json:"api"`
Cache struct {
Schedule string `json:"schedule"`
} `json:"cache"`
Cam []string `json:"cam"`
Contact struct {
Email string `json:"email"`
Facebook string `json:"facebook"`
Foursquare string `json:"foursquare"`
Google struct {
Plus string `json:"plus"`
} `json:"google"`
Identica string `json:"identica"`
Irc string `json:"irc"`
IssueMail string `json:"issue_mail"`
Jabber string `json:"jabber"`
Keymasters []struct {
Email string `json:"email"`
IrcNick string `json:"irc_nick"`
Name string `json:"name"`
Phone string `json:"phone"`
Twitter string `json:"twitter"`
} `json:"keymasters"`
Ml string `json:"ml"`
Phone string `json:"phone"`
Sip string `json:"sip"`
Twitter string `json:"twitter"`
} `json:"contact"`
Events []struct {
Extra string `json:"extra"`
Name string `json:"name"`
Timestamp float64 `json:"timestamp"`
Type string `json:"type"`
} `json:"events"`
Feeds struct {
Blog struct {
Type string `json:"type"`
URL string `json:"url"`
} `json:"blog"`
Calendar struct {
Type string `json:"type"`
URL string `json:"url"`
} `json:"calendar"`
Flickr struct {
Type string `json:"type"`
URL string `json:"url"`
} `json:"flickr"`
Wiki struct {
Type string `json:"type"`
URL string `json:"url"`
} `json:"wiki"`
} `json:"feeds"`
IssueReportChannels []string `json:"issue_report_channels"`
Location struct {
Address string `json:"address"`
Lat float64 `json:"lat"`
Lon float64 `json:"lon"`
} `json:"location"`
Logo string `json:"logo"`
Projects []string `json:"projects"`
RadioShow []struct {
End string `json:"end"`
Name string `json:"name"`
Start string `json:"start"`
Type string `json:"type"`
URL string `json:"url"`
} `json:"radio_show"`
Sensors struct {
AccountBalance []struct {
Description string `json:"description"`
Location string `json:"location"`
Name string `json:"name"`
Unit string `json:"unit"`
Value float64 `json:"value"`
} `json:"account_balance"`
Barometer []struct {
Description string `json:"description"`
Location string `json:"location"`
Name string `json:"name"`
Unit string `json:"unit"`
Value float64 `json:"value"`
} `json:"barometer"`
BeverageSupply []struct {
Description string `json:"description"`
Location string `json:"location"`
Name string `json:"name"`
Unit string `json:"unit"`
Value float64 `json:"value"`
} `json:"beverage_supply"`
DoorLocked []struct {
Description string `json:"description"`
Location string `json:"location"`
Name string `json:"name"`
Value bool `json:"value"`
} `json:"door_locked"`
Humidity []struct {
Description string `json:"description"`
Location string `json:"location"`
Name string `json:"name"`
Unit string `json:"unit"`
Value float64 `json:"value"`
} `json:"humidity"`
NetworkConnections []struct {
Description string `json:"description"`
Location string `json:"location"`
Machines []struct {
Mac string `json:"mac"`
Name string `json:"name"`
} `json:"machines"`
Name string `json:"name"`
Type string `json:"type"`
Value float64 `json:"value"`
} `json:"network_connections"`
PeopleNowPresent []struct {
Description string `json:"description"`
Location string `json:"location"`
Name string `json:"name"`
Names []string `json:"names"`
Value float64 `json:"value"`
} `json:"people_now_present"`
PowerConsumption []struct {
Description string `json:"description"`
Location string `json:"location"`
Name string `json:"name"`
Unit string `json:"unit"`
Value float64 `json:"value"`
} `json:"power_consumption"`
Radiation struct {
Alpha []struct {
ConversionFactor float64 `json:"conversion_factor"`
DeadTime float64 `json:"dead_time"`
Description string `json:"description"`
Location string `json:"location"`
Name string `json:"name"`
Unit string `json:"unit"`
Value float64 `json:"value"`
} `json:"alpha"`
Beta []struct {
ConversionFactor float64 `json:"conversion_factor"`
DeadTime float64 `json:"dead_time"`
Description string `json:"description"`
Location string `json:"location"`
Name string `json:"name"`
Unit string `json:"unit"`
Value float64 `json:"value"`
} `json:"beta"`
BetaGamma []struct {
ConversionFactor float64 `json:"conversion_factor"`
DeadTime float64 `json:"dead_time"`
Description string `json:"description"`
Location string `json:"location"`
Name string `json:"name"`
Unit string `json:"unit"`
Value float64 `json:"value"`
} `json:"beta_gamma"`
Gamma []struct {
ConversionFactor float64 `json:"conversion_factor"`
DeadTime float64 `json:"dead_time"`
Description string `json:"description"`
Location string `json:"location"`
Name string `json:"name"`
Unit string `json:"unit"`
Value float64 `json:"value"`
} `json:"gamma"`
} `json:"radiation"`
Temperature []struct {
Description string `json:"description"`
Location string `json:"location"`
Name string `json:"name"`
Unit string `json:"unit"`
Value float64 `json:"value"`
} `json:"temperature"`
TotalMemberCount []struct {
Description string `json:"description"`
Location string `json:"location"`
Name string `json:"name"`
Value float64 `json:"value"`
} `json:"total_member_count"`
Wind []struct {
Description string `json:"description"`
Location string `json:"location"`
Name string `json:"name"`
Properties struct {
Direction struct {
Unit string `json:"unit"`
Value float64 `json:"value"`
} `json:"direction"`
Elevation struct {
Unit string `json:"unit"`
Value float64 `json:"value"`
} `json:"elevation"`
Gust struct {
Unit string `json:"unit"`
Value float64 `json:"value"`
} `json:"gust"`
Speed struct {
Unit string `json:"unit"`
Value float64 `json:"value"`
} `json:"speed"`
} `json:"properties"`
} `json:"wind"`
} `json:"sensors"`
Space string `json:"space"`
Spacefed struct {
Spacenet bool `json:"spacenet"`
Spacephone bool `json:"spacephone"`
Spacesaml bool `json:"spacesaml"`
} `json:"spacefed"`
State struct {
Icon struct {
Closed string `json:"closed"`
Open string `json:"open"`
} `json:"icon"`
Lastchange float64 `json:"lastchange"`
Message string `json:"message"`
Open interface{} `json:"open"`
TriggerPerson string `json:"trigger_person"`
} `json:"state"`
Stream struct {
M4 string `json:"m4"`
Mjpeg string `json:"mjpeg"`
Ustream string `json:"ustream"`
} `json:"stream"`
URL string `json:"url"`
}