Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • kasalehlia/s0infodisplay
  • rohieb/s0infodisplay
  • larsan/s0infodisplay
  • marudor/s0infodisplay
  • thies/s0infodisplay
  • f10sh/s0infodisplay
6 results
Show changes
Commits on Source (56)
{
"presets": ["es2015"]
}
public/
module.exports = {
extends: 'marudor/noReact',
env: {
node: true,
},
globals: {
pad: false,
DOW: false,
},
rules: {
'header/header': 0,
}
}
*.swp
*.log
npm-debug.log
node_modules
__pycache__
env
var express = require('express');
var app = express();
var server = require('http').Server(app);
var io = require('socket.io')(server);
server.listen(8000);
app.use(express.static(__dirname + '/public'));
io.on('connection', function (socket) {
//socket.emit('meta', 'reload');
});
function endsWith(str, suffix) {
return str.indexOf(suffix, str.length - suffix.length) !== -1;
}
// for reasons
DOW = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];
pad = function (n, width, z) {
z = z || '0';
n = n + '';
return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
}
var normalizedPath = require("path").join(__dirname, "modules");
require("fs").readdirSync(normalizedPath).forEach(function(file) {
if (endsWith(file,'.js')) {
try {
require("./modules/" + file)(io);
console.log(file+" loaded");
} catch (e) {
console.log("Error loading "+file);
console.log(e);
}
}
});
import json
import threading
from queue import Queue
from flask import Flask, render_template
from flask_socketio import SocketIO
import logging
log = logging.getLogger('werkzeug')
log.setLevel(logging.ERROR)
app = Flask(__name__, static_url_path='', static_folder='public')
app.config['SECRET_KEY'] = 'Een3ooYiimePood5aeCe8ae0Eihohsh1'
socketio = SocketIO(app, async_mode='threading')
latest_updates = dict()
@socketio.on('ident')
def on_ident(json):
#TODO better dependency resolution for outlets
[socketio.emit('update', upd) for key,upd in latest_updates.items() if key[1] == '.']
[socketio.emit('update', upd) for key,upd in latest_updates.items() if key[1] != '.']
queue = Queue()
def consumer():
print('consumer')
while True:
event, data = queue.get()
socketio.emit(event, data)
latest_updates[(data['module'],data['outlet'])] = data
threading.Thread(target=consumer).start()
import modules
#TODO do not pollute namespace
from modules import *
def emit_factory(mod):
def emit(content, outlet='.'):
queue.put(('update', dict(outlet=outlet, module=mod, content=content)))
return emit
def log_factory(mod):
def log(err):
print('[{}] ERR: {}'.format(mod, err))
return log
for mod_name in modules.__all__:
mod = getattr(modules, mod_name)
print('starting module {}'.format(mod_name))
threading.Thread(target=mod.run, args=(emit_factory(mod_name),log_factory(mod_name))).start()
@app.route('/')
def index():
return app.send_static_file('index.html')
if __name__ == '__main__':
socketio.run(app, host='0.0.0.0', port=8000)
from os.path import dirname, basename, isfile
import glob
modules = glob.glob(dirname(__file__)+"/*.py")
__all__ = [ basename(f)[:-3] for f in modules if isfile(f) and not f.endswith('__init__.py')]
var httpreq = require('httpreq');
var Mustache = require('mustache');
var URL = 'https://status.stratum0.org/status.json';
var TEMPLATE = '';
require('fs').readFile('modules/brand/template.mustache', 'utf-8', function (err, data) {
TEMPLATE = data;
});
var status = {};
function renderStatus () {
return Mustache.render(TEMPLATE, status);
}
function fetchStatus (cb) {
httpreq.get(URL, function (err, res) {
var state = JSON.parse(res.body).state;
cb(state);
});
}
module.exports = function (io) {
function update() {
fetchStatus(function (state) {
if (status.lastchange != state.lastchange) {
var d = new Date(state.lastchange*1000);
state.since = DOW[d.getDay()]+', '+pad(d.getHours(),2)+':'
+pad(d.getMinutes(),2);
status = state;
io.emit('brand', renderStatus());
}
});
}
io.on('connection', function (sock) {
sock.emit('brand', renderStatus());
});
setInterval(update, 2*60*1000); //every 2 minutes
update();
}
import json
import requests
from datetime import datetime
from pystache import render
from time import sleep
URL = 'https://stratum0.org/status/status.json';
def run(emit, log_err):
lastchange = -1
templates = dict()
for name in ['status','template']:
with open('modules/brand/{}.mustache'.format(name), 'r') as f:
templates[name] = f.read()
emit(render(templates['template'], {}))
while True:
try:
r = requests.get(URL)
status = json.loads(r.text)
state = status['state']
if state['lastchange'] != lastchange:
lastchange = state['lastchange']
state['since'] = datetime.fromtimestamp(lastchange).strftime('%a, %H:%M')
emit(render(templates['status'], state), outlet='status')
except Exception as e:
log_err(e)
sleep(20)
Space is {{#open}}<span class="open">open</span> on <span class="person">{{trigger_person}}</span>{{/open}}
{{^open}}<span class="closed">closed</span>{{/open}}
<br>since {{since}}
<br>
<style>
#pa {
margin: auto;
display: flex;
height: 310px;
width: 470px;
}
#ch {
margin: auto; /* Magic! */
max-width: 100%;
max-height: 100%;
}
</style>
<div id="pa">
<img id="ch" src="http://maurudor.de/?rasd={{random}}"></img>
</div>
<img src="/modules/brand/stratum0_logo.svg"><br>
<h3>Space is
{{#open}}<span class="open">open</span> on <span class="person">{{trigger_person}}</span>{{/open}}
{{^open}}<span class="closed">closed</span>{{/open}}
<br>since {{since}}
</h3>
<h3 data-infodisplay-outlet="status"></h3>
var fs = require('fs');
var Mustache = require('mustache');
var XML = require('pixl-xml');
var iconv = require('iconv-lite');
var httpreq = require('httpreq');
/// CONFIG
var CITY = 'Braunschweig';
var STOPS = ['Ludwigstraße','Hamburger Straße'];
var ENTRIES = 6;
/// VARS
var CACHED = [];
var TEMPLATES = {};
fs.readFile('modules/bus/outer.mustache', 'utf-8', function (err, data) {
TEMPLATES.outer = data;
});
fs.readFile('modules/bus/inner.mustache', 'utf-8', function (err, data) {
TEMPLATES.inner = data;
});
/// FUNS
function pad(n, width, z) {
z = z || '0';
n = n + '';
return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
}
function fetchData(stop, cb) {
var url = "http://62.154.206.87/efaws2/default/XML_DM_REQUEST?sessionID=0&requestID=0&language=de&useRealtime=1&coordOutputFormat=WGS84[DD.ddddd]&locationServerActive=1&mode=direct&dmLineSelectionAll=1&depType=STOPEVENTS&useAllStops=1&command=null&type_dm=stop&name_dm="+CITY+' '+stop+"&mId=efa_rc2"
httpreq.get(url, {binary: true}, function (err, res) {
try {
cb(XML.parse(iconv.decode(res.body, 'latin1')));
} catch(e) {
}
});
}
function renderLine(line) {
var image = '';
if (line.charAt(0) == "M") {
image = 'metro.svg';
line = line.substr(1);
} else if (line.length < 3) {
image = 'tram.svg';
} else {
image = 'bus.svg';
}
return '<img src="/modules/bus/'+image+'"> '+line;
}
function getData(stop, count, cb) {
fetchData(stop, function (data) {
var deps = data.itdDepartureMonitorRequest.itdDepartureList.itdDeparture;
cb(deps.slice(0, count).map(function (dep) {
return {
line: dep.itdServingLine.symbol,
renderedLine: renderLine(dep.itdServingLine.symbol),
dir: dep.itdServingLine.direction.replace(CITY, '').trim(),
platform: dep.platform,
mean: dep.itdServingLine.itdNoTrain.name,
day: pad(dep.itdDateTime.itdDate.day,2),
month: pad(dep.itdDateTime.itdDate.month,2),
hour: pad(dep.itdDateTime.itdTime.hour,2),
minute: pad(dep.itdDateTime.itdTime.minute,2)
};
}));
});
}
function update(io, allStopsDoneCb) {
var done = [];
var context = [];
var innerGetData = function (stop, i) {
var ns = normalizeStop(stop);
getData(stop, ENTRIES, function (deps) {
try {
context[i] = {stop: stop, normalizedStop: ns, deps: deps};
io.emit('bus.'+ns, Mustache.render(TEMPLATES.inner, context[i]));
if (done.indexOf(stop) === -1) {
done.push(stop);
if (done.length === STOPS.length) {
allStopsDoneCb();
}
}
CACHED = context;
} catch (e) {console.log(e);}
// calculate when to update next
var d = new Date();
var hour = d.getHours();
var minute = d.getMinutes();
var depHour = deps[0].hour < hour ? deps[0].hour+24 : deps[0].hour;
var diff = (depHour-hour)*60 + (deps[0].minute-minute);
setTimeout(function () {
innerGetData(stop, i);
}, (diff * 60 + (60-d.getSeconds()) % 60) * 1000);
});
}
STOPS.forEach(function (stop, i) {
innerGetData(stop, i);
});
}
function normalizeStop(stop) {
return stop.replace(/[^a-zA-Z0-9_]/g,'');
}
module.exports = function (io) {
update(io, function () {
var pushToClient = function (sock) {
sock.emit('bus', Mustache.render(TEMPLATES.outer, CACHED));
setTimeout(function () {
for (var i in CACHED) {
sock.emit('bus.'+CACHED[i].normalizedStop,
Mustache.render(TEMPLATES.inner, CACHED[i]));
}
}, 3000); //wait a second to let the client process the outlets
};
io.on('connect', pushToClient);
pushToClient(io);
});
}
import re
import requests
import xml.etree.ElementTree as ET
from datetime import datetime
from pystache import render
from time import sleep
CITY = 'Braunschweig';
STOPS = ['Ludwigstraße', 'Hamburger Straße'];
ENTRIES = 6;
def run(emit, log_err):
templates = dict()
for name in ['inner','outer']:
with open('modules/bus/{}.mustache'.format(name), 'r') as f:
templates[name] = f.read()
emit(render(templates['outer'], [normalize_stop(s) for s in STOPS]))
while True:
for stop in STOPS:
try:
ns = normalize_stop(stop)
deps = get_data(stop, ENTRIES)
ctx = {'stop': stop, 'normalizedStop': ns, 'deps': deps}
emit(render(templates['inner'], ctx), outlet=ns)
except Exception as e:
log_err(e)
sleep(60)
def fetch_data(stop):
url = "http://62.154.206.87/efaws2/default/XML_DM_REQUEST?sessionID=0&requestID=0&language=de&useRealtime=1&coordOutputFormat=WGS84[DD.ddddd]&locationServerActive=1&mode=direct&dmLineSelectionAll=1&depType=STOPEVENTS&useAllStops=1&command=null&type_dm=stop&name_dm={} {}&mId=efa_rc2".format(CITY, stop);
r = requests.get(url)
return r.text
def get_data(stop, count):
t = ET.fromstring(fetch_data(stop))
deps = t.find('itdDepartureMonitorRequest').find('itdDepartureList')
out = []
for dep in list(deps)[:count]:
line = dep.find('itdServingLine')
time = dep.find('itdDateTime').find('itdTime')
out.append({
'line': line.get('symbol'),
'renderedLine': render_line(line.get('symbol')),
'dir': line.get('direction').replace(CITY, '').strip(),
'platform': dep.get('platform'),
'hour': time.get('hour').zfill(2),
'minute': time.get('minute').zfill(2)
})
return out
def render_line(line):
image = ''
if len(line) < 3:
image = 'tram.svg'
else:
image = 'bus.svg'
return '<img src="/modules/bus/{}"> {}'.format(image, line)
def normalize_stop(stop):
return re.sub('[^a-zA-Z0-9_]', '', stop)
......@@ -8,6 +8,6 @@
<col>
</colgroup>
{{#.}}
<tbody data-infodisplay-outlet="{{normalizedStop}}"></tbody>
<tbody data-infodisplay-outlet="{{.}}"></tbody>
{{/.}}
</table>
var httpreq = require('httpreq');
var ical = require('ical.js');
var Mustache = require('mustache');
var URL = 'https://stratum0.org/kalender/termine.ics';
var TEMPLATE = '';
require('fs').readFile('modules/calendar/template.mustache', 'utf-8', function (err, data) {
TEMPLATE = data;
});
var CALENDAR;
function pad(n, width, z) {
z = z || '0';
n = n + '';
return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
}
function parseDate(s) {
var i = parseInt;
if (s.indexOf('T') > -1) {
return new Date(i(s.substr(0,4)), i(s.substr(4,2))-1, i(s.substr(6,2)),
i(s.substr(9,2)), i(s.substr(11,2)), i(s.substr(13,2)));
} else {
return new Date(i(s.substr(0,4)), i(s.substr(4,2))-1, i(s.substr(6,2)));
}
}
function getData(count, cb) {
httpreq.get(URL, function (err, res) {
var ics = res.body.replace(/[0-9]{8}T[0-9]{6}/g, '$&Z');
var cal = new ical.Component(ical.parse(ics));
var rawEvents = cal.getAllSubcomponents('vevent').map(function (raw) {
return new ical.Event(raw);
});
var events = [];
var ev, p = function (p) {return ev.component.getFirstPropertyValue(p);};
for (var i in rawEvents) {
ev = rawEvents[i];
if (ev.isRecurring()) {
var iter = ev.iterator();
var duration = p('dtend').toUnixTime()-p('dtstart').toUnixTime();
for (var i = 0; i < 100; i++) {
var next = iter.next();
if (next === undefined) {break;}
var start = next.toUnixTime();
events.push({
title: p('summary'),
start: start,
end: start + duration
});
}
} else {
events.push({
title: p('summary'),
start: p('dtstart').toUnixTime(),
end: p('dtend').toUnixTime(),
});
}
}
events.sort(function (a, b) {
return a.start - b.start;
});
var threshold = Date.now()/1000 - 5*60*60; //show 5 hours ago
var i;
for (i in events) {
if (events[i].end > threshold) {
i = parseInt(i);
break;
}
}
var now = new Date();
cb(events.slice(i, i+count).map(function (ev) {
var start = new Date(ev.start*1000);
if (start.getMonth() == now.getMonth() && start.getDate() == now.getDate()) {
ev.startRendered = pad(start.getHours(),2)+':'+pad(start.getMinutes(),2);
} else {
ev.startRendered = DOW[start.getDay()]+', '+pad(start.getDate(),2)
+'.'+pad(start.getMonth(),2)+'. '+pad(start.getHours(),2)
+':'+pad(start.getMinutes(),2);
}
var end = new Date(ev.end*1000);
if (ev.end - ev.start >= 60*60*24) {
ev.endRendered = DOW[end.getDay()]+' '+pad(end.getHours(),2)
+':'+pad(end.getMinutes(),2);
} else {
ev.endRendered = pad(end.getHours(),2)+':'+pad(end.getMinutes(),2);
}
var dur = Math.floor((ev.end-ev.start)/60);
ev.duration = pad(Math.floor(dur/60),2)+':'+pad((dur%60),2)
if (start.getTime() < now.getTime()) {
if (end.getTime() < now.getTime()) {
ev.past = true;
} else {
ev.now = true;
}
}
return ev
}));
});
}
module.exports = function (io) {
io.on('connect', function (sock) {
sock.emit('calendar', Mustache.render(TEMPLATE, CALENDAR));
});
function update() {
getData(8, function (data) {
CALENDAR = data;
io.emit('calendar', Mustache.render(TEMPLATE, CALENDAR));
});
}
update();
setInterval(update, 600000);
}
import re
import requests
from datetime import date, datetime, timedelta
from pystache import render
from time import sleep
from arrow import Arrow
import ics
import dateutil.rrule as rrule
URL = 'https://stratum0.org/kalender/termine.ics';
def run(emit, log_err):
template = None
with open('modules/calendar/template.mustache', 'r') as f:
template = f.read()
while True:
try:
r = requests.get(URL)
cal = _fix_and_parse(r.text)
one_day = timedelta(days=1)
day = date.today()
soon = []
for _ in range(8*7):
soon.extend(cal.events.on(Arrow.fromdate(day)))
day += one_day
emit(render(template, [_process_event(ev) for ev in soon][:8]))
except Exception as e:
log_err(e)
sleep(60)
def _process_event(ev):
now = datetime.now()
begin = ev.begin.datetime.replace(tzinfo=None)
end = ev.end.datetime.replace(tzinfo=None)
e = dict(title=ev.name)
if end < now:
e['past'] = True
elif begin <= now <= end:
e['now'] = True
if begin.date() == date.today():
e['startRendered'] = begin.strftime('%H:%M')
else:
e['startRendered'] = begin.strftime('%A, %d.%m. %H:%M')
if ev.duration < timedelta(days=1):
e['endRendered'] = end.strftime('%H:%M')
else:
e['endRendered'] = end.strftime('%A %H:%M')
e['duration'] = ':'.join(str(ev.duration).split(':')[:2])
return e
def _fix_and_parse(ical):
ical = ical.replace(';VALUE=DATE-TIME', '')
cal = ics.Calendar(ical)
# add recurring events since the stupid lib does not handle that
start_day = datetime.now() - timedelta(hours=5)
limit_day = datetime.today() + timedelta(days=8*7)
add = []
for ev in cal.events:
m = re.search('\nRRULE:([^\n]+)\n', str(ev))
if m:
dtstart = ev.begin.datetime.replace(tzinfo=None)
rule = rrule.rrulestr(m.group(1), dtstart=dtstart)
duration = ev.duration
for occ in rule.xafter(start_day):
if occ > limit_day: break
nev = ev.clone()
nev.end = occ+duration
nev.begin = occ
add.append(nev)
[cal.events.append(e) for e in add]
return cal
function pad(n, width, z) {
z = z || '0';
n = n + '';
return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
}
function date() {
var d = new Date();
return DOW[d.getDay()]+', '+
pad(d.getDate(),2)+'.'+pad(d.getMonth()+1,2)+'.'+d.getFullYear()+' '+
pad(d.getHours(),2)+':'+pad(d.getMinutes(),2)+':'+pad(d.getSeconds(),2);
}
module.exports = function (io) {
setInterval(function () {
io.emit('date', '<div align="right"><h2>'+date()+'&nbsp;</h2></div>');
}, 1000);
}
import time
from datetime import datetime
DATE_FORMAT = "%A %d.%m.%Y %H:%M:%S"
def run(emit, log_err):
while True:
try:
start = time.time()
out = datetime.now().strftime(DATE_FORMAT)
out = '<div align="right"><h2>{}</h2></div>'.format(out)
emit(out)
except Exception as e:
log_err(e)
time.sleep(1-(time.time()-start))
import json
import requests
from datetime import datetime
from pystache import render
from time import sleep
URL = 'http://shiny.tinyhost.de/php/getdata.php?time=1&id[]={}'
def _fetch_data(id):
url = URL.format(id)
r = requests.get(url)
data = r.text.split('\n')
out = []
for line in data[1:]:
l = line.split(',')
if len(l) == 2:
date = datetime.strptime(l[0], '%Y/%m/%d %H:%M:%S')
out.append((int(date.timestamp()*1000), float(l[1])))
return json.dumps(out)
def run(emit, log_err):
lastchange = -1
templates = dict()
for name in ['data','main']:
with open('modules/diagrams/{}.mustache'.format(name), 'r') as f:
templates[name] = f.read()
emit(render(templates['main'], {}))
while True:
try:
power = _fetch_data(1)
devices = _fetch_data(4)
emit(render(templates['data'], dict(power=power, devices=devices)), outlet='data')
except Exception as e:
log_err(e)
sleep(30)
<script>
var power = {{power}};
var devices = {{devices}};
$.plot("#diagrams-container", [
{ data: power, label: "Power consumption (W)", color: '#b58900' },
{ data: devices, label: "Network devices", yaxis: 2, color: '#268bd2' }
], {
xaxes: [ { mode: "time" } ],
yaxes: [ { min: 0 }, {
alignTicksWithAxis: 0,
position: 'right',
min: 0
} ],
legend: { position: "sw", backgroundColor: '#eee8d5' }
});
</script>