Seed Buildroot config from repo installer

This commit is contained in:
Yellowcooln
2026-04-23 15:57:21 -04:00
parent e5c7632700
commit cc9c81de2a
+279
View File
@@ -68,6 +68,71 @@ is_buildroot() {
return 1
}
prompt_value() {
local prompt="$1"
local default_value="${2:-}"
local reply=""
if [ -n "${reply:-}" ]; then
:
fi
if [ ! -t 0 ]; then
printf '%s\n' "$default_value"
return 0
fi
if [ -n "$default_value" ]; then
printf '%s [%s]: ' "$prompt" "$default_value" >&2
else
printf '%s: ' "$prompt" >&2
fi
IFS= read -r reply || true
reply=${reply:-$default_value}
printf '%s\n' "$reply"
}
prompt_secret() {
local prompt="$1"
local first=""
local second=""
if [ ! -t 0 ]; then
fail "Interactive admin password prompt requires a TTY. Set PYMC_ADMIN_PASSWORD instead."
fi
while true; do
read -r -s -p "${prompt}: " first >&2
printf '\n' >&2
read -r -s -p "Confirm ${prompt,,}: " second >&2
printf '\n' >&2
[ -n "$first" ] || {
warn "Password cannot be empty."
continue
}
[ "${#first}" -ge 6 ] || {
warn "Password must be at least 6 characters."
continue
}
[ "$first" = "$second" ] || {
warn "Passwords do not match."
continue
}
printf '%s\n' "$first"
return 0
done
}
normalize_radio_profile() {
case "$1" in
1|v2|V2|pimesh-v2|pimesh_v2|pimesh2|PiMeshV2) printf 'v2\n' ;;
2|v1|V1|meshadv|MeshAdv|pimesh-v1|pimesh_v1|pimesh1) printf 'v1\n' ;;
*) return 1 ;;
esac
}
ensure_root() {
[ "$(id -u 2>/dev/null || echo 1)" -eq 0 ] || fail "This command must be run as root."
}
@@ -367,6 +432,210 @@ get_primary_ip() {
ip -o -4 addr show dev eth0 2>/dev/null | awk 'NR==1 { sub(/\/.*/, "", $4); print $4; exit }'
}
get_config_value() {
local key_path="$1"
local fallback="${2:-}"
python3 - "$CONFIG_DIR/config.yaml" "$key_path" "$fallback" <<'PY'
import sys
import yaml
config_path, key_path, fallback = sys.argv[1:4]
try:
with open(config_path, "r", encoding="utf-8") as fh:
data = yaml.safe_load(fh) or {}
except FileNotFoundError:
print(fallback)
raise SystemExit(0)
current = data
for part in key_path.split("."):
if isinstance(current, dict) and part in current:
current = current[part]
else:
print(fallback)
raise SystemExit(0)
if current in (None, ""):
print(fallback)
elif isinstance(current, float):
rendered = ("%f" % current).rstrip("0").rstrip(".")
print(rendered or "0")
else:
print(current)
PY
}
get_radio_frequency_mhz() {
python3 - "$CONFIG_DIR/config.yaml" <<'PY'
import yaml
with open(__import__("sys").argv[1], "r", encoding="utf-8") as fh:
data = yaml.safe_load(fh) or {}
freq = ((data.get("radio") or {}).get("frequency")) or 910525000
print(f"{float(freq) / 1_000_000:.3f}".rstrip("0").rstrip("."))
PY
}
get_radio_bandwidth_khz() {
python3 - "$CONFIG_DIR/config.yaml" <<'PY'
import yaml
with open(__import__("sys").argv[1], "r", encoding="utf-8") as fh:
data = yaml.safe_load(fh) or {}
bw = ((data.get("radio") or {}).get("bandwidth")) or 62500
print(f"{float(bw) / 1000:.3f}".rstrip("0").rstrip("."))
PY
}
write_repeater_config() {
local node_name="$1"
local admin_password="$2"
local jwt_secret="$3"
local freq_mhz="$4"
local sf="$5"
local bw_khz="$6"
local coding_rate="$7"
local tx_power="$8"
local profile="$9"
python3 - "$CONFIG_DIR/config.yaml" "$node_name" "$admin_password" "$jwt_secret" "$freq_mhz" "$sf" "$bw_khz" "$coding_rate" "$tx_power" "$profile" <<'PY'
import sys
import yaml
config_path, node_name, admin_password, jwt_secret, freq_mhz, sf, bw_khz, coding_rate, tx_power, profile = sys.argv[1:11]
with open(config_path, "r", encoding="utf-8") as fh:
data = yaml.safe_load(fh) or {}
repeater = data.setdefault("repeater", {})
security = repeater.setdefault("security", {})
radio = data.setdefault("radio", {})
sx1262 = data.setdefault("sx1262", {})
repeater["node_name"] = node_name
security["admin_password"] = admin_password
security["jwt_secret"] = jwt_secret
radio["frequency"] = int(round(float(freq_mhz) * 1_000_000))
radio["spreading_factor"] = int(sf)
radio["bandwidth"] = int(round(float(bw_khz) * 1000))
radio["coding_rate"] = int(coding_rate)
radio["tx_power"] = int(tx_power)
sx1262["bus_id"] = 0
sx1262["cs_id"] = 0
sx1262["txled_pin"] = -1
sx1262["rxled_pin"] = -1
sx1262["dio3_tcxo_voltage"] = 1.8
sx1262["use_dio3_tcxo"] = True
sx1262["is_waveshare"] = False
if profile == "v2":
sx1262["cs_pin"] = -1
sx1262["reset_pin"] = 54
sx1262["busy_pin"] = 122
sx1262["irq_pin"] = 121
sx1262["en_pin"] = 0
sx1262["txen_pin"] = -1
sx1262["rxen_pin"] = -1
sx1262["use_dio2_rf"] = True
else:
sx1262["cs_pin"] = 145
sx1262["reset_pin"] = 54
sx1262["busy_pin"] = 123
sx1262["irq_pin"] = 55
sx1262["en_pin"] = -1
sx1262["txen_pin"] = 52
sx1262["rxen_pin"] = 53
sx1262["use_dio2_rf"] = False
with open(config_path, "w", encoding="utf-8") as fh:
yaml.safe_dump(data, fh, sort_keys=False)
PY
}
select_radio_profile() {
local choice="${LUCKFOX_RADIO_PROFILE:-${PYMC_RADIO_PROFILE:-}}"
if [ -n "$choice" ]; then
normalize_radio_profile "$choice" || fail "Unknown radio profile choice: $choice"
return 0
fi
printf 'Select Luckfox radio profile:\n' >&2
printf ' 1) PiMesh V2\n' >&2
printf ' 2) PiMesh V1 / MeshAdv\n' >&2
choice=$(prompt_value "Profile" "1")
normalize_radio_profile "$choice" || fail "Unknown radio profile choice: $choice"
}
seed_repeater_config() {
local node_name admin_password jwt_secret profile freq_mhz sf bw_khz coding_rate tx_power
stage "Configuring repeater"
node_name="${PYMC_NODE_NAME:-}"
[ -n "$node_name" ] || node_name=$(prompt_value "Repeater name" "$(get_config_value repeater.node_name luckfox-repeater)")
[ -n "$node_name" ] || fail "Repeater name cannot be empty."
profile=$(select_radio_profile)
admin_password="${PYMC_ADMIN_PASSWORD:-}"
[ -n "$admin_password" ] || admin_password=$(prompt_secret "Admin password")
jwt_secret="${PYMC_JWT_SECRET:-}"
[ -n "$jwt_secret" ] || jwt_secret=$(python3 -c 'import secrets; print(secrets.token_hex(32))')
freq_mhz="${PYMC_RADIO_FREQUENCY_MHZ:-}"
[ -n "$freq_mhz" ] || freq_mhz=$(prompt_value "Frequency MHz" "$(get_radio_frequency_mhz)")
sf="${PYMC_RADIO_SF:-}"
[ -n "$sf" ] || sf=$(prompt_value "Spreading factor" "$(get_config_value radio.spreading_factor 7)")
bw_khz="${PYMC_RADIO_BANDWIDTH_KHZ:-}"
[ -n "$bw_khz" ] || bw_khz=$(prompt_value "Bandwidth kHz" "$(get_radio_bandwidth_khz)")
coding_rate="${PYMC_RADIO_CODING_RATE:-}"
[ -n "$coding_rate" ] || coding_rate=$(prompt_value "Coding rate" "$(get_config_value radio.coding_rate 5)")
tx_power="${PYMC_RADIO_TX_POWER_DBM:-}"
[ -n "$tx_power" ] || tx_power=$(prompt_value "TX power dBm" "$(get_config_value radio.tx_power 22)")
write_repeater_config "$node_name" "$admin_password" "$jwt_secret" "$freq_mhz" "$sf" "$bw_khz" "$coding_rate" "$tx_power" "$profile"
info "Saved config for ${node_name}"
}
configure_repeater() {
ensure_root
[ -f "$CONFIG_DIR/config.yaml" ] || fail "Config file is missing. Run install first."
seed_repeater_config
if service_exists; then
"$INIT_SCRIPT" restart
fi
}
configure_radio_profile() {
local profile
ensure_root
[ -f "$CONFIG_DIR/config.yaml" ] || fail "Config file is missing. Run install first."
profile=$(select_radio_profile)
write_repeater_config \
"$(get_config_value repeater.node_name luckfox-repeater)" \
"$(get_config_value repeater.security.admin_password admin123)" \
"$(get_config_value repeater.security.jwt_secret "$(python3 -c 'import secrets; print(secrets.token_hex(32))')")" \
"$(get_radio_frequency_mhz)" \
"$(get_config_value radio.spreading_factor 7)" \
"$(get_radio_bandwidth_khz)" \
"$(get_config_value radio.coding_rate 5)" \
"$(get_config_value radio.tx_power 22)" \
"$profile"
info "Applied Luckfox radio profile: $profile"
if service_exists; then
"$INIT_SCRIPT" restart
fi
}
service_exists() {
[ -x "$INIT_SCRIPT" ]
}
@@ -520,6 +789,8 @@ install_repeater() {
fail "Installed packages are present but one or more native modules are unusable on this image."
fi
seed_repeater_config
stage "Writing Buildroot init service"
create_init_script
@@ -619,6 +890,8 @@ Commands:
doctor Check Buildroot/Luckfox prerequisites
install Install pyMC Repeater on the Buildroot image
upgrade Upgrade the Buildroot installation from the checked-out repo
configure Prompt for repeater settings and rewrite config.yaml
radio-profile Reapply the Luckfox radio pin profile only
config Run the stock interactive config flow
start Start the init.d service
stop Stop the init.d service
@@ -640,6 +913,12 @@ case "${1:-}" in
upgrade)
upgrade_repeater
;;
configure)
configure_repeater
;;
radio-profile)
configure_radio_profile
;;
config)
shift
delegate_to_stock_manage config "$@"