1
- # SPDX-FileCopyrightText: 2023 DJDevon3
1
+ # SPDX-FileCopyrightText: 2024 DJDevon3
2
2
# 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
5
6
6
- import json
7
7
import os
8
- import ssl
9
8
import time
10
9
11
- import circuitpython_base64 as base64
12
- import socketpool
10
+ import adafruit_connection_manager
13
11
import wifi
14
12
15
13
import adafruit_requests
14
+ from adafruit_binascii import b2a_base64
16
15
17
16
# OpenSky-Network.org Website Login required for this API
18
17
# REST API: https://openskynetwork.github.io/opensky-api/rest.html
19
-
20
18
# 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
25
23
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" )
34
27
35
28
# Get WiFi details, ensure these are setup in settings.toml
36
29
ssid = os .getenv ("CIRCUITPY_WIFI_SSID" )
37
30
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.
62
66
# https://opensky-network.org/api/states/all?lamin=25.21&lomin=-84.36&lamax=30.0&lomax=-78.40
63
67
OPENSKY_SOURCE = (
64
68
"https://opensky-network.org/api/states/all?"
65
69
+ "lamin="
66
- + latmin
70
+ + LATMIN
67
71
+ "&lomin="
68
- + lonmin
72
+ + LONMIN
69
73
+ "&lamax="
70
- + latmax
74
+ + LATMAX
71
75
+ "&lomax="
72
- + lonmax
76
+ + LONMAX
73
77
)
74
78
75
79
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"""
78
82
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"
91
89
92
90
93
91
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} "
101
100
)
102
101
103
102
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 ("\n Connecting 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
+
109
114
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" )
116
122
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!" )
124
124
125
- print ("\n Attempting 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 )
127
128
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 } " )
133
133
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 } " )
140
149
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" ]
144
152
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 } " )
147
162
148
163
if osn_all_flights is not None :
149
164
# print("Flight Data: ", osn_all_flights)
150
165
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 ]} "
153
168
osn_o = f"Origin:{ flights [2 ]} "
154
169
osn_tm = f"Time:{ flights [3 ]} "
155
170
osn_l = f"Last:{ flights [4 ]} "
@@ -171,16 +186,20 @@ def _format_datetime(datetime):
171
186
string2 = f"{ osn_la } { osn_ba } { osn_g } { osn_v } { osn_h } { osn_vr } "
172
187
string3 = f"{ osn_s } { osn_ga } { osn_sq } { osn_pr } { osn_ps } { osn_ca } "
173
188
print (f"{ string1 } { string2 } { string3 } " )
174
- else :
175
- print ("Flight has no active data or you're polling too fast." )
176
-
177
- print ("\n Finished!" )
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 ("\n Finished!" )
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