mirror of
https://github.com/pyMC-dev/pyMC_Repeater.git
synced 2026-08-06 08:53:46 +02:00
Merge pull request #212 from yellowcooln/dev
Improve Buildroot repeater config flow and Pico Pi radio timing wiring
This commit is contained in:
+98
-3
@@ -21,6 +21,7 @@ SILENT_MODE="${PYMC_SILENT:-${SILENT:-}}"
|
||||
R2_BASE_URL="https://wheel.pymc.dev/pymc_build_deps"
|
||||
PIWHEELS_INDEX_URL="https://www.piwheels.org/simple"
|
||||
R2_ENABLED=1
|
||||
YQ_VERSION="${YQ_VERSION:-v4.44.3}"
|
||||
PYMC_CORE_REPO="${PYMC_CORE_REPO:-https://github.com/rightup/pyMC_core.git}"
|
||||
PYMC_CORE_REF="${PYMC_CORE_REF:-}"
|
||||
RADIO_SETTINGS_JSON="$SCRIPT_DIR/radio-settings.json"
|
||||
@@ -148,6 +149,16 @@ need_cmd() {
|
||||
command -v "$1" >/dev/null 2>&1 || fail "Missing required command: $1"
|
||||
}
|
||||
|
||||
normalize_interactive_tty() {
|
||||
[ -t 0 ] || return 0
|
||||
[ -r /dev/tty ] || return 0
|
||||
|
||||
# Buildroot shells sometimes end up with a bad erase character, which
|
||||
# makes backspace print ^H during interactive prompts.
|
||||
stty sane < /dev/tty >/dev/null 2>&1 || true
|
||||
stty erase '^H' < /dev/tty >/dev/null 2>&1 || true
|
||||
}
|
||||
|
||||
is_buildroot() {
|
||||
[ -f /etc/pymc-image-build-id ] && return 0
|
||||
[ -f /etc/os-release ] && grep -q '^ID=buildroot$' /etc/os-release 2>/dev/null && return 0
|
||||
@@ -164,6 +175,8 @@ prompt_value() {
|
||||
return 0
|
||||
fi
|
||||
|
||||
normalize_interactive_tty
|
||||
|
||||
if [ -n "$default_value" ]; then
|
||||
printf '%s [%s]: ' "$prompt" "$default_value" >&2
|
||||
else
|
||||
@@ -177,6 +190,8 @@ prompt_value() {
|
||||
prompt_secret() {
|
||||
local prompt="$1"
|
||||
|
||||
normalize_interactive_tty
|
||||
|
||||
python3 - "$prompt" <<'PY'
|
||||
import os
|
||||
import sys
|
||||
@@ -350,6 +365,59 @@ install_system_packages() {
|
||||
libffi-dev libusb-1.0-0 sudo jq pip python3-venv python3-rrdtool wget swig build-essential python3-dev
|
||||
}
|
||||
|
||||
get_yq_cmd() {
|
||||
local candidate
|
||||
|
||||
for candidate in /usr/local/bin/yq /usr/bin/yq; do
|
||||
if [ -x "$candidate" ] && "$candidate" --version 2>&1 | grep -q "mikefarah/yq"; then
|
||||
printf '%s\n' "$candidate"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
ensure_yq() {
|
||||
local yq_cmd yq_binary
|
||||
|
||||
yq_cmd=$(get_yq_cmd 2>/dev/null || true)
|
||||
if [ -n "$yq_cmd" ]; then
|
||||
printf '%s\n' "$yq_cmd"
|
||||
return 0
|
||||
fi
|
||||
|
||||
need_cmd wget
|
||||
|
||||
case "$(uname -m)" in
|
||||
aarch64|arm64) yq_binary="yq_linux_arm64" ;;
|
||||
x86_64|amd64) yq_binary="yq_linux_amd64" ;;
|
||||
armv7l|armv7) yq_binary="yq_linux_arm" ;;
|
||||
*)
|
||||
warn "Unable to install yq automatically on unsupported architecture: $(uname -m)"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
printf '\n==> Installing yq\n' >&2
|
||||
printf ' - Fetching mikefarah/yq %s\n' "$YQ_VERSION" >&2
|
||||
wget -qO /usr/local/bin/yq "https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/${yq_binary}" || {
|
||||
warn "Failed to download yq; config updates will keep working but comments will not be preserved."
|
||||
rm -f /usr/local/bin/yq
|
||||
return 1
|
||||
}
|
||||
chmod +x /usr/local/bin/yq
|
||||
|
||||
yq_cmd=$(get_yq_cmd 2>/dev/null || true)
|
||||
if [ -n "$yq_cmd" ]; then
|
||||
printf '%s\n' "$yq_cmd"
|
||||
return 0
|
||||
fi
|
||||
|
||||
warn "Installed yq is not the expected mikefarah/yq binary."
|
||||
return 1
|
||||
}
|
||||
|
||||
ensure_venv() {
|
||||
local recreate=0
|
||||
|
||||
@@ -937,13 +1005,22 @@ write_repeater_config() {
|
||||
local tx_power="$8"
|
||||
local board_key="$9"
|
||||
|
||||
python3 - "$CONFIG_DIR/config.yaml" "$RADIO_SETTINGS_JSON" "$BUILDROOT_RADIO_SETTINGS_JSON" "$node_name" "$admin_password" "$jwt_secret" "$freq_mhz" "$sf" "$bw_khz" "$coding_rate" "$tx_power" "$board_key" <<'PY'
|
||||
local config_path temp_config example_config yq_cmd stripped_user temp_merged
|
||||
|
||||
config_path="$CONFIG_DIR/config.yaml"
|
||||
example_config="$CONFIG_DIR/config.yaml.example"
|
||||
temp_config=$(mktemp)
|
||||
stripped_user=$(mktemp)
|
||||
temp_merged=$(mktemp)
|
||||
|
||||
python3 - "$config_path" "$temp_config" "$RADIO_SETTINGS_JSON" "$BUILDROOT_RADIO_SETTINGS_JSON" "$node_name" "$admin_password" "$jwt_secret" "$freq_mhz" "$sf" "$bw_khz" "$coding_rate" "$tx_power" "$board_key" <<'PY'
|
||||
import json
|
||||
import sys
|
||||
import yaml
|
||||
|
||||
(
|
||||
config_path,
|
||||
output_path,
|
||||
radio_settings_path,
|
||||
buildroot_settings_path,
|
||||
node_name,
|
||||
@@ -955,7 +1032,7 @@ import yaml
|
||||
coding_rate,
|
||||
tx_power,
|
||||
board_key,
|
||||
) = sys.argv[1:13]
|
||||
) = sys.argv[1:14]
|
||||
|
||||
with open(config_path, "r", encoding="utf-8") as fh:
|
||||
data = yaml.safe_load(fh) or {}
|
||||
@@ -1002,9 +1079,25 @@ if data["radio_type"] == "sx1262_ch341":
|
||||
if "pid" in hardware:
|
||||
ch341["pid"] = hardware["pid"]
|
||||
|
||||
with open(config_path, "w", encoding="utf-8") as fh:
|
||||
with open(output_path, "w", encoding="utf-8") as fh:
|
||||
yaml.safe_dump(data, fh, sort_keys=False)
|
||||
PY
|
||||
|
||||
yq_cmd=$(ensure_yq 2>/dev/null || true)
|
||||
if [ -n "$yq_cmd" ] && [ -f "$example_config" ]; then
|
||||
"$yq_cmd" eval '... comments=""' "$temp_config" > "$stripped_user" 2>/dev/null || cp "$temp_config" "$stripped_user"
|
||||
if "$yq_cmd" eval-all '. as $item ireduce ({}; . * $item)' "$example_config" "$stripped_user" > "$temp_merged" 2>/dev/null \
|
||||
&& "$yq_cmd" eval '.' "$temp_merged" >/dev/null 2>&1; then
|
||||
mv "$temp_merged" "$config_path"
|
||||
else
|
||||
warn "yq merge failed; writing config without preserving comments."
|
||||
cp "$temp_config" "$config_path"
|
||||
fi
|
||||
else
|
||||
cp "$temp_config" "$config_path"
|
||||
fi
|
||||
|
||||
rm -f "$temp_config" "$stripped_user" "$temp_merged"
|
||||
}
|
||||
|
||||
seed_repeater_config() {
|
||||
@@ -1221,6 +1314,7 @@ install_repeater() {
|
||||
ensure_root
|
||||
stage "Preparing Buildroot installation"
|
||||
install_system_packages
|
||||
ensure_yq >/dev/null 2>&1 || true
|
||||
ensure_service_user
|
||||
|
||||
stage "Preparing directories and config"
|
||||
@@ -1310,6 +1404,7 @@ upgrade_repeater() {
|
||||
if [ "$current_version" = "$git_version" ] && [ "${PYMC_FORCE_REPEATER:-0}" != "1" ]; then
|
||||
info "pyMC Repeater is already at ${git_version}; skipping reinstall"
|
||||
else
|
||||
ensure_yq >/dev/null 2>&1 || true
|
||||
install_repeater_package
|
||||
fi
|
||||
link_system_site_packages
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
"reset_pin": 54,
|
||||
"busy_pin": 122,
|
||||
"irq_pin": 121,
|
||||
"radio_timing_delay": 0.012,
|
||||
"en_pin": 0,
|
||||
"txen_pin": -1,
|
||||
"rxen_pin": -1,
|
||||
@@ -42,6 +43,7 @@
|
||||
"reset_pin": 54,
|
||||
"busy_pin": 123,
|
||||
"irq_pin": 55,
|
||||
"radio_timing_delay": 0.012,
|
||||
"en_pin": -1,
|
||||
"txen_pin": 52,
|
||||
"rxen_pin": 53,
|
||||
@@ -64,6 +66,7 @@
|
||||
"reset_pin": 54,
|
||||
"busy_pin": 123,
|
||||
"irq_pin": 55,
|
||||
"radio_timing_delay": 0.012,
|
||||
"en_pin": -1,
|
||||
"txen_pin": 52,
|
||||
"rxen_pin": 53,
|
||||
|
||||
@@ -326,6 +326,8 @@ def get_radio_for_board(board_config: dict):
|
||||
combined_config["gpio_chip"] = _parse_int(spi_config["gpio_chip"], default=0)
|
||||
if "use_gpiod_backend" in spi_config:
|
||||
combined_config["use_gpiod_backend"] = spi_config["use_gpiod_backend"]
|
||||
if "radio_timing_delay" in spi_config:
|
||||
combined_config["radio_timing_delay"] = float(spi_config["radio_timing_delay"])
|
||||
|
||||
radio = SX1262Radio.get_instance(**combined_config)
|
||||
|
||||
|
||||
@@ -6,14 +6,15 @@ Provides functions for service control operations like restart.
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
from typing import Tuple
|
||||
from typing import Dict, Optional, Tuple
|
||||
|
||||
logger = logging.getLogger("ServiceUtils")
|
||||
INIT_SCRIPT = "/etc/init.d/S80pymc-repeater"
|
||||
BUILDROOT_METADATA_PATH = "/etc/pymc-image-build-id"
|
||||
|
||||
|
||||
def is_buildroot() -> bool:
|
||||
if os.path.exists("/etc/pymc-image-build-id"):
|
||||
if os.path.exists(BUILDROOT_METADATA_PATH):
|
||||
return True
|
||||
if os.path.exists("/etc/os-release"):
|
||||
try:
|
||||
@@ -24,6 +25,27 @@ def is_buildroot() -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def get_buildroot_image_info() -> Dict[str, str]:
|
||||
info: Dict[str, str] = {}
|
||||
|
||||
try:
|
||||
with open(BUILDROOT_METADATA_PATH, "r", encoding="utf-8") as handle:
|
||||
for line in handle:
|
||||
line = line.strip()
|
||||
if not line or "=" not in line:
|
||||
continue
|
||||
key, value = line.split("=", 1)
|
||||
info[key.strip()] = value.strip()
|
||||
except OSError:
|
||||
return {}
|
||||
|
||||
return info
|
||||
|
||||
|
||||
def get_buildroot_image_version() -> Optional[str]:
|
||||
return get_buildroot_image_info().get("image_version")
|
||||
|
||||
|
||||
def restart_service() -> Tuple[bool, str]:
|
||||
"""
|
||||
Restart the pymc-repeater service.
|
||||
|
||||
@@ -16,6 +16,7 @@ from repeater.companion.identity_resolve import (
|
||||
heal_companion_empty_names,
|
||||
)
|
||||
from repeater.config import update_unscoped_flood_policy
|
||||
from repeater.service_utils import get_buildroot_image_info
|
||||
|
||||
from .auth.middleware import require_auth
|
||||
from .auth_endpoints import AuthAPIEndpoints
|
||||
@@ -625,6 +626,12 @@ class APIEndpoints:
|
||||
stats["core_version"] = pymc_core.__version__
|
||||
except ImportError:
|
||||
stats["core_version"] = "unknown"
|
||||
image_info = get_buildroot_image_info()
|
||||
if image_info:
|
||||
if image_info.get("image_name"):
|
||||
stats["image_name"] = image_info["image_name"]
|
||||
if image_info.get("image_version"):
|
||||
stats["image_version"] = image_info["image_version"]
|
||||
return stats
|
||||
except Exception as e:
|
||||
logger.error(f"Error serving stats: {e}")
|
||||
|
||||
Reference in New Issue
Block a user