Skip to content

Commit 55f1428

Browse files
committed
Update Open-Sky Network Area API Example with Connection Manager
1 parent 7106d2f commit 55f1428

File tree

1 file changed

+139
-120
lines changed

1 file changed

+139
-120
lines changed
Lines changed: 139 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -1,155 +1,170 @@
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_Area_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
1412

1513
import adafruit_requests
14+
from adafruit_binascii import b2a_base64
1615

1716
# OpenSky-Network.org Website Login required for this API
1817
# REST API: https://openskynetwork.github.io/opensky-api/rest.html
19-
2018
# Retrieves all traffic within a geographic area (Orlando example)
21-
latmin = "27.22" # east bounding box
22-
latmax = "28.8" # west bounding box
23-
lonmin = "-81.46" # north bounding box
24-
lonmax = "-80.40" # south bounding box
19+
LATMIN = "27.22" # east bounding box
20+
LATMAX = "28.8" # west bounding box
21+
LONMIN = "-81.46" # north bounding box
22+
LONMAX = "-80.40" # south bounding box
2523

26-
# Initialize WiFi Pool (There can be only 1 pool & top of script)
27-
pool = socketpool.SocketPool(wifi.radio)
28-
29-
# Time between API refreshes
30-
# 900 = 15 mins, 1800 = 30 mins, 3600 = 1 hour
31-
# OpenSky-Networks IP bans for too many requests, check rate limit.
32-
# https://openskynetwork.github.io/opensky-api/rest.html#limitations
33-
sleep_time = 1800
24+
# Github developer token required.
25+
username = os.getenv("GITHUB_USERNAME")
26+
token = os.getenv("GITHUB_TOKEN")
3427

3528
# Get WiFi details, ensure these are setup in settings.toml
3629
ssid = os.getenv("CIRCUITPY_WIFI_SSID")
3730
password = os.getenv("CIRCUITPY_WIFI_PASSWORD")
38-
# No token required, only website login
39-
osnu = os.getenv("OSN_Username")
40-
osnp = os.getenv("OSN_Password")
41-
42-
osn_cred = str(osnu) + ":" + str(osnp)
43-
bytes_to_encode = b" " + str(osn_cred) + " "
44-
base64_string = base64.encodebytes(bytes_to_encode)
45-
base64cred = repr(base64_string)[2:-1]
46-
47-
Debug_Auth = False # STREAMER WARNING this will show your credentials!
48-
if Debug_Auth:
49-
osn_cred = str(osnu) + ":" + str(osnp)
50-
bytes_to_encode = b" " + str(osn_cred) + " "
51-
print(repr(bytes_to_encode))
52-
base64_string = base64.encodebytes(bytes_to_encode)
53-
print(repr(base64_string)[2:-1])
54-
base64cred = repr(base64_string)[2:-1]
55-
print("Decoded Bytes:", str(base64cred))
56-
57-
# OSN requires your username:password to be base64 encoded
58-
# so technically it's not transmitted in the clear but w/e
59-
osn_header = {"Authorization": "Basic " + str(base64cred)}
60-
61-
# Example request of all traffic over Florida, geographic areas cost less per call.
31+
osnusername = os.getenv("OSN_USERNAME") # Website Credentials
32+
osnpassword = os.getenv("OSN_PASSWORD") # Website Credentials
33+
34+
# API Polling Rate
35+
# 900 = 15 mins, 1800 = 30 mins, 3600 = 1 hour
36+
# OpenSky-Networks IP bans for too many requests, check rate limit.
37+
# https://openskynetwork.github.io/opensky-api/rest.html#limitations
38+
SLEEP_TIME = 1800
39+
40+
# Set debug to True for full JSON response.
41+
# WARNING: makes credentials visible
42+
DEBUG = False
43+
44+
# Initalize Wifi, Socket Pool, Request Session
45+
pool = adafruit_connection_manager.get_radio_socketpool(wifi.radio)
46+
ssl_context = adafruit_connection_manager.get_radio_ssl_context(wifi.radio)
47+
requests = adafruit_requests.Session(pool, ssl_context)
48+
49+
# -- Base64 Conversion --
50+
OSN_CREDENTIALS = str(osnusername) + ":" + str(osnpassword)
51+
OSN_CREDENTIALS_B = b"" + str(OSN_CREDENTIALS) + ""
52+
BASE64_ASCII = b2a_base64(OSN_CREDENTIALS_B)
53+
BASE64_STRING = str(BASE64_ASCII) # bytearray
54+
TRUNCATED_BASE64_STRING = BASE64_STRING[2:-1] # truncate bytearray head/tail
55+
56+
if DEBUG:
57+
print("Original Binary Data: ", OSN_CREDENTIALS_B)
58+
print("Base64 ByteArray: ", BASE64_ASCII)
59+
print(f"Base64 String: {TRUNCATED_BASE64_STRING}")
60+
61+
# Area requires OpenSky-Network.org username:password to be base64 encoded
62+
OSN_HEADER = {"Authorization": "Basic " + str(TRUNCATED_BASE64_STRING)}
63+
64+
# Example request of all traffic over Florida.
65+
# Geographic areas calls cost less against the limit.
6266
# https://opensky-network.org/api/states/all?lamin=25.21&lomin=-84.36&lamax=30.0&lomax=-78.40
6367
OPENSKY_SOURCE = (
6468
"https://opensky-network.org/api/states/all?"
6569
+ "lamin="
66-
+ latmin
70+
+ LATMIN
6771
+ "&lomin="
68-
+ lonmin
72+
+ LONMIN
6973
+ "&lamax="
70-
+ latmax
74+
+ LATMAX
7175
+ "&lomax="
72-
+ lonmax
76+
+ LONMAX
7377
)
7478

7579

76-
# Converts seconds to human readable minutes/hours/days
77-
def time_calc(input_time): # input_time in seconds
80+
def time_calc(input_time):
81+
"""Converts seconds to minutes/hours/days"""
7882
if input_time < 60:
79-
sleep_int = input_time
80-
time_output = f"{sleep_int:.0f} seconds"
81-
elif 60 <= input_time < 3600:
82-
sleep_int = input_time / 60
83-
time_output = f"{sleep_int:.0f} minutes"
84-
elif 3600 <= input_time < 86400:
85-
sleep_int = input_time / 60 / 60
86-
time_output = f"{sleep_int:.1f} hours"
87-
else:
88-
sleep_int = input_time / 60 / 60 / 24
89-
time_output = f"{sleep_int:.1f} days"
90-
return time_output
83+
return f"{input_time:.0f} seconds"
84+
if input_time < 3600:
85+
return f"{input_time / 60:.0f} minutes"
86+
if input_time < 86400:
87+
return f"{input_time / 60 / 60:.0f} hours"
88+
return f"{input_time / 60 / 60 / 24:.1f} days"
9189

9290

9391
def _format_datetime(datetime):
94-
return "{:02}/{:02}/{} {:02}:{:02}:{:02}".format(
95-
datetime.tm_mon,
96-
datetime.tm_mday,
97-
datetime.tm_year,
98-
datetime.tm_hour,
99-
datetime.tm_min,
100-
datetime.tm_sec,
92+
"""F-String formatted struct time conversion"""
93+
return (
94+
f"{datetime.tm_mon:02}/"
95+
+ f"{datetime.tm_mday:02}/"
96+
+ f"{datetime.tm_year:02} "
97+
+ f"{datetime.tm_hour:02}:"
98+
+ f"{datetime.tm_min:02}:"
99+
+ f"{datetime.tm_sec:02}"
101100
)
102101

103102

104-
# Connect to Wi-Fi
105-
print("\n===============================")
106-
print("Connecting to WiFi...")
107-
request = adafruit_requests.Session(pool, ssl.create_default_context())
108-
while not wifi.radio.ipv4_address:
103+
while True:
104+
# Connect to Wi-Fi
105+
print("\nConnecting to WiFi...")
106+
while not wifi.radio.ipv4_address:
107+
try:
108+
wifi.radio.connect(ssid, password)
109+
except ConnectionError as e:
110+
print("❌ Connection Error:", e)
111+
print("Retrying in 10 seconds")
112+
print("✅ Wifi!")
113+
109114
try:
110-
wifi.radio.connect(ssid, password)
111-
except ConnectionError as e:
112-
print("Connection Error:", e)
113-
print("Retrying in 10 seconds")
114-
time.sleep(10)
115-
print("Connected!\n")
115+
print(" | Attempting to GET OpenSky-Network Area Flights JSON!")
116+
try:
117+
opensky_response = requests.get(url=OPENSKY_SOURCE, headers=OSN_HEADER)
118+
opensky_json = opensky_response.json()
119+
except ConnectionError as e:
120+
print("Connection Error:", e)
121+
print("Retrying in 10 seconds")
116122

117-
while True:
118-
# STREAMER WARNING this will show your credentials!
119-
debug_request = False # Set True to see full request
120-
if debug_request:
121-
print("Full API HEADER: ", str(osn_header))
122-
print("Full API GET URL: ", OPENSKY_SOURCE)
123-
print("===============================")
123+
print(" | ✅ OpenSky-Network JSON!")
124124

125-
print("\nAttempting to GET OpenSky-Network Data!")
126-
opensky_response = request.get(url=OPENSKY_SOURCE, headers=osn_header).json()
125+
if DEBUG:
126+
print("Full API GET URL: ", OPENSKY_SOURCE)
127+
print(opensky_json)
127128

128-
# Print Full JSON to Serial (doesn't show credentials)
129-
debug_response = False # Set True to see full response
130-
if debug_response:
131-
dump_object = json.dumps(opensky_response)
132-
print("JSON Dump: ", dump_object)
129+
# ERROR MESSAGE RESPONSES
130+
if "timestamp" in opensky_json:
131+
osn_timestamp = opensky_json["timestamp"]
132+
print(f"❌ Timestamp: {osn_timestamp}")
133133

134-
# Key:Value Serial Debug (doesn't show credentials)
135-
osn_debug_keys = True # Set True to print Serial data
136-
if osn_debug_keys:
137-
try:
138-
osn_flight = opensky_response["time"]
139-
print("Current Unix Time: ", osn_flight)
134+
if "message" in opensky_json:
135+
osn_message = opensky_json["message"]
136+
print(f"❌ Message: {osn_message}")
137+
138+
if "error" in opensky_json:
139+
osn_error = opensky_json["error"]
140+
print(f"❌ Error: {osn_error}")
141+
142+
if "path" in opensky_json:
143+
osn_path = opensky_json["path"]
144+
print(f"❌ Path: {osn_path}")
145+
146+
if "status" in opensky_json:
147+
osn_status = opensky_json["status"]
148+
print(f"❌ Status: {osn_status}")
140149

141-
current_struct_time = time.localtime(osn_flight)
142-
current_date = "{}".format(_format_datetime(current_struct_time))
143-
print(f"Unix to Readable Time: {current_date}")
150+
# Current flight data for single callsign (right now)
151+
osn_all_flights = opensky_json["states"]
144152

145-
# Current flight data for single callsign (right now)
146-
osn_all_flights = opensky_response["states"]
153+
if osn_all_flights is not None:
154+
if DEBUG:
155+
print(f" | | Area Flights Full Response: {osn_all_flights}")
156+
157+
osn_time = opensky_json["time"]
158+
# print(f" | | Last Contact Unix Time: {osn_time}")
159+
osn_struct_time = time.localtime(osn_time)
160+
osn_readable_time = f"{_format_datetime(osn_struct_time)}"
161+
print(f" | | Last Contact: {osn_readable_time}")
147162

148163
if osn_all_flights is not None:
149164
# print("Flight Data: ", osn_all_flights)
150165
for flights in osn_all_flights:
151-
osn_t = f"Trans:{flights[0]} "
152-
osn_c = f"Sign:{flights[1]} "
166+
osn_t = f" | | Trans:{flights[0]} "
167+
osn_c = f"Sign:{flights[1]}"
153168
osn_o = f"Origin:{flights[2]} "
154169
osn_tm = f"Time:{flights[3]} "
155170
osn_l = f"Last:{flights[4]} "
@@ -171,16 +186,20 @@ def _format_datetime(datetime):
171186
string2 = f"{osn_la}{osn_ba}{osn_g}{osn_v}{osn_h}{osn_vr}"
172187
string3 = f"{osn_s}{osn_ga}{osn_sq}{osn_pr}{osn_ps}{osn_ca}"
173188
print(f"{string1}{string2}{string3}")
174-
else:
175-
print("Flight has no active data or you're polling too fast.")
176-
177-
print("\nFinished!")
178-
print("Board Uptime: ", time_calc(time.monotonic()))
179-
print("Next Update: ", time_calc(sleep_time))
180-
time.sleep(sleep_time)
181-
print("===============================")
182-
183-
except (ConnectionError, ValueError, NameError) as e:
184-
print("OSN Connection Error:", e)
185-
print("Next Retry: ", time_calc(sleep_time))
186-
time.sleep(sleep_time)
189+
190+
else:
191+
print(" | | ❌ Area has no active data or you're polling too fast.")
192+
193+
opensky_response.close()
194+
print("✂️ Disconnected from OpenSky-Network API")
195+
196+
print("\nFinished!")
197+
print(f"Board Uptime: {time_calc(time.monotonic())}")
198+
print(f"Next Update: {time_calc(SLEEP_TIME)}")
199+
print("===============================")
200+
201+
except (ValueError, RuntimeError) as e:
202+
print(f"Failed to get data, retrying\n {e}")
203+
time.sleep(60)
204+
break
205+
time.sleep(SLEEP_TIME)

0 commit comments

Comments
 (0)