Skip to content

Commit d595686

Browse files
authored
Merge pull request #167 from DJDevon3/DJDevon3-openskynetwork_private
Update Open-Sky Network Private Single Flight API Example
2 parents 1c56938 + 6d627d9 commit d595686

File tree

3 files changed

+301
-338
lines changed

3 files changed

+301
-338
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
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 Single Flight Private API Example"""
55

6-
import json
6+
import binascii
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
1412

1513
import adafruit_requests
@@ -19,135 +17,177 @@
1917
# All active flights JSON: https://opensky-network.org/api/states/all # PICK ONE! :)
2018
# JSON order: transponder, callsign, country
2119
# ACTIVE transpondes only, for multiple "c822af&icao24=cb3993&icao24=c63923"
22-
transponder = "7c6b2d"
20+
TRANSPONDER = "4b1812"
2321

24-
# Initialize WiFi Pool (There can be only 1 pool & top of script)
25-
pool = socketpool.SocketPool(wifi.radio)
22+
# Get WiFi details, ensure these are setup in settings.toml
23+
ssid = os.getenv("CIRCUITPY_WIFI_SSID")
24+
password = os.getenv("CIRCUITPY_WIFI_PASSWORD")
25+
osnusername = os.getenv("OSN_USERNAME") # Website Credentials
26+
osnpassword = os.getenv("OSN_PASSWORD") # Website Credentials
2627

27-
# Time between API refreshes
28+
# API Polling Rate
2829
# 900 = 15 mins, 1800 = 30 mins, 3600 = 1 hour
2930
# OpenSky-Networks IP bans for too many requests, check rate limit.
3031
# https://openskynetwork.github.io/opensky-api/rest.html#limitations
31-
sleep_time = 1800
32+
SLEEP_TIME = 1800
3233

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))
34+
# Set debug to True for full JSON response.
35+
# WARNING: makes credentials visible
36+
DEBUG = False
37+
38+
# Initalize Wifi, Socket Pool, Request Session
39+
pool = adafruit_connection_manager.get_radio_socketpool(wifi.radio)
40+
ssl_context = adafruit_connection_manager.get_radio_ssl_context(wifi.radio)
41+
requests = adafruit_requests.Session(pool, ssl_context)
42+
43+
# -- Base64 Conversion --
44+
OSN_CREDENTIALS = str(osnusername) + ":" + str(osnpassword)
45+
# base64 encode and strip appended \n from bytearray
46+
OSN_CREDENTIALS_B = binascii.b2a_base64(OSN_CREDENTIALS.encode()).strip()
47+
BASE64_STRING = OSN_CREDENTIALS_B.decode() # bytearray
48+
49+
50+
if DEBUG:
51+
print("Base64 ByteArray: ", BASE64_STRING)
5352

5453
# Requests URL - icao24 is their endpoint required for a transponder
5554
# 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
55+
# OSN private: requires your website username:password to be base64 encoded
56+
OPENSKY_HEADER = {"Authorization": "Basic " + BASE64_STRING}
57+
OPENSKY_SOURCE = "https://opensky-network.org/api/states/all?" + "icao24=" + TRANSPONDER
5958

6059

61-
# Converts seconds to human readable minutes/hours/days
62-
def time_calc(input_time): # input_time in seconds
60+
def time_calc(input_time):
61+
"""Converts seconds to minutes/hours/days"""
6362
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
63+
return f"{input_time:.0f} seconds"
64+
if input_time < 3600:
65+
return f"{input_time / 60:.0f} minutes"
66+
if input_time < 86400:
67+
return f"{input_time / 60 / 60:.0f} hours"
68+
return f"{input_time / 60 / 60 / 24:.1f} days"
7669

7770

7871
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,
72+
return (
73+
f"{datetime.tm_mon:02}/"
74+
+ f"{datetime.tm_mday:02}/"
75+
+ f"{datetime.tm_year:02} "
76+
+ f"{datetime.tm_hour:02}:"
77+
+ f"{datetime.tm_min:02}:"
78+
+ f"{datetime.tm_sec:02}"
8679
)
8780

8881

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:
82+
while True:
83+
# Connect to Wi-Fi
84+
print("\nConnecting to WiFi...")
85+
while not wifi.radio.ipv4_address:
86+
try:
87+
wifi.radio.connect(ssid, password)
88+
except ConnectionError as e:
89+
print("❌ Connection Error:", e)
90+
print("Retrying in 10 seconds")
91+
print("✅ Wifi!")
92+
9493
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")
94+
print(" | Attempting to GET OpenSky-Network Single Private Flight JSON!")
95+
print(" | Website Credentials Required! Allows more daily calls than Public.")
96+
try:
97+
opensky_response = requests.get(url=OPENSKY_SOURCE, headers=OPENSKY_HEADER)
98+
opensky_json = opensky_response.json()
99+
except ConnectionError as e:
100+
print("Connection Error:", e)
101+
print("Retrying in 10 seconds")
101102

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("===============================")
103+
print(" | ✅ OpenSky-Network JSON!")
109104

110-
print("\nAttempting to GET OpenSky-Network Data!")
111-
opensky_response = request.get(url=OPENSKY_SOURCE, headers=osn_header).json()
105+
if DEBUG:
106+
print("Full API GET URL: ", OPENSKY_SOURCE)
107+
print("Full API GET Header: ", OPENSKY_HEADER)
108+
print(opensky_json)
112109

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)
110+
# ERROR MESSAGE RESPONSES
111+
if "timestamp" in opensky_json:
112+
osn_timestamp = opensky_json["timestamp"]
113+
print(f"❌ Timestamp: {osn_timestamp}")
118114

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

0 commit comments

Comments
 (0)