gluon-packages/libs/lua-geolocate/src/geolocate.lua

88 lines
1.9 KiB
Lua

local iwinfo = require 'iwinfo'
local json = require 'jsonc'
local ubus = require 'ubus'
local function canon_bssid(bssid)
return bssid:upper():gsub(':', '')
end
-- Iterates over all active WLAN interfaces
-- Returning true from the callback function will skip all remaining
-- interfaces of the same radio
local function foreach_radio(f)
local uconn = assert(ubus.connect(), 'failed to connect to ubus')
local status = uconn:call('network.wireless', 'status', {})
ubus.close(uconn)
for name, radio in pairs(status) do
for _, iface in ipairs(radio.interfaces) do
if f(iface.ifname) then
break
end
end
end
end
local function receive_json(request)
local f = assert(io.popen(string.format("exec wget -T 15 -q -O- '%s'", request)), 'failed to run wget')
local data = f:read('*a')
f:close()
return json.parse(data)
end
local function locate(blacklist)
local done_bssids = {}
for _, bssid in ipairs(blacklist or {}) do
done_bssids[canon_bssid(bssid)] = true
end
local found_bssids = {}
foreach_radio(function(ifname)
local iw = iwinfo[iwinfo.type(ifname)]
if not iw then
-- Skip other ifaces of this radio, as they
-- will have the same type
return true
end
local scanlist = iw.scanlist(ifname)
if not scanlist then
return false
end
for _, entry in ipairs(scanlist) do
local bssid = canon_bssid(entry.bssid)
if not done_bssids[bssid] then
table.insert(found_bssids, bssid)
done_bssids[bssid] = true
end
end
return true
end)
assert(#found_bssids >= 12, 'insufficient BSSIDs found')
local data = receive_json('http://openwifi.su/api/v1/bssids/' .. table.concat(found_bssids, ','))
assert(type(data) == 'table' and data.lon and data.lat, 'location not available')
return data
end
local geolocate = {}
function geolocate.locate(blacklist)
ok, result = pcall(locate, blacklist)
if ok then
return result
else
return nil, result
end
end
return geolocate