Improved API & added web version

This commit is contained in:
Filip Znachor 2022-12-08 20:02:58 +01:00
parent a26666071a
commit 1067333518
5 changed files with 131 additions and 24 deletions

View file

@ -1,5 +1,6 @@
from bottle import route, template, error, run, ServerAdapter
from bottle import route, template, error, run, get, ServerAdapter, static_file
from departures import Departure
from operator import itemgetter
import threading
class Server(ServerAdapter):
@ -16,15 +17,20 @@ class Server(ServerAdapter):
def stop(self):
self.server.shutdown()
server = Server(port=8080)
server = Server(host="0.0.0.0", port=8080)
@route('/departures')
def index():
resp = []
for d in Departure.storage:
resp.append(Departure.storage[d].json())
resp.sort(key=itemgetter("departure"))
return {'departures': resp}
@get("/")
def static():
return static_file("index.html", root="static")
@error(404)
def error404(error):
return ''

View file

@ -91,7 +91,7 @@ class Departure:
self.delay = delay
def get_departure(self):
departure = round(self.get_accurate_departure()*10)
departure = floor(self.get_accurate_departure()*10)
return departure
def get_accurate_departure(self):
@ -106,5 +106,6 @@ class Departure:
'line': self.line,
'type': self.type,
'last_stop': self.last_stop,
'departure': round(self.get_accurate_departure())
'departure': floor(self.get_accurate_departure()),
'delay': self.delay
}

View file

@ -57,7 +57,7 @@ class LoraController:
while True:
if not len(self.message_pool):
break
data = self.message_pool.pop()
data = self.message_pool.pop(0)
url = f"https://lora.plzen.eu/api/v2/nodes/{data[0]:0>16x}/queue"
string = data[2]()
headers = CaseInsensitiveDict()

View file

@ -40,30 +40,35 @@ class MainLoop:
sleep(1)
if self.ended:
break
def stop(self):
print("Stopping...")
server.stop()
self.ended = True
self.controller.message_pool = []
def input_loop(self):
while not self.ended:
print("> ", end="")
command = input()
try:
while not self.ended:
print("> ", end="")
command = input()
if command in ["d", "dep", "departures"]:
for did in Departure.storage:
print(Departure.storage[did])
if len(Departure.storage) == 0:
print("No loaded departures")
if command in ["d", "dep", "departures"]:
for did in Departure.storage:
print(Departure.storage[did])
if len(Departure.storage) == 0:
print("No loaded departures")
if command in ["q", "queue"]:
for m in self.controller.message_pool:
print(m)
if len(self.controller.message_pool) == 0:
print("No messages in queue")
if command in ["q", "queue"]:
for m in self.controller.message_pool:
print(m)
if len(self.controller.message_pool) == 0:
print("No messages in queue")
if command in ["s", "stop"]:
print("Stopping...")
server.stop()
self.ended = True
self.controller.message_pool = []
if command in ["s", "stop"]:
self.stop()
except KeyboardInterrupt:
self.stop()
main_loop = MainLoop("40", lora_controller)
main_loop.input_loop()

95
server/static/index.html Normal file
View file

@ -0,0 +1,95 @@
<!DOCTYPE html>
<html>
<head>
<title>Odjezdová tabule</title>
<style>
:root {
--site-bg: #1C1C1C;
--site-alt-bg: #272727;
}
body {background-color: var(--site-bg); color: #fffd; margin: 0; margin-top: 35px; font-family: sans-serif; box-sizing: border-box;}
.container {max-width: 700px; padding: 25px; margin: auto;}
.departure-grid {display: grid; grid-template-columns: 30px 1fr max-content max-content; gap: 10px 20px; line-height: 31px;}
.header {line-height: 100%; opacity: .5; margin-bottom: 10px; font-size: 90%;}
.line {background-color: var(--site-alt-bg); border-radius: 3px; box-shadow: 0 0 5px 0 #0004; text-align: center; font-weight: 700; width: 30px; height: 30px; color: #fff; text-shadow: 0 0 15px #000;}
.last-stop {font-weight: 700;}
.departure {text-align: right;}
.delay {text-align: right; color: #ff6e6e; font-size: 80%;}
.line.type1 {background-color: #F0BE32;}
.line.type2 {background-color: #1E9641;}
.line.type3 {background-color: #CD2837;}
h3 {color: #fff;}
.nav {position: fixed; top: 0; left: 0; width: 100%; z-index: 10;}
.nav .inner {background: linear-gradient(to bottom, var(--site-bg) 70%, transparent 100%); padding: 5px 25px; margin: auto; max-width: 700px; padding-bottom: 25px; transition: padding .1s;}
.nav.top .inner {padding-bottom: 0px;}
.nav .inner::after {content: ""; position: fixed; bottom: 0; left: 0; height: 20px; width: 100%; background: linear-gradient(to top, var(--site-bg) 0%, transparent 100%)}
</style>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div class="container" id="app">
<div class="nav" :class="{'top': top == 0}">
<div class="inner">
<h3>Borský park</h3>
</div>
</div>
<div class="departure-grid">
<div class="header">spoj</div>
<div class="header">poslední zastávka</div>
<div class="header">zpoždění</div>
<div class="header">odjezd</div>
<template v-for="d in departures">
<template v-if="d.departure >= 0">
<div class="line" :class="'type'+d.type">{{ d.line }}</div>
<div class="last-stop">{{ d.last_stop }}</div>
<div class="delay">{{ d.delay == 0 ? '' : '+'+d.delay }}</div>
<div class="departure">{{ d.departure == 0 ? '<1' : d.departure }}</div>
</template>
</template>
</div>
</div>
<script>
const { createApp } = Vue;
let app = createApp({
data() {
return {
top: 0,
departures: []
}
}
}).mount('#app');
async function api() {
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if(this.readyState !== 4) return;
if(!this.responseText) reject(this);
try {
resolve(JSON.parse(this.responseText));
} catch(e) {
resolve(null);
}
};
xhr.open("GET", "/departures", true);
xhr.send();
});
}
window.addEventListener("scroll", () => {
app.$data.top = document.querySelector('html').scrollTop;
})
async function update() {
app.$data.departures = (await api()).departures;
}
setInterval(update, 5000);
update();
</script>
</body>
</html>