From 7432d7ab83118271f28e4164cf9caa445667f0c5 Mon Sep 17 00:00:00 2001 From: Travis Herbranson Date: Tue, 30 Jun 2026 05:49:51 -0400 Subject: [PATCH] initial commit --- .gitignore | 10 ++++++ .python-version | 1 + README.md | 0 main.py | 6 ++++ pyproject.toml | 7 ++++ uv.lock | 8 +++++ wifi-dns | 91 +++++++++++++++++++++++++++++++++++++++++++++++++ wifi-join | 73 +++++++++++++++++++++++++++++++++++++++ wifi-scan | 53 ++++++++++++++++++++++++++++ wifi-status | 38 +++++++++++++++++++++ 10 files changed, 287 insertions(+) create mode 100644 .gitignore create mode 100644 .python-version create mode 100644 README.md create mode 100644 main.py create mode 100644 pyproject.toml create mode 100644 uv.lock create mode 100755 wifi-dns create mode 100755 wifi-join create mode 100755 wifi-scan create mode 100755 wifi-status diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..505a3b1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +# Python-generated files +__pycache__/ +*.py[oc] +build/ +dist/ +wheels/ +*.egg-info + +# Virtual environments +.venv diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..e4fba21 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.12 diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/main.py b/main.py new file mode 100644 index 0000000..c88fcff --- /dev/null +++ b/main.py @@ -0,0 +1,6 @@ +def main(): + print("Hello from pi-modem!") + + +if __name__ == "__main__": + main() diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..2878e79 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,7 @@ +[project] +name = "pi-modem" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +requires-python = ">=3.12" +dependencies = [] diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..41ef124 --- /dev/null +++ b/uv.lock @@ -0,0 +1,8 @@ +version = 1 +revision = 3 +requires-python = ">=3.12" + +[[package]] +name = "pi-modem" +version = "0.1.0" +source = { virtual = "." } diff --git a/wifi-dns b/wifi-dns new file mode 100755 index 0000000..1011557 --- /dev/null +++ b/wifi-dns @@ -0,0 +1,91 @@ +#!/usr/bin/lua + +local json = require("luci.jsonc") + +local function sh(cmd) + local h = io.popen(cmd .. " 2>&1") + local out = h:read("*a") + local ok = h:close() + return ok, out +end + +local function get_servers() + local h = io.popen("uci -q get dhcp.@dnsmasq[0].server 2>/dev/null") + local out = h:read("*a") or "" + h:close() + local list = {} + for s in out:gmatch("%S+") do list[#list+1] = s end + return list +end + +local function get_noresolv() + local h = io.popen("uci -q get dhcp.@dnsmasq[0].noresolv 2>/dev/null") + local out = (h:read("*a") or ""):gsub("%s+", "") + h:close() + return out == "1" +end + +local function read_state() + return { + noresolv = get_noresolv(), + servers = get_servers(), + } +end + +io.write("Content-Type: application/json\r\n\r\n") + +local body = io.read("*a") or "" +local req = json.parse(body) or {} +local action = req.action or "status" + +local ADGUARD = "127.0.0.1#5335" + +if action == "status" then + io.write(json.stringify({ ok = true, state = read_state() })) + os.exit(0) +end + +local function set_noresolv(v) + sh("uci set dhcp.@dnsmasq[0].noresolv='" .. (v and "1" or "0") .. "'") +end + +local function clear_servers() + sh("uci -q delete dhcp.@dnsmasq[0].server") +end + +local function add_server(s) + sh("uci add_list dhcp.@dnsmasq[0].server='" .. s .. "'") +end + +local function apply() + sh("uci commit dhcp") + sh("/etc/init.d/dnsmasq restart") +end + +if action == "normal" then + clear_servers() + add_server(ADGUARD) + set_noresolv(true) + apply() + +elseif action == "portal" then + clear_servers() + set_noresolv(false) + apply() + +elseif action == "set" then + if req.noresolv ~= nil then set_noresolv(req.noresolv and true or false) end + if req.servers ~= nil then + clear_servers() + for _, s in ipairs(req.servers) do + if s and s ~= "" then add_server(s) end + end + end + apply() + +else + io.write(json.stringify({ ok = false, error = "unknown action: " .. tostring(action) })) + os.exit(0) +end + +io.write(json.stringify({ ok = true, action = action, state = read_state() })) diff --git a/wifi-join b/wifi-join new file mode 100755 index 0000000..9fb1bd1 --- /dev/null +++ b/wifi-join @@ -0,0 +1,73 @@ +#!/usr/bin/lua + +local json = require("luci.jsonc") + +local function uci(cmd) + local h = io.popen("uci " .. cmd .. " 2>&1") + local out = h:read("*a") + local ok = h:close() + return ok, out +end + +local function next_uplink_index() + local h = io.popen("uci show wireless 2>/dev/null | grep -o 'trm_uplink[0-9]*' | grep -o '[0-9]*'") + local used = {} + for n in h:lines() do used[tonumber(n)] = true end + h:close() + local i = 0 + while used[i] do i = i + 1 end + return i +end + +io.write("Content-Type: application/json\r\n\r\n") + +local body = io.read("*a") or "" +local req = json.parse(body) + +if not req or not req.ssid or req.ssid == "" then + io.write(json.stringify({ ok = false, error = "missing ssid" })) + os.exit(0) +end + +local ssid = req.ssid +local key = req.key +local secured = req.secured and true or false + +if secured and (not key or key == "") then + io.write(json.stringify({ ok = false, error = "secured network requires key" })) + os.exit(0) +end + +local idx = next_uplink_index() +local sec = "wireless.trm_uplink" .. idx + +local function q(s) return "'" .. tostring(s):gsub("'", "'\\''") .. "'" end + +uci("set " .. sec .. "=wifi-iface") +uci("set " .. sec .. ".device='radio0'") +uci("set " .. sec .. ".mode='sta'") +uci("set " .. sec .. ".network='tm_wwan'") +uci("set " .. sec .. ".ssid=" .. q(ssid)) +uci("set " .. sec .. ".disabled='0'") +if secured then + uci("set " .. sec .. ".encryption='psk2+ccmp'") + uci("set " .. sec .. ".key=" .. q(key)) +else + uci("set " .. sec .. ".encryption='none'") +end + +local up = "travelmate.trm_uplink" .. idx +uci("set " .. up .. "=uplink") +uci("set " .. up .. ".device='radio0'") +uci("set " .. up .. ".ssid=" .. q(ssid)) +uci("set " .. up .. ".enabled='1'") + +local ok_w = uci("commit wireless") +local ok_t = uci("commit travelmate") + +io.write(json.stringify({ + ok = (ok_w and ok_t) and true or false, + ssid = ssid, + section = "trm_uplink" .. idx, + secured = secured, +})) diff --git a/wifi-scan b/wifi-scan new file mode 100755 index 0000000..92d62f9 --- /dev/null +++ b/wifi-scan @@ -0,0 +1,53 @@ +#!/usr/bin/lua + +local function read_scan() + local h = io.popen("ubus call iwinfo scan '{\"device\":\"radio0\"}' 2>/dev/null") + local raw = h:read("*a") + h:close() + return raw +end + +local function jsondec(s) + local json = require("luci.jsonc") + return json.parse(s) +end + +local function jsonenc(t) + local json = require("luci.jsonc") + return json.stringify(t) +end + +io.write("Content-Type: application/json\r\n\r\n") + +local raw = read_scan() +local data = raw and jsondec(raw) + +if not data or not data.results then + io.write(jsonenc({ ok = false, error = "scan failed or no results" })) + os.exit(0) +end + +local best = {} +for _, n in ipairs(data.results) do + local ssid = n.ssid + if ssid and ssid ~= "" then + local prev = best[ssid] + if not prev or (n.signal or -999) > prev.signal then + best[ssid] = { + ssid = ssid, + signal = n.signal or -999, + quality = n.quality or 0, + quality_max = n.quality_max or 70, + band = n.band, + channel = n.channel, + secured = n.encryption and n.encryption.enabled or false, + } + end + end +end + +local list = {} +for _, v in pairs(best) do list[#list+1] = v end +table.sort(list, function(a, b) return a.signal > b.signal end) + +io.write(jsonenc({ ok = true, networks = list })) diff --git a/wifi-status b/wifi-status new file mode 100755 index 0000000..03450d2 --- /dev/null +++ b/wifi-status @@ -0,0 +1,38 @@ +#!/usr/bin/lua + +local json = require("luci.jsonc") + +io.write("Content-Type: application/json\r\n\r\n") + +local f = io.open("/tmp/trm_runtime.json", "r") +if not f then + io.write(json.stringify({ ok = false, error = "no runtime file" })) + os.exit(0) +end + +local raw = f:read("*a") +f:close() + +local rt = json.parse(raw) +if not rt or not rt.data then + io.write(json.stringify({ ok = false, error = "bad runtime json" })) + os.exit(0) +end + +local d = rt.data +local status = d.travelmate_status or "" +local connected = status:match("^connected") ~= nil +local net_ok = status:match("net ok") ~= nil + +local ssid = nil +if d.station_id then + ssid = d.station_id:match("radio0/(.+)/%-") or d.station_id:match("radio0/(.+)/") +end + +io.write(json.stringify({ + ok = true, + connected = connected, + net_ok = net_ok, + ssid = ssid, + raw_status = status, +}))