Commit 6e47190d authored by loelkes's avatar loelkes
Browse files

WIP

parent ea340750
......@@ -40,5 +40,5 @@
]
},
"api_url": "https://api.frickelfunk.net/yanic/meshviewer/nodes.json",
"node_id": "c46e1ffe5248"
"node_id": "6c98eb300008"
}
{
"firstseen": "2019-09-20T02:03:31+0200",
"lastseen": "2019-10-05T15:03:29+0200",
"flags": {
"online": true,
"gateway": false
},
"statistics": {
"node_id": "6c98eb300008",
"clients": 0,
"rootfs_usage": 0.0974,
"loadavg": 0.09,
"memory_usage": 0.37309840169458885,
"uptime": 351981.01,
"idletime": 342112.25,
"gateway": "fc:e5:17:0a:03:80",
"gateway6": "fc:cb:ff:00:0a:03",
"processes": {
"total": 57,
"running": 2
},
"mesh_vpn": {
"groups": {
"backbone": {
"peers": {
"alb0": null,
"alb1": null,
"alb2": {
"established": 263134.264
},
"alb3": null
},
"groups": null
}
}
},
"traffic": {
"tx": {
"bytes": 42077985,
"packets": 331155,
"dropped": 112
},
"rx": {
"bytes": 366651931,
"packets": 576347
},
"forward": {
"bytes": 370,
"packets": 5
},
"mgmt_tx": {
"bytes": 599254740,
"packets": 5368354
},
"mgmt_rx": {
"bytes": 177532820,
"packets": 2106432
}
}
},
"nodeinfo": {
"node_id": "6c98eb300008",
"network": {
"mac": "6c:98:eb:30:00:08",
"addresses": [
"2001:678:6e3:10b0:6e98:ebff:fe30:8",
"fe80::6e98:ebff:fe30:8"
],
"mesh": {
"bat0": {
"interfaces": {
"wireless": [
"4a:11:4f:2e:cc:75",
"4a:11:4f:2e:cc:71"
],
"other": [
"4a:11:4f:2e:cc:73"
],
"tunnel": [
"4a:11:4f:2e:cc:77"
]
}
}
},
"mesh_interfaces": null
},
"owner": null,
"system": {
"site_code": "ffka",
"domain_code": "kakr_76327"
},
"hostname": "ffka-Seltenbach-Sued",
"location": {
"longitude": 8.524798,
"latitude": 49.004719
},
"software": {
"autoupdater": {
"enabled": true,
"branch": "beta"
},
"batman-adv": {
"version": "openwrt-2018.1-8",
"compat": 15
},
"babeld": {},
"fastd": {
"enabled": true,
"version": "v18"
},
"firmware": {
"base": "gluon-v2018.2.3",
"release": "0.7.5-beta.0-20190915"
},
"status-page": {
"api": 0
}
},
"hardware": {
"nproc": 1,
"model": "OCEDO Koala"
},
"vpn": false
}
}
#!/usr/bin/python
# -*- coding: utf-8 -*-
class FreifunkNode(object):
def __init__(self, data):
self.data = data
# Easier handline og the YANIC API of Freifunk.
from http_API import API
import logging
logger = logging.getLogger(__name__)
class Freifunk(object):
def __init__(self):
self.node_id = 0
self.node_data = False
self.human_readable = True
self.supported_versions = [2]
def update(self, data, id):
self.data = data
self.select(id)
def check_version(self):
if self.data['data']['version'] not in self.supported_versions:
return False
else:
return True
def select(self, id):
self.node_id = id
self.node_data = next(node for node in self.data['nodes'] \
if node['nodeinfo']['node_id'] == self.node_id)
def select(self, id = False):
if id: self.node_id = id
self.node_data = next(node for node in self.data['nodes'] if node['nodeinfo']['node_id'] == self.node_id)
# From https://stackoverflow.com/questions/1094841/reusable-library-to-get-human-readable-version-of-file-size
# See also https://en.wikipedia.org/wiki/Binary_prefix#yobi
......@@ -122,9 +131,9 @@ class FreifunkNode(object):
def traffic(self):
return 'Tx: {} | Rx: {}'.format(self.tx_bytes, self.rx_bytes)
# --------------------------------------------------------------------------
# Node information
# --------------------------------------------------------------------------
# --------------------------------------------------------------------------
# Node information
# --------------------------------------------------------------------------
@property
def hostname(self):
......
import time, json, pytz, requests
from datetime import datetime, timedelta
# Version 05.Oktober 2019
import time
import json
import requests
from sseclient import SSEClient
import xml.etree.ElementTree as ET
import logging
from toolbox import Util
class Timer(object):
"""
Timer to enforce rate limits and colltect statisticsself.
Parameters
----------
timeout: integer
Number of seconds until the timeout resturns false after reset or init.
"""
def __init__(self, timeout=10):
self.tOut = timeout
def __enter__(self):
self.tStart = time.time()
def __exit__(self, type, value, traceback):
pass
@property
def timeout(self):
if self.tOut - self.elapsed > 0:
return True
else:
return False
@property
def reset(self):
self.tStart = time.time()
@property
def elapsed(self):
return time.time() - self.tStart
logger = logging.getLogger(__name__)
class API(object):
def __init__(self, url=None, user=None, key=None):
self.setupRequests(url=url, user=user, key=key)
self.reset()
self.statistics = dict(speed=0, time=0, size=0)
self.format = 'json'
def reset(self):
self.tElapsed, self.lines, self.util = 0, [], Util()
self.format = ''
def setupRequests(self, url, user=None, key=None, rateLimit=3600, api_suffix=''):
"""
Setup the request parameters.
This should be called once within __init__.
This should be called once within __init__
Parametes
---------
......@@ -70,9 +34,11 @@ class API(object):
String to append to the URL for the request. Example: '&page=1'
"""
self.api_url, self.rateLimit = url, rateLimit
self.lastUpdate, self.response = 0, dict()
self.api_suffix, self.request_url = '', ''
self.api_url = url
self.rateLimit = rateLimit
self.lastUpdate = 0
self.api_suffix = ''
self.request_url = ''
self.status = False
self.auth = requests.auth.HTTPBasicAuth(user, key) if user and key else None
......@@ -99,7 +65,17 @@ class API(object):
"""
pass
def query(self, stream=False):
def stream(self):
try:
response = requests.get(self.request_url, auth=self.auth, stream=True)
except Excpetion as e:
logging.warning(e)
pass
if response.status_code == 200:
self.content = None
self.sseclient = SSEClient(response)
def query(self, url=False):
"""
Perfom a GET-Request on the URL in self.request_url.
......@@ -112,25 +88,18 @@ class API(object):
request has failed or it hits the rate limit.
"""
if time.time() - self.lastUpdate > self.rateLimit:
if time.time() - self.lastUpdate > self.rateLimit or url:
# Ignore rate limit if manual url
self.status = False
self.preQuery()
tStart = time.time()
result = requests.get(self.request_url, auth=self.auth, stream=stream)
self.tElapsed = time.time() - tStart
self.lastUpdate += 5 if result.status_code == 202 else 0
if result.status_code == 200:
if not stream:
self.stats(self.tElapsed, len(result.content))
if self.format == 'json':
self.response = result.json()
elif self.format == 'xml':
self.response = ET.fromstring(result.content)
elif stream:
self.response = None
self.sseclient = SSEClient(result)
self.lastUpdate = time.time()
self.status = True
try:
response = requests.get(url or self.request_url, auth=self.auth)
except Exception as e:
logger.warning(e)
return self.status
self.lastUpdate += 5 if response.status_code == 202 else 0
if response.status_code == 200:
self.handleResponse(response)
self.postQuery()
else:
pass
......@@ -138,9 +107,46 @@ class API(object):
self.postQuery()
return self.status
def stats(self, time, size):
self.statistics.update(time=time, size=size)
self.statistics.update(speed=round(self.statistics['speed']/self.statistics['time']))
def handleResponse(self, response):
if self.format == 'json':
self.content = response.json()
elif self.format == 'xml':
self.content = ET.fromstring(response.content)
else:
self.content = response.content
self.lastUpdate = time.time()
self.status = True
def update(self):
pass
def checkConnection(self, url='https://1.1.1.1', timeout=5):
logger.info('Performing selftest with {}'.format(url))
if self.query(url):
logger.info('connected to the internet.')
else:
logger.warning('Not connected to the internet')
def speedtest(self, url='http://speedtest.belwue.net/100M'):
logger.info('Performing speedtest with {}'.format(url))
now = time.time()
if self.query(url):
self.connectionSpeed = int(len(self.content) / (time.time() - now))
logger.info('Speed is {}/s'.format(human_readable(self.connectionSpeed)))
# Source https://stackoverflow.com/a/43750422
def human_readable(bytes, units=[' bytes','KB','MB','GB','TB', 'PB', 'EB']):
""" Returns a human readable string reprentation of bytes"""
return str(bytes) + units[0] if bytes < 1024 else human_readable(bytes>>10, units[1:])
if __name__ == '__main__':
logging.basicConfig(level=logging.INFO)
logger.info('Script is executed as standalone file.')
test = API()
# See if we are connected to the internet
test.checkConnection()
# Test our connection speed.
test.speedtest()
File added
---
--- node.lua for BMJV - Gesetze im Internet
---
gl.setup(NATIVE_WIDTH, NATIVE_HEIGHT)
local json = require "json"
local st = util.screen_transform(0)
-- local font = resource.load_font "Inconsolata-Regular.ttf"
local metadata = ""
local on = true
local pushedLines = {}
local elements = {}
--
-- watch the json files
......@@ -20,55 +16,43 @@ util.json_watch("config.json", function(cfg)
st = util.screen_transform(cfg.rotation)
font = resource.load_font(cfg.font)
background_color = cfg.bg_color
text_color = cfg.text_color
end)
util.data_mapper{
push = function(data)
local payload = json.decode(data)
text = payload.text
buttons = payload.buttons
end;
stream = function(data)
clear = function(data)
for i in pairs(elements) do
elements[i] = nil
end;
end;
text = function(data)
local payload = json.decode(data)
table.remove(pushedLines, 52)
table.insert(pushedLines, 1, payload.line)
table.insert(elements, 1, payload.data)
end;
toggle = function(status)
on = status == "on"
end;
}
-- create streamed content
function streamContent()
for i, line in ipairs(pushedLines) do
-- font:write(line.x, (i)*line.y, line.string, line.h, line.r, line.g, line.b, line.a)
font:write(line.x, (i)*line.y, line.string, line.h, text_color.r, text_color.g, text_color.b, text_color.a)
function writeText()
for i, line in ipairs(elements) do
font:write(line.x, line.y, line.string, line.h, line.r, line.g, line.b, line.a)
end
end
-- create the content
function content()
-- -- get the single lines to be written
-- for i, button in ipairs(buttons) do
-- width = 0 -- font:width(button.text.string, button.text.h)
-- for j, shape in ipairs(button.shapes) do
-- plain_color = resource.create_colored_texture(shape.r,shape.g,shape.b,shape.a)
-- plain_color:draw(shape.x1, shape.y1, shape.x2+width, shape.y2)
-- end
-- font:write(button.text.x, button.text.y, button.text.string, button.text.h, button.text.r, button.text.g, button.text.b, button.text.a)
-- end
for i, line in ipairs(text) do
font:write(line.x, line.y, line.string, line.h, text_color.r, text_color.g, text_color.b, text_color.a)
end
end
function node.render()
gl.clear(background_color.r, background_color.g, background_color.b, background_color.a) -- clear the backgrount
st()
if on then
content()
streamContent()
end
gl.clear(background_color.r, background_color.g, background_color.b, background_color.a)
st()
if on then
writeText()
end
end
......@@ -6,7 +6,7 @@ import traceback
from hosted import CONFIG, NODE
CONFIG.restart_on_update()
from freifunk import FreifunkNode
from freifunk import Freifunk
from http_API import API
from toolbox import Util
from hosted import node, device
......@@ -16,25 +16,27 @@ import time
class Service(object):
def __init__(self, url, node_id):
self.util = Util()
self.api_url = API(url=url)
self.api_url.format = 'json'
self.yanic = API(url=url)
self.yanic.format = 'json'
self.freifunk = Freifunk()
self.lastRefresh = 0
self.node_id = node_id
def refresh(self):
if time.time() - self.lastRefresh > 60:
node['/clear'](True)
self.util.reset()
self.api_url.query()
self.node = FreifunkNode(self.api_url.response)
self.node.select(self.node_id)
self.util.text('Freifunk Node {}'.format(self.node.model), 20, 20, 100)
self.yanic.query()
self.freifunk.update(self.yanic.content, self.node_id)
line = self.freifunk.arrange()
node['/text'](dict(data=line))
self.lastRefresh = time.time()
else:
pass
def update(self):
self.refresh()
self.util.update()
# self.util.update()
def run(self):
while 1:
......
File added
# Version: 17 Juni 2019
# Version: 05 Oktober 2019
import time, json, pytz
from datetime import datetime
......@@ -17,54 +17,52 @@ class Util(object):
def reset(self):
self.timezone = pytz.timezone('Europe/Berlin')
self.lines, self.rectangles, self.buttons = [], [], []
self.touch_areas, self.lineHeight = [], 32
self.offset, self.margin = dict(x=0, y=0), dict(x=20, y=20)
self.text = []
# self.rectangles
# self.buttons = [], [], []
# self.touch_areas = []
self.lineHeight = 32
self.colour = dict(
button=Colour(1, 1, 1, 1),
border=Colour(1, 0, 0, 1),
text=Colour(1, 1, 1, 1))
self.timeFormat = '%Y-%m-%d %H:%M:%S'
# self.timeFormat = '%Y-%m-%d %H:%M:%S'
# Touch utils.
def button(self, x, y, width, height, label='', id=0, border=0, colour=None):
colour = colour or self.colour
touch = TouchArea(x, y, x+width, y+height, id, border=border, colour=colour)
if id: # No actions if no ID provided
self.touch_areas.append(touch)
button = dict(shapes=[s._asdict() for s in touch.shapes], text={})
button.update(dict(text=self.line(string=label, height=height/2,
offset=dict(x=x, y=y), colour=colour['text']._asdict())))
self.buttons.append(button)
def touched(self, c): # c holds the cursos position, f the field element
active = []
for f in self.touch_areas:
if c.x in range(f.box.x1, f.box.x2) and c.y in range(f.box.y1, f.box.y2):
active.append(f.id)
return active
# def button(self, x, y, width, height, label='', id=0, border=0, colour=None):
# colour = colour or self.colour
# touch = TouchArea(x, y, x+width, y+height, id, border=border, colour=colour)
# if id: # No actions if no ID provided
# self.touch_areas.append(touch)
# button = dict(shapes=[s._asdict() for s in touch.shapes], text={})
# button.update(dict(text=self.line(string=label, height=height/2,
# offset=dict(x=x, y=y), colour=colour['text']._asdict())))
# self.buttons.append(button)
#
# def touched(self, c): # c holds the cursos position, f the field element
# active = []
# for f in self.touch_areas:
# if c.x in range(f.box.x1, f.box.x2) and c.y in range(f.box.y1, f.box.y2):
# active.append(f.id)
# return active
# Design utils
def line(self, string=None, offset=None, height=0, colour=None):
offset = offset or self.offset
def line(self, position=None, string=None, height=0, colour=None):
line = dict(string='' or string, h=height or self.lineHeight)
line.update({k: offset[k] + self.margin[k] for k in ['x', 'y']})
line.update({k: position[k] for k in ['x', 'y']})
line.update(colour or self.colour['text']._asdict())
return line
def text(self, string, x, y, height):
self.lines.append(dict(string=string, x=x, y=y, h=height))
# Datetime tools
def current_time(self, timestamp=None):
now = datetime.utcnow() if not timestamp else datetime.utcfromtimestamp(timestamp)
now = now.replace(tzinfo=pytz.utc)
now = now.astimezone(self.timezone)
now = now.replace(tzinfo=None)
return now
# # Datetime tools
# def current_time(self, timestamp=None):
# now = datetime.utcnow() if not timestamp else datetime.utcfromtimestamp(timestamp)
# now = now.replace(tzinfo=pytz.utc)
# now = now.astimezone(self.timezone)
# now = now.replace(tzinfo=None)
# return now
def week(self, timestamp=None):
return self.current_time(timestamp).strftime('%U')
# def week(self, timestamp=None):
# return self.current_time(timestamp).strftime('%U')
def update(self):
node['/push'](dict(buttons=self.buttons, text=self.lines))
# def update(self):
# node['/push'](dict(buttons=self.buttons, text=self.lines))
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please