flog module changed to use glidernet log api
This commit is contained in:
parent
35593de3b9
commit
166757674b
22
README.md
22
README.md
|
@ -358,25 +358,26 @@ Read more about OGN at https://www.glidernet.org/
|
||||||
FLOG module supports showing field logs for OGN receivers and can display live
|
FLOG module supports showing field logs for OGN receivers and can display live
|
||||||
field log in a room.
|
field log in a room.
|
||||||
|
|
||||||
It uses KTrax service. Please read https://ktrax.kisstech.ch/api-usage and request a
|
It uses FlightBook instance at https://flightbook.glidernet.org/
|
||||||
API key for yourself.
|
|
||||||
|
FlightBook sources and documentation at https://gitlab.com/lemoidului/ogn-flightbook
|
||||||
|
|
||||||
|
|
||||||
Real life field log output looks something like:
|
Real life field log output looks something like:
|
||||||
|
|
||||||
```
|
```
|
||||||
Flights at ESGE today:
|
Flights at Chateau Arnoux St Auban (LFMX) 2021-04-09:
|
||||||
12:02-12:13 0:11 290m Ultralight SE-VSL
|
13:36-18:01 04:24 F-CLDG JS-3 18M FM 2291m
|
||||||
12:17-13:18 1:01 1080m ASG-32 MI SE-SKV VH
|
13:46-··:·· F-CIFF Arcus T FP
|
||||||
12:26-13:52 1:25 1090m DG-1000 SE-UMY VF
|
13:57-17:13 03:15 JS-1 C21 72 2636m
|
||||||
12:34-16:32 3:57 1710m Duo Discus xlt SE-UXF XF
|
14:08-17:30 03:22 ZS-GCC JS-1 C21 FD 2754m
|
||||||
|
18:29-··:·· F-CLDG JS-3 18M FM
|
||||||
```
|
```
|
||||||
|
|
||||||
Commands and examples:
|
Commands and examples:
|
||||||
|
|
||||||
(You must be room admin for all commands, except apikey which requires
|
(You must be room admin for all commands)
|
||||||
bot ownership)
|
|
||||||
|
|
||||||
* !flog apikey FLOG-123456 - Set api key
|
|
||||||
* !flog station EFJM - set the default station to track for this room
|
* !flog station EFJM - set the default station to track for this room
|
||||||
* !flog rmstation - remove station from this room
|
* !flog rmstation - remove station from this room
|
||||||
* !flog - Show field flog for the room's station (can be used by any user)
|
* !flog - Show field flog for the room's station (can be used by any user)
|
||||||
|
@ -384,7 +385,6 @@ bot ownership)
|
||||||
* !flog status - print status of this room
|
* !flog status - print status of this room
|
||||||
* !flog live - enable live field log for this room
|
* !flog live - enable live field log for this room
|
||||||
* !flog rmlive - disable live field log for this room
|
* !flog rmlive - disable live field log for this room
|
||||||
* !flog timezone 3 - set timezone (relative to UTC, see API docs)
|
|
||||||
|
|
||||||
NOTE: disabled by default
|
NOTE: disabled by default
|
||||||
|
|
||||||
|
|
189
modules/flog.py
189
modules/flog.py
|
@ -10,80 +10,100 @@ from random import randrange
|
||||||
|
|
||||||
from modules.common.module import BotModule
|
from modules.common.module import BotModule
|
||||||
|
|
||||||
|
class FlightBook:
|
||||||
|
def __init__(self):
|
||||||
|
self.base_url = 'https://flightbook.glidernet.org/api'
|
||||||
|
self.AC_TYPES = [ '?', 'Glider', 'Towplane', \
|
||||||
|
'Helicopter', 'Parachute', 'Drop plane', 'Hang glider', \
|
||||||
|
'Paraglider', 'Powered', 'Jet', 'UFO', 'Balloon', \
|
||||||
|
'Airship', 'UAV', '?', 'Static object' ]
|
||||||
|
|
||||||
|
def get_flights(self, icao):
|
||||||
|
response = urllib.request.urlopen(self.base_url + "/logbook/" + icao)
|
||||||
|
data = json.loads(response.read().decode("utf-8"))
|
||||||
|
# print(json.dumps(data, sort_keys=True, indent=4))
|
||||||
|
return data
|
||||||
|
|
||||||
|
def format_time(self, time):
|
||||||
|
if not time:
|
||||||
|
return '··:··'
|
||||||
|
time = time.replace('h', ':')
|
||||||
|
return time
|
||||||
|
|
||||||
|
def flight2string(self, flight, data):
|
||||||
|
devices = data['devices']
|
||||||
|
device = devices[flight['device']]
|
||||||
|
start = self.format_time(flight["start"])
|
||||||
|
end = self.format_time(flight["stop"])
|
||||||
|
duration = ' '
|
||||||
|
if flight["duration"]:
|
||||||
|
duration = time.strftime('%H:%M', time.gmtime(flight["duration"]))
|
||||||
|
maxalt = ''
|
||||||
|
if flight["max_alt"]:
|
||||||
|
maxalt = str(flight["max_alt"]) + 'm'
|
||||||
|
|
||||||
|
identity = f'{device.get("registration") or ""} {device.get("aircraft") or ""} {device.get("competition") or ""} {maxalt}'
|
||||||
|
identity = ' '.join(identity.split())
|
||||||
|
return f'{start} - {end} {duration} {identity}'
|
||||||
|
|
||||||
|
def print_flights(self, data, showtow=False):
|
||||||
|
print(f'✈ Flights at {data["airfield"]["name"]} ({data["airfield"]["code"]}) {data["date"]}:')
|
||||||
|
flights = data['flights']
|
||||||
|
for flight in flights:
|
||||||
|
if not showtow and flight["towing"]:
|
||||||
|
continue
|
||||||
|
print(self.flight2string(flight, data))
|
||||||
|
|
||||||
|
def test():
|
||||||
|
fb = FlightBook()
|
||||||
|
data = fb.get_flights('LFMX')
|
||||||
|
fb.print_flights(data)
|
||||||
|
|
||||||
class MatrixModule(BotModule):
|
class MatrixModule(BotModule):
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
super().__init__(name)
|
super().__init__(name)
|
||||||
self.service_name = 'FLOG'
|
self.service_name = 'FLOG'
|
||||||
self.station_rooms = dict() # Roomid -> ogn station
|
self.station_rooms = dict() # Roomid -> ogn station
|
||||||
self.live_rooms = [] # Roomid's with live enabled
|
self.live_rooms = [] # Roomid's with live enabled
|
||||||
self.room_timezones = dict() # Roomid -> timezone
|
|
||||||
self.api_key = ''
|
|
||||||
self.logged_flights = []
|
self.logged_flights = []
|
||||||
self.logged_flights_date = ""
|
self.logged_flights_date = ""
|
||||||
self.first_poll = True
|
self.first_poll = True
|
||||||
self.enabled = False
|
self.enabled = False
|
||||||
|
self.fb = FlightBook()
|
||||||
|
|
||||||
async def matrix_poll(self, bot, pollcount):
|
async def matrix_poll(self, bot, pollcount):
|
||||||
if len(self.api_key) > 0:
|
if pollcount % (6 * 5) == 0: # Poll every 5 min
|
||||||
if pollcount % (6 * 5) == 0: # Poll every 5 min
|
await self.poll_implementation(bot)
|
||||||
await self.poll_implementation(bot)
|
|
||||||
|
|
||||||
async def poll_implementation(self, bot):
|
async def poll_implementation(self, bot):
|
||||||
for roomid in self.live_rooms:
|
for roomid in self.live_rooms:
|
||||||
station = self.station_rooms[roomid]
|
station = self.station_rooms[roomid]
|
||||||
data = self.get_flights(station, self.room_timezones.get(roomid, 0))
|
data = self.fb.get_flights(station)
|
||||||
|
|
||||||
# Date changed - reset flight count
|
# Date changed - reset flight count
|
||||||
if data["begin_date"] != self.logged_flights_date:
|
if data["date"] != self.logged_flights_date:
|
||||||
self.logged_flights = []
|
self.logged_flights = []
|
||||||
self.logged_flights_date = data["begin_date"]
|
self.logged_flights_date = data["date"]
|
||||||
|
|
||||||
flights = []
|
flights = data['flights']
|
||||||
|
|
||||||
for sortie in data["sorties"]:
|
|
||||||
# Don't show towplanes
|
|
||||||
if sortie["type"] != 2:
|
|
||||||
# Count only landed gliders
|
|
||||||
if sortie["ldg"]["time"] != "":
|
|
||||||
flights.append(
|
|
||||||
{
|
|
||||||
"takeoff": sortie["tkof"]["time"],
|
|
||||||
"landing": sortie["ldg"]["time"],
|
|
||||||
"duration": sortie["dt"],
|
|
||||||
"glider": self.glider2string(sortie),
|
|
||||||
"altitude": str(sortie["dalt"]),
|
|
||||||
"seq": sortie["seq"]
|
|
||||||
})
|
|
||||||
for flight in flights:
|
for flight in flights:
|
||||||
if flight["seq"] not in self.logged_flights:
|
if flight["towing"]:
|
||||||
|
continue
|
||||||
|
if flight["stop"]:
|
||||||
|
self.logged_flights.append(flight)
|
||||||
|
|
||||||
|
for flight in flights:
|
||||||
|
if not self.find_flight_in_list(flight, self.logged_flights):
|
||||||
if not self.first_poll:
|
if not self.first_poll:
|
||||||
await bot.send_text(bot.get_room_by_id(roomid), flight["takeoff"] + "-" + flight["landing"] + " (" + flight["duration"] + ") - " + flight["altitude"] + "m " + flight["glider"])
|
await bot.send_text(bot.get_room_by_id(roomid), self.fb.flight2string(flight, data))
|
||||||
self.logged_flights.append(flight["seq"])
|
self.logged_flights.append(flight)
|
||||||
self.first_poll = False
|
self.first_poll = False
|
||||||
|
|
||||||
def get_flights(self, station, timezone):
|
def find_flight_in_list(self, flight, flight_list):
|
||||||
timenow = time.localtime(time.time())
|
for fl in flight_list:
|
||||||
today = str(timenow[0]) + "-" + str(timenow[1]) + "-" + str(timenow[2])
|
if fl["device"] == flight["device"] and fl["start"] == flight["start"]:
|
||||||
# Example 'https://ktrax.kisstech.ch/backend/logbook?db=sortie&query_type=ap&tz=3&id=ESGE&dbeg=2020-05-03&dend=2020-05-03'
|
return True
|
||||||
log_url = f'https://ktrax.kisstech.ch/backend/logbook?db=sortie&query_type=ap&tz={timezone}&id={station}&dbeg={today}&dend={today}&apikey={self.api_key}'
|
return False
|
||||||
response = urllib.request.urlopen(log_url)
|
|
||||||
data = json.loads(response.read().decode("utf-8"))
|
|
||||||
# print(json.dumps(data, sort_keys=True, indent=4))
|
|
||||||
return data
|
|
||||||
|
|
||||||
def glider2string(self, sortie):
|
|
||||||
actype = sortie["actype"]
|
|
||||||
cs = sortie["cs"]
|
|
||||||
cn = sortie["cn"]
|
|
||||||
if cs == "-":
|
|
||||||
cs = ""
|
|
||||||
if cn == "-":
|
|
||||||
cn = ""
|
|
||||||
|
|
||||||
if actype == "" and cs == "" and cn == "":
|
|
||||||
return "????"
|
|
||||||
return (actype + " " + cs + " " + cn).strip()
|
|
||||||
|
|
||||||
|
|
||||||
async def matrix_message(self, bot, room, event):
|
async def matrix_message(self, bot, room, event):
|
||||||
args = event.body.split()
|
args = event.body.split()
|
||||||
|
@ -103,7 +123,7 @@ class MatrixModule(BotModule):
|
||||||
|
|
||||||
elif args[1] == 'status':
|
elif args[1] == 'status':
|
||||||
bot.must_be_admin(room, event)
|
bot.must_be_admin(room, event)
|
||||||
await bot.send_text(room, f'OGN station for this room: {self.station_rooms.get(room.room_id)}, live updates enabled: {room.room_id in self.live_rooms}, timezone: {self.room_timezones.get(room.room_id, 0)} api key is set: {len(self.api_key) > 0}')
|
await bot.send_text(room, f'OGN station for this room: {self.station_rooms.get(room.room_id)}, live updates enabled: {room.room_id in self.live_rooms}')
|
||||||
|
|
||||||
elif args[1] == 'poll':
|
elif args[1] == 'poll':
|
||||||
bot.must_be_admin(room, event)
|
bot.must_be_admin(room, event)
|
||||||
|
@ -137,47 +157,42 @@ class MatrixModule(BotModule):
|
||||||
bot.save_settings()
|
bot.save_settings()
|
||||||
await bot.send_text(room, f'Set OGN station {station} to this room')
|
await bot.send_text(room, f'Set OGN station {station} to this room')
|
||||||
|
|
||||||
elif args[1] == 'apikey':
|
def text_flog(self, data, showtow):
|
||||||
bot.must_be_owner(event)
|
out = ""
|
||||||
|
if len(data["flights"]) == 0:
|
||||||
|
out = f'No known flights today at {data["airfield"]["name"]}'
|
||||||
|
else:
|
||||||
|
out = f'Flights at {data["airfield"]["name"]} ({data["airfield"]["code"]}) {data["date"]}:' + "\n"
|
||||||
|
flights = data['flights']
|
||||||
|
for flight in flights:
|
||||||
|
if not showtow and flight["towing"]:
|
||||||
|
continue
|
||||||
|
out = out + self.fb.flight2string(flight, data) + "\n"
|
||||||
|
return out
|
||||||
|
|
||||||
self.api_key = args[2]
|
def html_flog(self, data, showtow):
|
||||||
bot.save_settings()
|
out = ""
|
||||||
await bot.send_text(room, 'Api key set')
|
if len(data["flights"]) == 0:
|
||||||
|
out = f'No known flights today at {data["airfield"]["name"]}'
|
||||||
elif args[1] == 'timezone':
|
else:
|
||||||
bot.must_be_admin(room, event)
|
out = f'<b>✈ Flights at {data["airfield"]["name"]} ({data["airfield"]["code"]}) {data["date"]}:' + "</b>\n"
|
||||||
tz = int(args[2])
|
flights = data['flights']
|
||||||
self.room_timezones[room.room_id] = tz
|
out = out + "<ul>"
|
||||||
bot.save_settings()
|
for flight in flights:
|
||||||
await bot.send_text(room, f'Timezone set to {tz}')
|
if not showtow and flight["towing"]:
|
||||||
|
continue
|
||||||
|
out = out + "<li>" + self.fb.flight2string(flight, data) + "</li>\n"
|
||||||
|
out = out + "</ul>"
|
||||||
|
return out
|
||||||
|
|
||||||
async def show_flog(self, bot, room, station):
|
async def show_flog(self, bot, room, station):
|
||||||
data = self.get_flights(station, self.room_timezones.get(room.room_id, 0))
|
data = self.fb.get_flights(station)
|
||||||
out = ""
|
await bot.send_html(room, self.html_flog(data, False), self.text_flog(data, False))
|
||||||
if len(data["sorties"]) == 0:
|
|
||||||
out = "No known flights today at " + station
|
|
||||||
else:
|
|
||||||
out = "Flights at " + station.upper() + " today:\n"
|
|
||||||
for sortie in data["sorties"]:
|
|
||||||
# Don't show towplanes
|
|
||||||
if sortie["type"] != 2:
|
|
||||||
if sortie["ldg"]["time"] == "":
|
|
||||||
sortie["ldg"]["time"] = u" \u2708 "
|
|
||||||
else:
|
|
||||||
sortie["ldg"]["time"] = "-" + sortie["ldg"]["time"]
|
|
||||||
if sortie["ldg"]["loc"] != sortie["tkof"]["loc"]:
|
|
||||||
sortie["tkof"]["time"] = sortie["tkof"]["time"] + "(" + sortie["tkof"]["loc"] + ")"
|
|
||||||
sortie["ldg"]["time"] = sortie["ldg"]["time"] + "(" + sortie["ldg"]["loc"] + ") "
|
|
||||||
|
|
||||||
out = out + sortie["tkof"]["time"] + sortie["ldg"]["time"] + " " + sortie["dt"] + " " + str(sortie["dalt"]) + "m " + self.glider2string(sortie) + "\n"
|
|
||||||
await bot.send_text(room, out)
|
|
||||||
|
|
||||||
def get_settings(self):
|
def get_settings(self):
|
||||||
data = super().get_settings()
|
data = super().get_settings()
|
||||||
data['apikey'] = self.api_key
|
|
||||||
data['station_rooms'] = self.station_rooms
|
data['station_rooms'] = self.station_rooms
|
||||||
data['live_rooms'] = self.live_rooms
|
data['live_rooms'] = self.live_rooms
|
||||||
data['room_timezones'] = self.room_timezones
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def set_settings(self, data):
|
def set_settings(self, data):
|
||||||
|
@ -186,12 +201,6 @@ class MatrixModule(BotModule):
|
||||||
self.station_rooms = data['station_rooms']
|
self.station_rooms = data['station_rooms']
|
||||||
if data.get('live_rooms'):
|
if data.get('live_rooms'):
|
||||||
self.live_rooms = data['live_rooms']
|
self.live_rooms = data['live_rooms']
|
||||||
if data.get('room_timezones'):
|
|
||||||
self.room_timezones = data['room_timezones']
|
|
||||||
if data.get('apikey'):
|
|
||||||
self.api_key = data['apikey']
|
|
||||||
if self.api_key and len(self.api_key) == 0:
|
|
||||||
self.api_key = None
|
|
||||||
|
|
||||||
def help(self):
|
def help(self):
|
||||||
return ('Open Glider Network Field Log')
|
return ('Open Glider Network Field Log')
|
||||||
|
|
Loading…
Reference in New Issue