Improved API & added web version
This commit is contained in:
parent
a26666071a
commit
1067333518
|
@ -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 ''
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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()
|
||||
|
|
|
@ -41,8 +41,14 @@ class MainLoop:
|
|||
if self.ended:
|
||||
break
|
||||
|
||||
def stop(self):
|
||||
print("Stopping...")
|
||||
server.stop()
|
||||
self.ended = True
|
||||
self.controller.message_pool = []
|
||||
|
||||
def input_loop(self):
|
||||
try:
|
||||
while not self.ended:
|
||||
print("> ", end="")
|
||||
command = input()
|
||||
|
@ -60,10 +66,9 @@ class MainLoop:
|
|||
print("No messages in queue")
|
||||
|
||||
if command in ["s", "stop"]:
|
||||
print("Stopping...")
|
||||
server.stop()
|
||||
self.ended = True
|
||||
self.controller.message_pool = []
|
||||
self.stop()
|
||||
except KeyboardInterrupt:
|
||||
self.stop()
|
||||
|
||||
main_loop = MainLoop("40", lora_controller)
|
||||
main_loop.input_loop()
|
95
server/static/index.html
Normal file
95
server/static/index.html
Normal 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>
|
Loading…
Reference in a new issue