Skip to content

Commit 6f63caa

Browse files
committed
Update Open-Sky Network Single Flight API Example with Connection Manager
1 parent 7106d2f commit 6f63caa

File tree

1 file changed

+160
-115
lines changed

1 file changed

+160
-115
lines changed
Lines changed: 160 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
1-
# SPDX-FileCopyrightText: 2023 DJDevon3
1+
# SPDX-FileCopyrightText: 2024 DJDevon3
22
# SPDX-License-Identifier: MIT
3-
# Coded for Circuit Python 8.1
4-
# DJDevon3 ESP32-S3 OpenSkyNetwork_Private_API_Example
3+
# Coded for Circuit Python 8.2.x
4+
"""OpenSky-Network.org Private API Example"""
5+
# pylint: disable=import-error
56

6-
import json
77
import os
8-
import ssl
98
import time
109

11-
import circuitpython_base64 as base64
12-
import socketpool
10+
import adafruit_connection_manager
1311
import wifi
12+
from adafruit_binascii import b2a_base64
1413

1514
import adafruit_requests
1615

@@ -19,135 +18,181 @@
1918
# All active flights JSON: https://opensky-network.org/api/states/all # PICK ONE! :)
2019
# JSON order: transponder, callsign, country
2120
# ACTIVE transpondes only, for multiple "c822af&icao24=cb3993&icao24=c63923"
22-
transponder = "7c6b2d"
21+
TRANSPONDER = "471efd"
2322

24-
# Initialize WiFi Pool (There can be only 1 pool & top of script)
25-
pool = socketpool.SocketPool(wifi.radio)
23+
# Github developer token required.
24+
username = os.getenv("GITHUB_USERNAME")
25+
token = os.getenv("GITHUB_TOKEN")
2626

27-
# Time between API refreshes
27+
# Get WiFi details, ensure these are setup in settings.toml
28+
ssid = os.getenv("CIRCUITPY_WIFI_SSID")
29+
password = os.getenv("CIRCUITPY_WIFI_PASSWORD")
30+
osnusername = os.getenv("OSN_USERNAME") # Website Credentials
31+
osnpassword = os.getenv("OSN_PASSWORD") # Website Credentials
32+
33+
# API Polling Rate
2834
# 900 = 15 mins, 1800 = 30 mins, 3600 = 1 hour
2935
# OpenSky-Networks IP bans for too many requests, check rate limit.
3036
# https://openskynetwork.github.io/opensky-api/rest.html#limitations
31-
sleep_time = 1800
37+
SLEEP_TIME = 1800
3238

33-
# Get WiFi details, ensure these are setup in settings.toml
34-
ssid = os.getenv("CIRCUITPY_WIFI_SSID")
35-
password = os.getenv("CIRCUITPY_WIFI_PASSWORD")
36-
osnu = os.getenv("OSN_Username")
37-
osnp = os.getenv("OSN_Password")
38-
39-
osn_cred = str(osnu) + ":" + str(osnp)
40-
bytes_to_encode = b" " + str(osn_cred) + " "
41-
base64_string = base64.encodebytes(bytes_to_encode)
42-
base64cred = repr(base64_string)[2:-1]
43-
44-
Debug_Auth = False # STREAMER WARNING this will show your credentials!
45-
if Debug_Auth:
46-
osn_cred = str(osnu) + ":" + str(osnp)
47-
bytes_to_encode = b" " + str(osn_cred) + " "
48-
print(repr(bytes_to_encode))
49-
base64_string = base64.encodebytes(bytes_to_encode)
50-
print(repr(base64_string)[2:-1])
51-
base64cred = repr(base64_string)[2:-1]
52-
print("Decoded Bytes:", str(base64cred))
39+
# Set debug to True for full JSON response.
40+
# WARNING: makes credentials visible
41+
DEBUG = False
42+
43+
# Initalize Wifi, Socket Pool, Request Session
44+
pool = adafruit_connection_manager.get_radio_socketpool(wifi.radio)
45+
ssl_context = adafruit_connection_manager.get_radio_ssl_context(wifi.radio)
46+
requests = adafruit_requests.Session(pool, ssl_context)
47+
48+
# -- Base64 Conversion --
49+
OSN_CREDENTIALS = str(osnusername) + ":" + str(osnpassword)
50+
OSN_CREDENTIALS_B = b"" + str(OSN_CREDENTIALS) + ""
51+
BASE64_ASCII = b2a_base64(OSN_CREDENTIALS_B)
52+
BASE64_STRING = str(BASE64_ASCII) # bytearray
53+
TRUNCATED_BASE64_STRING = BASE64_STRING[2:-1] # truncate bytearray head/tail
54+
55+
if DEBUG:
56+
print("Original Binary Data: ", OSN_CREDENTIALS_B)
57+
print("Base64 ByteArray: ", BASE64_ASCII)
58+
print(f"Base64 String: {TRUNCATED_BASE64_STRING}")
5359

5460
# Requests URL - icao24 is their endpoint required for a transponder
5561
# example https://opensky-network.org/api/states/all?icao24=a808c5
56-
# OSN private requires your username:password to be base64 encoded
57-
osn_header = {"Authorization": "Basic " + str(base64cred)}
58-
OPENSKY_SOURCE = "https://opensky-network.org/api/states/all?" + "icao24=" + transponder
62+
# OSN private: requires your website username:password to be base64 encoded
63+
OSN_HEADER = {"Authorization": "Basic " + str(TRUNCATED_BASE64_STRING)}
64+
OPENSKY_SOURCE = "https://opensky-network.org/api/states/all?" + "icao24=" + TRANSPONDER
5965

6066

61-
# Converts seconds to human readable minutes/hours/days
62-
def time_calc(input_time): # input_time in seconds
67+
def time_calc(input_time):
68+
"""Converts seconds to minutes/hours/days"""
6369
if input_time < 60:
64-
sleep_int = input_time
65-
time_output = f"{sleep_int:.0f} seconds"
66-
elif 60 <= input_time < 3600:
67-
sleep_int = input_time / 60
68-
time_output = f"{sleep_int:.0f} minutes"
69-
elif 3600 <= input_time < 86400:
70-
sleep_int = input_time / 60 / 60
71-
time_output = f"{sleep_int:.1f} hours"
72-
else:
73-
sleep_int = input_time / 60 / 60 / 24
74-
time_output = f"{sleep_int:.1f} days"
75-
return time_output
70+
return f"{input_time:.0f} seconds"
71+
if input_time < 3600:
72+
return f"{input_time / 60:.0f} minutes"
73+
if input_time < 86400:
74+
return f"{input_time / 60 / 60:.0f} hours"
75+
return f"{input_time / 60 / 60 / 24:.1f} days"
7676

7777

7878
def _format_datetime(datetime):
79-
return "{:02}/{:02}/{} {:02}:{:02}:{:02}".format(
80-
datetime.tm_mon,
81-
datetime.tm_mday,
82-
datetime.tm_year,
83-
datetime.tm_hour,
84-
datetime.tm_min,
85-
datetime.tm_sec,
79+
return (
80+
f"{datetime.tm_mon:02}/"
81+
+ f"{datetime.tm_mday:02}/"
82+
+ f"{datetime.tm_year:02} "
83+
+ f"{datetime.tm_hour:02}:"
84+
+ f"{datetime.tm_min:02}:"
85+
+ f"{datetime.tm_sec:02}"
8686
)
8787

8888

89-
# Connect to Wi-Fi
90-
print("\n===============================")
91-
print("Connecting to WiFi...")
92-
request = adafruit_requests.Session(pool, ssl.create_default_context())
93-
while not wifi.radio.ipv4_address:
89+
while True:
90+
# Connect to Wi-Fi
91+
print("\nConnecting to WiFi...")
92+
while not wifi.radio.ipv4_address:
93+
try:
94+
wifi.radio.connect(ssid, password)
95+
except ConnectionError as e:
96+
print("❌ Connection Error:", e)
97+
print("Retrying in 10 seconds")
98+
print("✅ Wifi!")
99+
94100
try:
95-
wifi.radio.connect(ssid, password)
96-
except ConnectionError as e:
97-
print("Connection Error:", e)
98-
print("Retrying in 10 seconds")
99-
time.sleep(10)
100-
print("Connected!\n")
101+
print(" | Attempting to GET OpenSky-Network Single Flight JSON!")
102+
try:
103+
opensky_response = requests.get(url=OPENSKY_SOURCE, headers=OSN_HEADER)
104+
opensky_json = opensky_response.json()
105+
except ConnectionError as e:
106+
print("Connection Error:", e)
107+
print("Retrying in 10 seconds")
101108

102-
while True:
103-
# STREAMER WARNING this will show your credentials!
104-
debug_request = False # Set True to see full request
105-
if debug_request:
106-
print("Full API HEADER: ", str(osn_header))
107-
print("Full API GET URL: ", OPENSKY_SOURCE)
108-
print("===============================")
109+
print(" | ✅ OpenSky-Network JSON!")
109110

110-
print("\nAttempting to GET OpenSky-Network Data!")
111-
opensky_response = request.get(url=OPENSKY_SOURCE, headers=osn_header).json()
111+
if DEBUG:
112+
print("Full API GET URL: ", OPENSKY_SOURCE)
113+
print(opensky_json)
112114

113-
# Print Full JSON to Serial (doesn't show credentials)
114-
debug_response = False # Set True to see full response
115-
if debug_response:
116-
dump_object = json.dumps(opensky_response)
117-
print("JSON Dump: ", dump_object)
115+
# ERROR MESSAGE RESPONSES
116+
if "timestamp" in opensky_json:
117+
osn_timestamp = opensky_json["timestamp"]
118+
print(f"❌ Timestamp: {osn_timestamp}")
118119

119-
# Key:Value Serial Debug (doesn't show credentials)
120-
osn_debug_keys = True # Set True to print Serial data
121-
if osn_debug_keys:
122-
try:
123-
osn_flight = opensky_response["time"]
124-
print("Current Unix Time: ", osn_flight)
125-
126-
current_struct_time = time.localtime(osn_flight)
127-
current_date = "{}".format(_format_datetime(current_struct_time))
128-
print(f"Unix to Readable Time: {current_date}")
129-
130-
# Current flight data for single callsign (right now)
131-
osn_single_flight_data = opensky_response["states"]
132-
133-
if osn_single_flight_data is not None:
134-
print("Flight Data: ", osn_single_flight_data)
135-
transponder = opensky_response["states"][0][0]
136-
print("Transponder: ", transponder)
137-
callsign = opensky_response["states"][0][1]
138-
print("Callsign: ", callsign)
139-
country = opensky_response["states"][0][2]
140-
print("Flight Country: ", country)
120+
if "message" in opensky_json:
121+
osn_message = opensky_json["message"]
122+
print(f"❌ Message: {osn_message}")
123+
124+
if "error" in opensky_json:
125+
osn_error = opensky_json["error"]
126+
print(f"❌ Error: {osn_error}")
127+
128+
if "path" in opensky_json:
129+
osn_path = opensky_json["path"]
130+
print(f"❌ Path: {osn_path}")
131+
132+
if "status" in opensky_json:
133+
osn_status = opensky_json["status"]
134+
print(f"❌ Status: {osn_status}")
135+
136+
# Current flight data for single callsign (right now)
137+
osn_single_flight_data = opensky_json["states"]
138+
139+
if osn_single_flight_data is not None:
140+
if DEBUG:
141+
print(f" | | Single Flight Data: {osn_single_flight_data}")
142+
143+
last_contact = opensky_json["states"][0][4]
144+
# print(f" | | Last Contact Unix Time: {last_contact}")
145+
lc_struct_time = time.localtime(last_contact)
146+
lc_readable_time = f"{_format_datetime(lc_struct_time)}"
147+
print(f" | | Last Contact: {lc_readable_time}")
148+
149+
flight_transponder = opensky_json["states"][0][0]
150+
print(f" | | Transponder: {flight_transponder}")
151+
152+
callsign = opensky_json["states"][0][1]
153+
print(f" | | Callsign: {callsign}")
154+
155+
squawk = opensky_json["states"][0][14]
156+
print(f" | | Squawk: {squawk}")
157+
158+
country = opensky_json["states"][0][2]
159+
print(f" | | Origin: {country}")
160+
161+
longitude = opensky_json["states"][0][5]
162+
print(f" | | Longitude: {longitude}")
163+
164+
latitude = opensky_json["states"][0][6]
165+
print(f" | | Latitude: {latitude}")
166+
167+
# Return Air Flight data if not on ground
168+
on_ground = opensky_json["states"][0][8]
169+
if on_ground is True:
170+
print(f" | | On Ground: {on_ground}")
141171
else:
142-
print("Flight has no active data or you're polling too fast.")
143-
144-
print("\nFinished!")
145-
print("Board Uptime: ", time_calc(time.monotonic()))
146-
print("Next Update: ", time_calc(sleep_time))
147-
time.sleep(sleep_time)
148-
print("===============================")
149-
150-
except (ConnectionError, ValueError, NameError) as e:
151-
print("OSN Connection Error:", e)
152-
print("Next Retry: ", time_calc(sleep_time))
153-
time.sleep(sleep_time)
172+
altitude = opensky_json["states"][0][7]
173+
print(f" | | Barometric Altitude: {altitude}")
174+
175+
velocity = opensky_json["states"][0][9]
176+
if velocity != "null":
177+
print(f" | | Velocity: {velocity}")
178+
179+
vertical_rate = opensky_json["states"][0][11]
180+
if vertical_rate != "null":
181+
print(f" | | Vertical Rate: {vertical_rate}")
182+
183+
else:
184+
print(" | | ❌ Flight has no active data or you're polling too fast.")
185+
186+
opensky_response.close()
187+
print("✂️ Disconnected from OpenSky-Network API")
188+
189+
print("\nFinished!")
190+
print(f"Board Uptime: {time_calc(time.monotonic())}")
191+
print(f"Next Update: {time_calc(SLEEP_TIME)}")
192+
print("===============================")
193+
194+
except (ValueError, RuntimeError) as e:
195+
print(f"Failed to get data, retrying\n {e}")
196+
time.sleep(60)
197+
break
198+
time.sleep(SLEEP_TIME)

0 commit comments

Comments
 (0)