FlightRadar
This is a real-time flight radar script for AWTRIX-NG that uses adsb.fi to retrieve and display information about the three nearest aircraft, including flight number, distance, altitude, speed, and direction, with alerts for overhead aircraft and emergenc
| System | AWTRIX NG Scripts |
|---|---|
| Firmware | AWTRIX NG |
| Topic | News |
| Built by | Anthony |
| File | lVAkm0aR5EvW.ax · 13.5 KB |
| Icons | — |
| Published | 6 Sep 2026 |
| Downloads | 2 |
Purpose: A real-time flight radar script for AWTRIX-NG that retrieves nearby aircraft data from the adsb.fi API.
Aircraft tracking: It finds and displays the three nearest aircraft within a configurable radius.
Displayed information: Shows flight number or aircraft hex ID, distance, altitude, ground speed, and flight direction.
Automatic refresh: Aircraft data is refreshed periodically, with the refresh interval configurable from the script settings.
Overhead aircraft alert:Sends a notification when the nearest aircraft comes within the configured overhead distance.
Emergency alerts: Detects aviation emergency squawk codes 7500 (hijacking), 7600 (radio failure), and 7700 (general emergency) and displays a red alert notification.
Display behavior:Aircraft information is shown in rotating pages with an animated airplane icon and scrolling text.
Customization:Latitude, longitude, search radius, refresh interval, page duration, overhead alert distance, notification duration, and airplane color can all be configured.
lVAkm0aR5EvW.ax
# @name FlightRadar
# @desc Nearest 3 aircraft via adsb.fi
# @version 1.5-lite
# @config lat text "Latitude" default="31.2304" maxlen=16
# @config lon text "Longitude" default="121.4737" maxlen=16
# @config radius number "Radius" default=100 min=5 max=450 step=5 unit=km
# @config refresh number "Refresh Interval" default=30 min=15 max=300 unit=s
# @config page number "Page Duration" default=3 min=2 max=8 unit=s
# @config overhead number "Overhead Distance" default=5 min=1 max=30 unit=km
# @config alert bool "Overhead Alert" default=true
# @config emergency bool "Emergency Alert" default=true
# @config alertsec number "Notification Duration" default=8 min=3 max=30 unit=s
# @config tint color "Aircraft Color" default=#33AAFF
class FlightRadar
var url
var ticks
var busy
var pages
var count
var status
var error
var shown
var page_ms
var color
var nhex
var nflight
var ndist
var nalt
var nspd
var ntrk
var ndir
var last_over
var last_emg
var opts
def init()
var km = store.get("radius")
if km == nil km = 100 end
if km < 5 km = 5 end
if km > 463 km = 463 end
var nm = int(km / 1.852)
if nm < 1 nm = 1 end
if nm > 250 nm = 250 end
self.url = "https://opendata.adsb.fi/api/v3/lat/" +
store.get("lat") + "/lon/" + store.get("lon") +
"/dist/" + str(nm)
self.ticks = 0
self.busy = false
self.pages = []
self.count = 0
self.status = "SCAN..."
self.error = false
self.shown = now_ms()
self.page_ms = store.get("page") * 1000
self.color = store.get("tint")
if self.color == nil self.color = 0x33AAFF end
self.nhex = nil
self.nflight = nil
self.ndist = nil
self.nalt = nil
self.nspd = nil
self.ntrk = nil
self.ndir = ""
self.last_over = nil
self.last_emg = nil
self.opts = {
"mode":"wrap",
"speed":75,
"holdMs":500
}
shared.set("active", false)
shared.set("count", 0)
shared.set("status", "scan")
end
def dir(v)
if v == nil return "" end
if v >= 337.5 || v < 22.5 return "N" end
if v < 67.5 return "NE" end
if v < 112.5 return "E" end
if v < 157.5 return "SE" end
if v < 202.5 return "S" end
if v < 247.5 return "SW" end
if v < 292.5 return "W" end
return "NW"
end
def emg(s)
if s == "7500" return "HIJACK" end
if s == "7600" return "RADIO" end
if s == "7700" return "EMERG" end
return nil
end
def km1(v)
var n = int(v * 10 + 0.5)
return str(int(n / 10)) + "." + str(n % 10) + "KM"
end
def on_body(body, st)
self.busy = false
if st == 0
self.error = true
self.status = "NET ERR"
shared.set("status", "network")
return
end
if st != 200
self.error = true
self.status = "HTTP " + str(st)
shared.set("status", self.status)
return
end
if body == nil
self.pages = []
self.count = 0
self.status = "NO FLT"
self.error = false
self.nhex = nil
self.last_over = nil
self.last_emg = nil
shared.set("active", false)
shared.set("count", 0)
shared.set("status", "empty")
return
end
var ps = re.matchall(
"[{][^{}]*\"hex\":\"[A-Za-z0-9]+\"[^{}]*[}]",
body
)
if ps == nil || size(ps) == 0
self.pages = []
self.count = 0
self.status = "NO FLT"
self.error = false
shared.set("active", false)
shared.set("count", 0)
shared.set("status", "empty")
return
end
var bo = [nil,nil,nil]
var bd = [999999.0,999999.0,999999.0]
var fb = []
var ehex = nil
var eflight = nil
var esq = nil
for p : ps
if size(fb) < 3
fb.push(p)
end
var q = re.search(
"\"squawk\":\"([0-9]+)\"",
p
)
if q != nil &&
self.emg(q[1]) != nil &&
esq == nil
esq = q[1]
var eh = re.search(
"\"hex\":\"([A-Za-z0-9]+)",
p
)
if eh != nil
ehex = eh[1]
end
var ef = re.search(
"\"flight\":\"[ ]*([A-Za-z0-9]+)",
p
)
if ef != nil
eflight = ef[1]
else
eflight = ehex
end
end
var md = re.search(
"\"dst\":([0-9.]+)",
p
)
if md != nil
var d = num(md[1])
if d != nil
if d < bd[0]
bd[2] = bd[1]
bo[2] = bo[1]
bd[1] = bd[0]
bo[1] = bo[0]
bd[0] = d
bo[0] = p
elif d < bd[1]
bd[2] = bd[1]
bo[2] = bo[1]
bd[1] = d
bo[1] = p
elif d < bd[2]
bd[2] = d
bo[2] = p
end
end
end
end
if bo[0] == nil
var j = 0
while j < size(fb) && j < 3
bo[j] = fb[j]
bd[j] = -1
j += 1
end
end
var out = []
var c = 0
var i = 0
self.nhex = nil
self.nflight = nil
self.ndist = nil
self.nalt = nil
self.nspd = nil
self.ntrk = nil
self.ndir = ""
while i < 3
var p = bo[i]
if p != nil
var mh = re.search(
"\"hex\":\"([A-Za-z0-9]+)",
p
)
if mh != nil
var hx = mh[1]
var mf = re.search(
"\"flight\":\"[ ]*([A-Za-z0-9]+)",
p
)
var fl = hx
if mf != nil
fl = mf[1]
end
var dist = nil
if bd[i] >= 0
dist = bd[i] * 1.852
end
var alt = nil
var ma = re.search(
"\"alt_baro\":([-0-9.]+)",
p
)
if ma != nil
alt = num(ma[1])
else
ma = re.search(
"\"alt_geom\":([-0-9.]+)",
p
)
if ma != nil
alt = num(ma[1])
end
end
var sp = nil
var ms = re.search(
"\"gs\":([0-9.]+)",
p
)
if ms != nil
sp = num(ms[1])
end
var tr = nil
var mt = re.search(
"\"track\":([0-9.]+)",
p
)
if mt != nil
tr = num(mt[1])
end
var di = self.dir(tr)
# Page 1: flight + distance
var p1 =
str(i + 1) + " " + fl
if dist != nil
p1 =
p1 + " " +
self.km1(dist)
end
# Page 2: altitude + speed + direction
var p2 = str(i + 1)
if alt != nil
if alt >= 1000
p2 =
p2 + " " +
str(
int(
(alt + 500) /
1000
)
) +
"K"
else
p2 =
p2 + " " +
str(int(alt)) +
"FT"
end
end
if sp != nil
p2 =
p2 + " " +
str(int(sp)) +
"KT"
end
if di != ""
p2 =
p2 + " " +
di
end
out.push(p1)
out.push(p2)
c += 1
# Nearest aircraft
if i == 0
self.nhex = hx
self.nflight = fl
self.ndist = dist
self.nalt = alt
self.nspd = sp
self.ntrk = tr
self.ndir = di
end
end
end
i += 1
end
self.pages = out
self.count = c
self.error = false
if c > 0
self.status = "LIVE"
shared.set("status", "ok")
else
self.status = "NO FLT"
shared.set("status", "empty")
end
shared.set("active", c > 0)
shared.set("count", c)
# Shared: nearest aircraft
if self.nhex != nil
shared.set(
"flight",
self.nflight
)
shared.set(
"hex",
self.nhex
)
if self.ndist != nil
shared.set(
"distance_km",
self.ndist
)
else
shared.set(
"distance_km",
-1
)
end
if self.nalt != nil
shared.set(
"altitude_ft",
self.nalt
)
else
shared.set(
"altitude_ft",
-1
)
end
if self.nspd != nil
shared.set(
"speed_kt",
self.nspd
)
else
shared.set(
"speed_kt",
-1
)
end
if self.ntrk != nil
shared.set(
"heading_deg",
self.ntrk
)
else
shared.set(
"heading_deg",
-1
)
end
shared.set(
"direction",
self.ndir
)
end
# ==========================================
# Overhead aircraft alert
# ==========================================
if store.get("alert") &&
self.nhex != nil &&
self.ndist != nil &&
self.ndist <= store.get("overhead")
if self.nhex != self.last_over
notify({
"text":
"OVERHEAD " +
self.nflight +
" " +
self.km1(self.ndist),
"textColor":
0xFFFF00,
"durationMs":
store.get("alertsec") *
1000,
"wakeup":
true,
"stack":
false
})
self.last_over =
self.nhex
end
elif self.ndist == nil ||
self.ndist > store.get("overhead")
self.last_over = nil
end
# ==========================================
# 7500 / 7600 / 7700
# ==========================================
if esq != nil &&
store.get("emergency")
var eid =
ehex + ":" + esq
if eid != self.last_emg
notify({
"text":
self.emg(esq) +
" " +
esq +
" " +
eflight,
"textColor":
0xFF0000,
"durationMs":
store.get("alertsec") *
1000,
"wakeup":
true,
"stack":
false
})
self.last_emg = eid
end
else
self.last_emg = nil
end
end
# ============================================
# Periodically fetch aircraft data
# ============================================
def loop()
if self.ticks <= 0
var r = store.get("refresh")
if r == nil || r < 15
r = 30
end
self.ticks = r
if !self.busy
self.busy = true
if self.count == 0 &&
!self.error
self.status =
"SCAN..."
end
http.get(
self.url,
/ b, st ->
self.on_body(b, st),
{
"find":
"\"ac\":[",
"keep":
8192,
"headers":{
"User-Agent":
"AWTRIX-NG/FlightRadar",
"Accept":
"application/json"
}
}
)
end
end
self.ticks -= 1
end
def on_show()
self.shown = now_ms()
end
# Show all aircraft pages before switching to the next app
def duration()
var n = size(self.pages)
if n < 1
return self.page_ms
end
return self.page_ms * n
end
# ============================================
# Aircraft icon
# ============================================
def draw_plane()
var x =
int(now_ms() / 500) % 2
# Fuselage
line(
x + 3,
0,
x + 3,
7,
self.color
)
# Wings
line(
x,
3,
x + 6,
3,
self.color
)
pixel(
x + 1,
2,
self.color
)
pixel(
x + 5,
2,
self.color
)
# Tail
line(
x + 2,
6,
x + 4,
6,
self.color
)
# Red flashing light
if now_ms() % 1000 < 150
pixel(
x + 6,
3,
0xFF0000
)
end
end
# ============================================
# Rendering
# ============================================
def draw()
clear()
self.draw_plane()
if self.error
pixel(
width() - 1,
0,
0xFF0000
)
end
var label =
self.status
var col =
0x666666
var n =
size(self.pages)
if n > 0
var p =
int(
(now_ms() - self.shown) /
self.page_ms
) % n
label =
self.pages[p]
col =
settings.get("textColor")
if col == nil
col = 0xFFFFFF
end
elif self.error
col = 0xFF4444
end
scroll_text(
8,
6,
width() - 8,
label,
col,
self.opts
)
end
end
return FlightRadar()
Install it on your AWTRIX NG
Your browser writes the script straight to the device on your network. It joins the app rotation right away.
Some browsers refuse to talk to a device on your network from a public page. Download the flow and paste it into the Scripts tab of your device, or install it from a terminal:
More flows for AWTRIX NG Scripts or News
Something wrong with this flow? Report it.