diff --git a/.env.example b/.env.example index 73dedd0..dc7f095 100644 --- a/.env.example +++ b/.env.example @@ -3,17 +3,17 @@ # Published image to run. Use a different repository or tag if you are testing # a fork or a specific release. -PYMC_REPEATER_IMAGE=pymcdev/pymc-repeater:main +PYMC_REPEATER_IMAGE=pymcdev/openhop-repeater:main # Storage defaults to Docker named volumes. This is the safest option for # Portainer and fresh installs because Docker preserves the image ownership. # To use host bind mounts instead, create the folders first and make them # writable by UID/GID 15888: -# sudo mkdir -p /opt/pymc-repeater/config /opt/pymc-repeater/data -# sudo chown -R 15888:15888 /opt/pymc-repeater/config /opt/pymc-repeater/data +# sudo mkdir -p /opt/openhop-repeater/config /opt/openhop-repeater/data +# sudo chown -R 15888:15888 /opt/openhop-repeater/config /opt/openhop-repeater/data # Then uncomment and adjust these paths: -# PYMC_CONFIG_VOLUME=/opt/pymc-repeater/config -# PYMC_DATA_VOLUME=/opt/pymc-repeater/data +# PYMC_CONFIG_VOLUME=/opt/openhop-repeater/config +# PYMC_DATA_VOLUME=/opt/openhop-repeater/data # Serial/SPI/GPIO access uses the host's numeric group IDs. Check your host with: # getent group dialout diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index c96b1a3..b96d3d3 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -10,14 +10,14 @@ on: image_repository: description: "Docker image repository to publish to" required: false - default: "pymcdev/pymc-repeater" + default: "pymcdev/openhop-repeater" jobs: docker: if: | github.event_name == 'workflow_dispatch' || - github.repository == 'pyMC-dev/pyMC_Repeater' || - github.repository == 'yellowcooln/pyMC_Repeater' + github.repository == 'pyMC-dev/openhop-repeater' || + github.repository == 'yellowcooln/openhop-repeater' runs-on: ubuntu-latest permissions: contents: read @@ -58,10 +58,10 @@ jobs: run: | if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ -n "${{ inputs.image_repository }}" ]; then image_repository="${{ inputs.image_repository }}" - elif [ "${{ github.repository }}" = "yellowcooln/pyMC_Repeater" ]; then - image_repository="yellowcooln/pymc-repeater" + elif [ "${{ github.repository }}" = "yellowcooln/openhop-repeater" ]; then + image_repository="yellowcooln/openhop-repeater" else - image_repository="pymcdev/pymc-repeater" + image_repository="pymcdev/openhop-repeater" fi echo "Using image repository: ${image_repository}" @@ -92,7 +92,7 @@ jobs: cache-to: type=gha,mode=max - name: Notify Home Assistant add-on repository - if: github.repository == 'pyMC-dev/pyMC_Repeater' + if: github.repository == 'pyMC-dev/openhop-repeater' env: DISPATCH_TOKEN: ${{ secrets.HA_ADDON_REPO_DISPATCH_TOKEN }} CHANNEL: ${{ github.ref_name }} diff --git a/.gitignore b/.gitignore index 14915d6..21aee28 100644 --- a/.gitignore +++ b/.gitignore @@ -28,9 +28,9 @@ share/python-wheels/ DEBIAN/ debian/files debian/.debhelper/ -debian/pymc-repeater/ -debian/pymc-repeater.debhelper.log -debian/pymc-repeater.substvars +debian/openhop-repeater/ +debian/openhop-repeater.debhelper.log +debian/openhop-repeater.substvars # Virtual environments .venv/ diff --git a/README.md b/README.md index 1c69019..03c860c 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# pyMC Repeater +# openHop Repeater -Lightweight Python MeshCore repeater daemon built on `pymc_core`. +Lightweight Python MeshCore repeater daemon built on `openhop_core`. -pyMC Repeater is designed to run continuously on low-power Linux hardware such +openHop Repeater is designed to run continuously on low-power Linux hardware such as Raspberry Pi-class devices, Proxmox LXC containers, and network-attached radio modems. It forwards LoRa packets, exposes a web dashboard, and provides configuration tools for radio setup, policy management, monitoring, and @@ -29,7 +29,7 @@ integrations. ## Overview The repeater daemon runs as a background service and forwards LoRa packets using -the `pymc_core` dispatcher and routing stack. The project favors a simple, +the `openhop_core` dispatcher and routing stack. The project favors a simple, hackable architecture: - CherryPy provides a lightweight HTTP server for the web UI and API. @@ -59,7 +59,7 @@ Historical statistics and performance metrics. ## Supported Hardware -pyMC Repeater supports these radio backends: +openHop Repeater supports these radio backends: - **SX1262 over Linux SPI**: set `radio_type: sx1262` - **SX1262 over CH341 USB-to-SPI**: set `radio_type: sx1262_ch341` @@ -111,8 +111,8 @@ sudo apt install git -y ### Clone The Repository ```bash -git clone https://github.com/pyMC-dev/pyMC_Repeater.git -cd pyMC_Repeater +git clone https://github.com/pyMC-dev/openhop-repeater.git +cd openhop-repeater ``` ### Quick Install @@ -124,17 +124,17 @@ sudo bash ./manage.sh install The installer will: - Create a dedicated `repeater` service user with hardware access -- Install application files to `/opt/pymc_repeater` -- Create the configuration directory at `/etc/pymc_repeater` -- Create the log directory at `/var/log/pymc_repeater` +- Install application files to `/opt/openhop_repeater` +- Create the configuration directory at `/etc/openhop_repeater` +- Create the log directory at `/var/log/openhop_repeater` - Launch the interactive radio and hardware setup wizard -- Install and enable the `pymc-repeater` systemd service +- Install and enable the `openhop-repeater` systemd service After installation: ```bash # View live logs -sudo journalctl -u pymc-repeater -f +sudo journalctl -u openhop-repeater -f ``` Open the web dashboard at: @@ -160,7 +160,7 @@ pip install -e ".[dev]" The main configuration file is created during installation: ```text -/etc/pymc_repeater/config.yaml +/etc/openhop_repeater/config.yaml ``` ### Setup Wizard @@ -207,19 +207,19 @@ TX power defaults to 14 dBm and can be changed later. To reconfigure radio and hardware settings after installation: ```bash -sudo bash setup-radio-config.sh /etc/pymc_repeater +sudo bash setup-radio-config.sh /etc/openhop_repeater ``` You can also launch the management menu: ```bash sudo ./manage.sh -sudo systemctl restart pymc-repeater +sudo systemctl restart openhop-repeater ``` ### Optional pyMC_Glass Integration -pyMC Repeater supports an optional `glass` configuration section for +openHop Repeater supports an optional `glass` configuration section for pyMC_Glass control-plane integration. When enabled, the repeater sends periodic `/inform` payloads to pyMC_Glass, receives queued commands, and reports command results on the next inform cycle. @@ -258,7 +258,7 @@ The web interface can upgrade an installation or switch branches. ### CLI ```bash -cd pyMC_Repeater +cd openhop-repeater sudo bash ./manage.sh upgrade ``` @@ -272,7 +272,7 @@ The upgrade script will: ## Proxmox LXC Installation -pyMC Repeater can run inside a Proxmox LXC container using a CH341 USB-to-SPI +openHop Repeater can run inside a Proxmox LXC container using a CH341 USB-to-SPI adapter or a TCP modem. This is useful for headless, always-on deployments without dedicating a full Raspberry Pi. @@ -295,7 +295,7 @@ Hardware, choose one: Run this command on the Proxmox host, not inside a container: ```bash -bash -c "$(curl -fsSL https://raw.githubusercontent.com/pyMC-dev/pyMC_Repeater/main/scripts/proxmox-install.sh)" +bash -c "$(curl -fsSL https://raw.githubusercontent.com/pyMC-dev/openhop-repeater/main/scripts/proxmox-install.sh)" ``` Replace `main` in the URL with another branch name if needed. @@ -315,7 +315,7 @@ disk, bridge, etc.) and then: | Setting | Default | |---------|---------| | Container ID | Next available | -| Hostname | `pymc-repeater` | +| Hostname | `openhop-repeater` | | RAM | 1024 MB | | Disk | 4 GB | | CPU cores | 2 | @@ -330,10 +330,10 @@ disk, bridge, etc.) and then: pct enter # View service logs -journalctl -u pymc-repeater -f +journalctl -u openhop-repeater -f # Manage the repeater -cd /opt/pymc_repeater +cd /opt/openhop_repeater bash manage.sh ``` @@ -394,7 +394,7 @@ The script prompts before each optional removal step. ## Docker Compose -You can run pyMC Repeater in Docker using the published image. +You can run openHop Repeater in Docker using the published image. Copy `.env.example` to `.env` before starting: @@ -410,7 +410,7 @@ root-owned `./config` and `./data` bind mount folders on first start. If you want host bind mounts, use absolute host paths and pre-create/chown them to `15888:15888`. -Do not mount `./config.yaml:/etc/pymc_repeater/config.yaml`; Docker can create +Do not mount `./config.yaml:/etc/openhop_repeater/config.yaml`; Docker can create that source as a directory, which breaks startup. ### Setup @@ -430,9 +430,9 @@ docker compose up -d ```yaml services: - pymc-repeater: - image: ${PYMC_REPEATER_IMAGE:-pymcdev/pymc-repeater:main} - container_name: pymc-repeater + openhop-repeater: + image: ${PYMC_REPEATER_IMAGE:-pymcdev/openhop-repeater:main} + container_name: openhop-repeater restart: unless-stopped ports: - 8000:8000 @@ -455,12 +455,12 @@ services: - plugdev volumes: - - ${PYMC_CONFIG_VOLUME:-pymc-repeater-config}:/etc/pymc_repeater - - ${PYMC_DATA_VOLUME:-pymc-repeater-data}:/var/lib/pymc_repeater + - ${PYMC_CONFIG_VOLUME:-openhop-repeater-config}:/etc/openhop_repeater + - ${PYMC_DATA_VOLUME:-openhop-repeater-data}:/var/lib/openhop_repeater volumes: - pymc-repeater-config: - pymc-repeater-data: + openhop-repeater-config: + openhop-repeater-data: ``` ## Roadmap @@ -509,7 +509,7 @@ pre-commit run --all-files ``` Hardware support for LoRa radio drivers is included in the base installation -through `pymc_core[hardware]`. +through `openhop_core[hardware]`. Pre-commit hooks will automatically: - Lint and auto-fix Python issues with Ruff @@ -518,7 +518,7 @@ Pre-commit hooks will automatically: ## Support -- [pyMC Core](https://github.com/pyMC-dev/pyMC_core) +- [pyMC Core](https://github.com/pyMC-dev/openhop-core) - [MeshCore Discord](https://meshcore.gg) ## Disclaimer diff --git a/buildroot-manage.sh b/buildroot-manage.sh index 26b4cea..2451b4a 100644 --- a/buildroot-manage.sh +++ b/buildroot-manage.sh @@ -1,28 +1,28 @@ #!/bin/bash -# Buildroot/Luckfox management entrypoint for pyMC Repeater +# Buildroot/Luckfox management entrypoint for openHop Repeater set -euo pipefail SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) -INSTALL_DIR="/opt/pymc_repeater" +INSTALL_DIR="/opt/openhop_repeater" VENV_DIR="$INSTALL_DIR/venv" VENV_PIP="$VENV_DIR/bin/pip" VENV_PYTHON="$VENV_DIR/bin/python" -CONFIG_DIR="/etc/pymc_repeater" -LOG_DIR="/var/log/pymc_repeater" -DATA_DIR="/var/lib/pymc_repeater" +CONFIG_DIR="/etc/openhop_repeater" +LOG_DIR="/var/log/openhop_repeater" +DATA_DIR="/var/lib/openhop_repeater" SERVICE_USER="root" -INIT_SCRIPT="/etc/init.d/S80pymc-repeater" -PIDFILE="/var/run/pymc-repeater.pid" +INIT_SCRIPT="/etc/init.d/S80openhop-repeater" +PIDFILE="/var/run/openhop-repeater.pid" LOGFILE="$LOG_DIR/repeater.log" -SERVICE_NAME="pymc-repeater" +SERVICE_NAME="openhop-repeater" 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_REPO="${PYMC_CORE_REPO:-https://github.com/rightup/openhop-core.git}" PYMC_CORE_REF="${PYMC_CORE_REF:-}" PYMC_CORE_LOCAL_DIR="${PYMC_CORE_LOCAL_DIR:-}" PYMC_SKIP_BUILDROOT_DEP_INSTALL="${PYMC_SKIP_BUILDROOT_DEP_INSTALL:-0}" @@ -486,9 +486,9 @@ cleanup_legacy_install_state() { for path in \ "$INSTALL_DIR/repeater" \ - "$INSTALL_DIR/pymc_core" \ - "$INSTALL_DIR/pyMC_Repeater" \ - "$INSTALL_DIR/pyMC_core" + "$INSTALL_DIR/openhop_core" \ + "$INSTALL_DIR/openhop-repeater" \ + "$INSTALL_DIR/openhop-core" do if [ -e "$path" ]; then rm -rf "$path" @@ -641,8 +641,8 @@ install_core_into_venv() { local core_repo core_ref core_spec if [ -n "$PYMC_CORE_LOCAL_DIR" ]; then - [ -d "$PYMC_CORE_LOCAL_DIR" ] || fail "Missing local pyMC_core checkout: $PYMC_CORE_LOCAL_DIR" - stage "Installing pyMC_core" + [ -d "$PYMC_CORE_LOCAL_DIR" ] || fail "Missing local openhop-core checkout: $PYMC_CORE_LOCAL_DIR" + stage "Installing openhop-core" info "Local dir: ${PYMC_CORE_LOCAL_DIR}" "$VENV_PIP" install --upgrade --no-cache-dir --no-deps --no-build-isolation "$PYMC_CORE_LOCAL_DIR" return 0 @@ -654,8 +654,8 @@ install_core_into_venv() { *) core_repo="${core_repo}.git" ;; esac core_ref=$(resolve_core_ref) - core_spec="pyMC_core[hardware] @ git+${core_repo}@${core_ref}" - stage "Installing pyMC_core" + core_spec="openhop-core[hardware] @ git+${core_repo}@${core_ref}" + stage "Installing openhop-core" info "Repo: ${PYMC_CORE_REPO}" info "Ref: ${core_ref}" "$VENV_PIP" install --upgrade --no-cache-dir --no-deps --no-build-isolation "$core_spec" @@ -667,7 +667,7 @@ import glob import json import os -matches = glob.glob("/opt/pymc_repeater/venv/lib/python*/site-packages/pymc_core-*.dist-info/direct_url.json") +matches = glob.glob("/opt/openhop_repeater/venv/lib/python*/site-packages/openhop_core-*.dist-info/direct_url.json") for path in matches: try: with open(path, "r", encoding="utf-8") as fh: @@ -693,7 +693,7 @@ resolve_core_commit() { } install_repeater_package() { - stage "Installing pyMC Repeater into venv" + stage "Installing openHop Repeater into venv" info "Installing checked-out repo without re-resolving dependencies" "$VENV_PIP" install --upgrade --no-cache-dir --no-deps --no-build-isolation "$SCRIPT_DIR" } @@ -1232,7 +1232,7 @@ start_or_restart_service() { get_version() { if [ -x "$VENV_PYTHON" ]; then - "$VENV_PYTHON" -c "from importlib.metadata import version; print(version('pymc_repeater'))" 2>/dev/null || echo "not installed" + "$VENV_PYTHON" -c "from importlib.metadata import version; print(version('openhop_repeater'))" 2>/dev/null || echo "not installed" else echo "not installed" fi @@ -1355,7 +1355,7 @@ install_repeater() { info "Install dir: $INSTALL_DIR" info "Config dir: $CONFIG_DIR" info "Data dir: $DATA_DIR" - mkdir -p "$INSTALL_DIR" "$CONFIG_DIR" "$LOG_DIR" "$DATA_DIR" "$DATA_DIR/.config/pymc_repeater" + mkdir -p "$INSTALL_DIR" "$CONFIG_DIR" "$LOG_DIR" "$DATA_DIR" "$DATA_DIR/.config/openhop_repeater" chown -R root:root "$CONFIG_DIR" "$LOG_DIR" "$DATA_DIR" chmod 755 "$INSTALL_DIR" "$DATA_DIR" chmod 750 "$CONFIG_DIR" "$LOG_DIR" @@ -1417,7 +1417,7 @@ upgrade_repeater() { git_version=$(prepare_git_version) preinstall_r2_wheels - stage "Upgrading pyMC Repeater" + stage "Upgrading openHop Repeater" if [ "${PYMC_FORCE_DEPS:-0}" = "1" ]; then info "Forcing dependency reinstall" install_buildroot_dependencies @@ -1431,13 +1431,13 @@ upgrade_repeater() { target_core_commit=$(resolve_core_commit) installed_core_commit=$(get_installed_core_commit) if [ -n "$target_core_commit" ] && [ "$installed_core_commit" = "$target_core_commit" ] && [ "${PYMC_FORCE_CORE:-0}" != "1" ]; then - info "pyMC_core is already at ${target_core_commit}; skipping reinstall" + info "openhop-core is already at ${target_core_commit}; skipping reinstall" else install_core_into_venv fi if [ "$current_version" = "$git_version" ] && [ "${PYMC_FORCE_REPEATER:-0}" != "1" ]; then - info "pyMC Repeater is already at ${git_version}; skipping reinstall" + info "openHop Repeater is already at ${git_version}; skipping reinstall" else ensure_yq >/dev/null 2>&1 || true install_repeater_package @@ -1521,7 +1521,7 @@ Usage: bash buildroot-manage.sh Commands: doctor Check Buildroot/Luckfox prerequisites - install Install pyMC Repeater on the Buildroot image + install Install openHop Repeater on the Buildroot image upgrade Upgrade the Buildroot installation from the checked-out repo config Prompt for repeater settings and rewrite config.yaml configure Same as config diff --git a/config.yaml.example b/config.yaml.example index b48964a..016e0e8 100644 --- a/config.yaml.example +++ b/config.yaml.example @@ -151,7 +151,7 @@ gps: # File source settings (used when source: file) # The file may contain raw NMEA lines or JSON with a "sentences" list / # "last_sentence" field. - source_path: "/var/lib/pymc_repeater/gps_nmea.txt" + source_path: "/var/lib/openhop_repeater/gps_nmea.txt" poll_interval_seconds: 2.0 # Modem HTTP source settings (used when source: modem_http) @@ -406,7 +406,7 @@ radio: # Use implicit header mode implicit_header: false -# KISS modem (when radio_type: kiss). Requires pyMC_core with KISS support. +# KISS modem (when radio_type: kiss). Requires openhop-core with KISS support. # kiss: # port: "/dev/ttyUSB0" # baud_rate: 9600 @@ -422,7 +422,7 @@ radio: # # kiss_full_duplex: false # disable carrier-sense/CSMA entirely (not recommended) # pymc_usb firmware modem over Wi-Fi/TCP (when radio_type: pymc_tcp). -# Requires pyMC_core with the TCPLoRaRadio driver +# Requires openhop-core with the TCPLoRaRadio driver # pymc_tcp: # host: "pymc-3e2834.local" # modem hostname / mDNS name / LAN IP # port: 5055 # firmware default @@ -432,7 +432,7 @@ radio: # lbt_max_attempts: 5 # pymc_usb firmware modem over USB-CDC (when radio_type: pymc_usb). -# Requires pyMC_core with the USBLoRaRadio driver +# Requires openhop-core with the USBLoRaRadio driver # pymc_usb: # port: "/dev/ttyACM0" # USB-CDC device; udev rule may symlink to /dev/lora-modem # baudrate: 921600 # must match firmware monitor_speed @@ -492,8 +492,8 @@ duty_cycle: # Storage Configuration storage: # Directory for persistent storage files (SQLite, RRD). - # Use a writable path for local/dev (e.g. "./var/pymc_repeater" or "~/var/pymc_repeater"). - storage_dir: "/var/lib/pymc_repeater" + # Use a writable path for local/dev (e.g. "./var/openhop_repeater" or "~/var/openhop_repeater"). + storage_dir: "/var/lib/openhop_repeater" # Data retention settings retention: @@ -526,7 +526,7 @@ mqtt_brokers: # format: meshcoretomqtt|letsmesh|waev|mqtt # meshcoretomqtt - canonical open-source MC2MQTT topic structure # letsmesh, waev - MC2MQTT family flavors (same topic structure, network-specific identity) - # mqtt - legacy pyMC_Repeater local-broker convention (custom topic, singular 'packet') + # mqtt - legacy openhop-repeater local-broker convention (custom topic, singular 'packet') # retain_status: true|false # Sets MQTT "retain" on status messages so they remain on the broker when disconnected. Also enforces a QOS of 1 (guaranteed delivery) # tls: # enabled: true|false # Enable TLS. If the endpoint's certificate is self-signed, the Root CA should be added to the OS's certificate store. @@ -602,7 +602,7 @@ glass: api_token: "" # Where cert_renewal payloads are written - cert_store_dir: "/etc/pymc_repeater/glass" + cert_store_dir: "/etc/openhop_repeater/glass" logging: # Log level: DEBUG, INFO, WARNING, ERROR diff --git a/convert_firmware_key.sh b/convert_firmware_key.sh index 335c9aa..1c9c97b 100755 --- a/convert_firmware_key.sh +++ b/convert_firmware_key.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Convert MeshCore firmware 64-byte private key to pyMC_Repeater format +# Convert MeshCore firmware 64-byte private key to openhop-repeater format # # Usage: sudo ./convert_firmware_key.sh <64-byte-hex-key> [--output-format=] [config-path] # Example: sudo ./convert_firmware_key.sh 987BDA619630197351F2B3040FD19B2EE0DEE357DD69BBEEE295786FA78A4D5F298B0BF1B7DE73CBC23257CDB2C562F5033DF58C232916432948B0F6BA4448F2 @@ -12,18 +12,18 @@ if [ $# -eq 0 ]; then echo "Usage: sudo $0 <64-byte-hex-key> [--output-format=] [config-path]" echo "" echo "This script imports a 64-byte MeshCore firmware private key into" - echo "pyMC_Repeater for full identity compatibility." + echo "openhop-repeater for full identity compatibility." echo "" echo "The 64-byte key format: [32-byte scalar][32-byte nonce]" echo " - Enables same node address as firmware device" echo " - Supports signing using MeshCore/orlp ed25519 algorithm" - echo " - Fully compatible with pyMC_core LocalIdentity" + echo " - Fully compatible with openhop-core LocalIdentity" echo "" echo "Arguments:" echo " --output-format: Optional output format (yaml|identity, default: yaml)" echo " yaml - Store in config.yaml (embedded binary)" echo " identity - Save to identity.key file (base64 encoded)" - echo " config-path: Optional path to config.yaml (default: /etc/pymc_repeater/config.yaml)" + echo " config-path: Optional path to config.yaml (default: /etc/openhop_repeater/config.yaml)" echo "" echo "Examples:" echo " # Save to config.yaml (default)" @@ -67,7 +67,7 @@ fi # Set default config path if not provided if [ -z "$CONFIG_PATH" ]; then - CONFIG_PATH="/etc/pymc_repeater/config.yaml" + CONFIG_PATH="/etc/openhop_repeater/config.yaml" fi # Validate hex string @@ -92,7 +92,7 @@ if [ "$OUTPUT_FORMAT" = "yaml" ]; then fi else # For identity format, use system-wide location matching config.yaml - IDENTITY_DIR="/etc/pymc_repeater" + IDENTITY_DIR="/etc/openhop_repeater" IDENTITY_PATH="$IDENTITY_DIR/identity.key" fi @@ -261,39 +261,39 @@ fi # Offer to restart service (only relevant for yaml format) if [ "$OUTPUT_FORMAT" = "yaml" ]; then - if systemctl is-active --quiet pymc-repeater 2>/dev/null; then - read -p "Restart pymc-repeater service now? (yes/no): " RESTART + if systemctl is-active --quiet openhop-repeater 2>/dev/null; then + read -p "Restart openhop-repeater service now? (yes/no): " RESTART if [ "$RESTART" = "yes" ]; then - systemctl restart pymc-repeater + systemctl restart openhop-repeater echo "✓ Service restarted" echo "" echo "Check logs for new identity:" - echo " sudo journalctl -u pymc-repeater -f | grep -i 'identity\|hash'" + echo " sudo journalctl -u openhop-repeater -f | grep -i 'identity\|hash'" else echo "Remember to restart the service:" - echo " sudo systemctl restart pymc-repeater" + echo " sudo systemctl restart openhop-repeater" fi else - echo "Note: pymc-repeater service is not running" - echo "Start it with: sudo systemctl start pymc-repeater" + echo "Note: openhop-repeater service is not running" + echo "Start it with: sudo systemctl start openhop-repeater" fi else echo "Identity key saved to file." echo "" - if systemctl is-active --quiet pymc-repeater 2>/dev/null; then - read -p "Restart pymc-repeater service now? (yes/no): " RESTART + if systemctl is-active --quiet openhop-repeater 2>/dev/null; then + read -p "Restart openhop-repeater service now? (yes/no): " RESTART if [ "$RESTART" = "yes" ]; then - systemctl restart pymc-repeater + systemctl restart openhop-repeater echo "✓ Service restarted" echo "" echo "Check logs for new identity:" - echo " sudo journalctl -u pymc-repeater -f | grep -i 'identity\|hash'" + echo " sudo journalctl -u openhop-repeater -f | grep -i 'identity\|hash'" else echo "Remember to restart the service:" - echo " sudo systemctl restart pymc-repeater" + echo " sudo systemctl restart openhop-repeater" fi else - echo "Note: pymc-repeater service is not running" - echo "Start it with: sudo systemctl start pymc-repeater" + echo "Note: openhop-repeater service is not running" + echo "Start it with: sudo systemctl start openhop-repeater" fi fi diff --git a/debian/.gitignore b/debian/.gitignore index e18f4ce..4922945 100644 --- a/debian/.gitignore +++ b/debian/.gitignore @@ -3,4 +3,4 @@ *.substvars .debhelper/ files -pymc-repeater/ +openhop-repeater/ diff --git a/debian/changelog b/debian/changelog index 8fbe9bb..68e73bc 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -pymc-repeater (1.0.5~dev0) unstable; urgency=medium +openhop-repeater (1.0.5~dev0) unstable; urgency=medium * Development build from git commit 7112da9 * Version: 1.0.5.post0 diff --git a/debian/control b/debian/control index ce2a53a..6ca3097 100644 --- a/debian/control +++ b/debian/control @@ -1,4 +1,4 @@ -Source: pymc-repeater +Source: openhop-repeater Section: net Priority: optional Maintainer: Rightup @@ -15,10 +15,10 @@ Build-Depends: debhelper-compat (= 13), python3-psutil, git Standards-Version: 4.6.2 -Homepage: https://github.com/rightup/pyMC_Repeater +Homepage: https://github.com/rightup/openhop-repeater X-Python3-Version: >= 3.9 -Package: pymc-repeater +Package: openhop-repeater Architecture: all Depends: ${python3:Depends}, ${misc:Depends}, @@ -36,8 +36,8 @@ Recommends: python3-periphery, Description: PyMC Repeater Daemon A mesh networking repeater daemon for LoRa devices. . - This package provides the pymc-repeater service for managing + This package provides the openhop-repeater service for managing mesh network repeater functionality with a web interface. . - Note: This package will install pymc_core, cherrypy-cors, and ws4py + Note: This package will install openhop_core, cherrypy-cors, and ws4py from PyPI during postinst as they are not available in Debian repos. diff --git a/debian/debhelper-build-stamp b/debian/debhelper-build-stamp index a8bda05..208f027 100644 --- a/debian/debhelper-build-stamp +++ b/debian/debhelper-build-stamp @@ -1 +1 @@ -pymc-repeater +openhop-repeater diff --git a/debian/pymc-repeater.dirs b/debian/pymc-repeater.dirs deleted file mode 100644 index d79d584..0000000 --- a/debian/pymc-repeater.dirs +++ /dev/null @@ -1,3 +0,0 @@ -etc/pymc_repeater -var/log/pymc_repeater -usr/share/pymc_repeater diff --git a/debian/pymc-repeater.install b/debian/pymc-repeater.install deleted file mode 100644 index 2cbb0af..0000000 --- a/debian/pymc-repeater.install +++ /dev/null @@ -1,3 +0,0 @@ -config.yaml.example usr/share/pymc_repeater/ -radio-presets.json usr/share/pymc_repeater/ -radio-settings.json usr/share/pymc_repeater/ diff --git a/debian/pymc-repeater.postinst b/debian/pymc-repeater.postinst deleted file mode 100755 index d2d57d7..0000000 --- a/debian/pymc-repeater.postinst +++ /dev/null @@ -1,57 +0,0 @@ -#!/bin/sh -set -e - -case "$1" in - configure) - # Create system user - if ! getent passwd pymc-repeater >/dev/null; then - adduser --system --group --home /var/lib/pymc-repeater \ - --gecos "PyMC Repeater Service" pymc-repeater - fi - - # Add user to gpio and spi groups for hardware access - if getent group gpio >/dev/null; then - usermod -a -G gpio pymc-repeater - fi - if getent group spi >/dev/null; then - usermod -a -G spi pymc-repeater - fi - # Create and set permissions on data directory - mkdir -p /var/lib/pymc_repeater - chown -R pymc-repeater:pymc-repeater /var/lib/pymc_repeater - chmod 750 /var/lib/pymc_repeater - # Set permissions - chown -R pymc-repeater:pymc-repeater /etc/pymc_repeater - chown -R pymc-repeater:pymc-repeater /var/log/pymc-repeater - chmod 750 /etc/pymc_repeater - chmod 750 /var/log/pymc-repeater - - # Copy example config if no config exists - if [ ! -f /etc/pymc_repeater/config.yaml ]; then - cp /usr/share/pymc_repeater/config.yaml.example /etc/pymc_repeater/config.yaml - chown pymc-repeater:pymc-repeater /etc/pymc_repeater/config.yaml - chmod 640 /etc/pymc_repeater/config.yaml - fi - - # Install pymc_core from PyPI if not already installed - if ! python3 -c "import pymc_core" 2>/dev/null; then - echo "Installing pymc_core[hardware] from PyPI..." - python3 -m pip install --break-system-packages 'pymc_core[hardware]>=1.0.7' || true - fi - - # Install packages not available in Debian repos - if ! python3 -c "import cherrypy_cors" 2>/dev/null; then - echo "Installing cherrypy-cors from PyPI..." - python3 -m pip install --break-system-packages 'cherrypy-cors==1.7.0' || true - fi - - if ! python3 -c "import ws4py" 2>/dev/null; then - echo "Installing ws4py from PyPI..." - python3 -m pip install --break-system-packages 'ws4py>=0.5.1' || true - fi - ;; -esac - -#DEBHELPER# - -exit 0 diff --git a/debian/pymc-repeater.postrm b/debian/pymc-repeater.postrm deleted file mode 100755 index 5762452..0000000 --- a/debian/pymc-repeater.postrm +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/sh -set -e - -case "$1" in - purge) - # Remove user and directories - if getent passwd pymc-repeater >/dev/null; then - deluser --system pymc-repeater || true - fi - rm -rf /etc/pymc-repeater - rm -rf /var/log/pymc-repeater - rm -rf /var/lib/pymc-repeater - ;; -esac - -#DEBHELPER# - -exit 0 diff --git a/debian/pymc-repeater.service b/debian/pymc-repeater.service deleted file mode 100644 index 5ed7252..0000000 --- a/debian/pymc-repeater.service +++ /dev/null @@ -1,19 +0,0 @@ -[Unit] -Description=PyMC Repeater Daemon -After=network.target - -[Service] -Type=simple -User=pymc-repeater -Group=pymc-repeater -WorkingDirectory=/etc/pymc-repeater -ExecStart=/usr/bin/pymc-repeater -Restart=always -RestartSec=10 - -# Allow GPS time sync to update CLOCK_REALTIME without running as root -CapabilityBoundingSet=CAP_SYS_TIME -AmbientCapabilities=CAP_SYS_TIME - -[Install] -WantedBy=multi-user.target diff --git a/debian/rules b/debian/rules index 90bb323..0f05c64 100755 --- a/debian/rules +++ b/debian/rules @@ -1,7 +1,7 @@ #!/usr/bin/make -f # -*- makefile -*- -export PYBUILD_NAME=pymc-repeater +export PYBUILD_NAME=openhop-repeater export DH_VERBOSE=1 %: @@ -19,4 +19,4 @@ override_dh_auto_test: rm -f repeater/_version.py override_dh_installsystemd: - dh_installsystemd --name=pymc-repeater + dh_installsystemd --name=openhop-repeater diff --git a/docker-compose.build.yml b/docker-compose.build.yml index bfcc204..f8b1b49 100644 --- a/docker-compose.build.yml +++ b/docker-compose.build.yml @@ -1,6 +1,6 @@ services: - pymc-repeater: - image: pymc-repeater:local + openhop-repeater: + image: openhop-repeater:local build: context: . dockerfile: dockerfile diff --git a/docker-compose.yml b/docker-compose.yml index 31301f6..504cbf7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ services: - pymc-repeater: - image: ${PYMC_REPEATER_IMAGE:-pymcdev/pymc-repeater:main} - container_name: pymc-repeater + openhop-repeater: + image: ${PYMC_REPEATER_IMAGE:-pymcdev/openhop-repeater:main} + container_name: openhop-repeater restart: unless-stopped ports: - 8000:8000 @@ -21,9 +21,9 @@ services: - "${SPI_GID:-989}" - plugdev volumes: - - ${PYMC_CONFIG_VOLUME:-pymc-repeater-config}:/etc/pymc_repeater - - ${PYMC_DATA_VOLUME:-pymc-repeater-data}:/var/lib/pymc_repeater + - ${PYMC_CONFIG_VOLUME:-openhop-repeater-config}:/etc/openhop_repeater + - ${PYMC_DATA_VOLUME:-openhop-repeater-data}:/var/lib/openhop_repeater volumes: - pymc-repeater-config: - pymc-repeater-data: + openhop-repeater-config: + openhop-repeater-data: diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index eb17104..504447a 100644 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -1,8 +1,8 @@ #!/bin/sh set -eu -INSTALL_DIR="${INSTALL_DIR:-/opt/pymc_repeater}" -CONFIG_DIR="${CONFIG_DIR:-/etc/pymc_repeater}" +INSTALL_DIR="${INSTALL_DIR:-/opt/openhop_repeater}" +CONFIG_DIR="${CONFIG_DIR:-/etc/openhop_repeater}" CONFIG_PATH="${PYMC_REPEATER_CONFIG:-${CONFIG_DIR}/config.yaml}" EXAMPLE_PATH="${CONFIG_DIR}/config.yaml.example" BUNDLED_EXAMPLE_PATH="${INSTALL_DIR}/config.yaml.example" @@ -22,7 +22,7 @@ fail_bad_config_mount() { echo "Invalid Docker config mount: ${CONFIG_PATH} is a directory, but it must be the config file." >&2 echo "This usually happens when ./config.yaml is bind-mounted before that host file exists." >&2 echo "Use the supported folder mount instead:" >&2 - echo " - ./config:/etc/pymc_repeater" >&2 + echo " - ./config:/etc/openhop_repeater" >&2 echo "Then place the config at ./config/config.yaml." >&2 print_permission_help exit 1 @@ -40,7 +40,7 @@ copy_or_die() { use_runtime_merged_config() { src="$1" - runtime_dir="$(mktemp -d /tmp/pymc-repeater-config.XXXXXX)" + runtime_dir="$(mktemp -d /tmp/openhop-repeater-config.XXXXXX)" runtime_config="${runtime_dir}/config.yaml" if ! cp "${src}" "${runtime_config}"; then diff --git a/dockerfile b/dockerfile index 17e06c3..0b201cd 100644 --- a/dockerfile +++ b/dockerfile @@ -11,9 +11,9 @@ ARG SPI_GID=989 ARG TARGETARCH ARG YQ_VERSION=v4.40.5 -ENV INSTALL_DIR=/opt/pymc_repeater \ - CONFIG_DIR=/etc/pymc_repeater \ - DATA_DIR=/var/lib/pymc_repeater \ +ENV INSTALL_DIR=/opt/openhop_repeater \ + CONFIG_DIR=/etc/openhop_repeater \ + DATA_DIR=/var/lib/openhop_repeater \ HOME_DIR=/home/${USER} \ PATH=/home/${USER}/.local/bin:${PATH} \ PYTHONUNBUFFERED=1 \ diff --git a/docs/adding_sensors.md b/docs/adding_sensors.md index 28e672e..0afcd05 100644 --- a/docs/adding_sensors.md +++ b/docs/adding_sensors.md @@ -1,6 +1,6 @@ # Adding a New Sensor Plug-in -Sensors in pyMC_Repeater are self-contained modules that live in `repeater/sensors/`. The subsystem is plug-in based: adding a new sensor requires only one new file. The manager discovers and loads it automatically at runtime by importing the module named after the sensor type. +Sensors in openhop-repeater are self-contained modules that live in `repeater/sensors/`. The subsystem is plug-in based: adding a new sensor requires only one new file. The manager discovers and loads it automatically at runtime by importing the module named after the sensor type. --- diff --git a/env.example b/env.example index af3af2c..f63e0f4 100644 --- a/env.example +++ b/env.example @@ -3,17 +3,17 @@ # Published image to run. Use a different repository or tag if you are testing # a fork or a specific release. -PYMC_REPEATER_IMAGE=pymcdev/pymc-repeater:main +PYMC_REPEATER_IMAGE=pymcdev/openhop-repeater:main # Storage defaults to Docker named volumes. This is the safest option for # Portainer and fresh installs because Docker preserves the image ownership. # To use host bind mounts instead, create the folders first and make them # writable by UID/GID 15888: -# sudo mkdir -p /opt/pymc-repeater/config /opt/pymc-repeater/data -# sudo chown -R 15888:15888 /opt/pymc-repeater/config /opt/pymc-repeater/data +# sudo mkdir -p /opt/openhop-repeater/config /opt/openhop-repeater/data +# sudo chown -R 15888:15888 /opt/openhop-repeater/config /opt/openhop-repeater/data # Then uncomment and adjust these paths: -# PYMC_CONFIG_VOLUME=/opt/pymc-repeater/config -# PYMC_DATA_VOLUME=/opt/pymc-repeater/data +# PYMC_CONFIG_VOLUME=/opt/openhop-repeater/config +# PYMC_DATA_VOLUME=/opt/openhop-repeater/data # SPI/GPIO access uses the host's numeric group IDs. Check your host with: # getent group gpio diff --git a/manage.sh b/manage.sh index 71afcf3..e7ecbb0 100755 --- a/manage.sh +++ b/manage.sh @@ -1,18 +1,24 @@ #!/bin/bash -# pyMC Repeater Management Script - Deploy, Upgrade, Uninstall +# openHop Repeater Management Script - Deploy, Upgrade, Uninstall set -e -INSTALL_DIR="/opt/pymc_repeater" +INSTALL_DIR="/opt/openhop_repeater" VENV_DIR="$INSTALL_DIR/venv" VENV_PIP="$VENV_DIR/bin/pip" VENV_PYTHON="$VENV_DIR/bin/python" -CONFIG_DIR="/etc/pymc_repeater" -LOG_DIR="/var/log/pymc_repeater" +CONFIG_DIR="/etc/openhop_repeater" +LOG_DIR="/var/log/openhop_repeater" +DATA_DIR="/var/lib/openhop_repeater" SERVICE_USER="repeater" -SERVICE_NAME="pymc-repeater" +SERVICE_NAME="openhop-repeater" SILENT_MODE="${PYMC_SILENT:-${SILENT:-}}" +LEGACY_INSTALL_DIR="/opt/openhop-repeater" +LEGACY_CONFIG_DIR="/etc/openhop-repeater" +LEGACY_LOG_DIR="/var/log/openhop-repeater" +LEGACY_DATA_DIR="/var/lib/openhop-repeater" + # R2 Wheels Configuration improves install speed on ARM devices R2_BASE_URL="https://wheel.pymc.dev/pymc_build_deps" R2_ENABLED=1 # Set to 0 to disable R2 wheels and always build from source @@ -21,7 +27,68 @@ R2_ENABLED=1 # Set to 0 to disable R2 wheels and always build from source # Virtual-environment helpers # --------------------------------------------------------------------------- -# Create (or re-create) the dedicated venv for pymc_repeater +cleanup_stale_source_trees() { + local removed=0 + local path + + for path in \ + "$INSTALL_DIR/repeater" \ + "$INSTALL_DIR/openhop_core" \ + "$INSTALL_DIR/openhop-repeater" \ + "$INSTALL_DIR/openhop-core" \ + "$LEGACY_INSTALL_DIR/repeater" \ + "$LEGACY_INSTALL_DIR/openhop_core" \ + "$LEGACY_INSTALL_DIR/openhop-repeater" \ + "$LEGACY_INSTALL_DIR/openhop-core" + do + if [ -e "$path" ]; then + rm -rf "$path" + removed=1 + echo " ✓ Removed stale source tree at $path" + fi + done + + if [ "$removed" -eq 0 ]; then + echo " ✓ No stale source-tree paths found" + fi +} + +migrate_legacy_paths() { + local timestamp legacy current label backup_path + timestamp="$(date +%Y%m%d_%H%M%S)" + + migrate_one_path() { + legacy="$1" + current="$2" + label="$3" + + if [ ! -e "$legacy" ]; then + return 0 + fi + + mkdir -p "$current" 2>/dev/null || true + + if [ ! -e "$current" ] || [ -z "$(ls -A "$current" 2>/dev/null)" ]; then + rm -rf "$current" 2>/dev/null || true + mv "$legacy" "$current" + echo " ✓ Migrated legacy $label path: $legacy -> $current" + return 0 + fi + + cp -an "$legacy"/. "$current"/ 2>/dev/null || true + backup_path="${legacy}.migrated.${timestamp}" + mv "$legacy" "$backup_path" + echo " ✓ Merged legacy $label data into $current" + echo " ✓ Archived legacy $label path at $backup_path" + } + + migrate_one_path "$LEGACY_CONFIG_DIR" "$CONFIG_DIR" "config" + migrate_one_path "$LEGACY_LOG_DIR" "$LOG_DIR" "log" + migrate_one_path "$LEGACY_DATA_DIR" "$DATA_DIR" "data" + migrate_one_path "$LEGACY_INSTALL_DIR" "$INSTALL_DIR" "install" +} + +# Create (or re-create) the dedicated venv for openhop_repeater ensure_venv() { if [ ! -x "$VENV_PYTHON" ]; then echo ">>> Creating virtual environment at $VENV_DIR ..." @@ -40,15 +107,16 @@ migrate_to_venv() { ensure_venv # 2. Remove legacy PYTHONPATH from the service unit - local svc_unit="/etc/systemd/system/pymc-repeater.service" + local svc_unit="/etc/systemd/system/openhop-repeater.service" if [ -f "$svc_unit" ]; then if grep -q 'PYTHONPATH' "$svc_unit" 2>/dev/null; then sed -i '/^Environment=.*PYTHONPATH/d' "$svc_unit" echo " ✓ Removed legacy PYTHONPATH from service unit" fi # 3. Fix WorkingDirectory if still pointing at old source - if grep -q 'WorkingDirectory=/opt/pymc_repeater' "$svc_unit" 2>/dev/null; then - sed -i 's|WorkingDirectory=/opt/pymc_repeater|WorkingDirectory=/var/lib/pymc_repeater|' "$svc_unit" + if grep -q 'WorkingDirectory=/opt/openhop_repeater\|WorkingDirectory=/opt/openhop-repeater' "$svc_unit" 2>/dev/null; then + sed -i 's|WorkingDirectory=/opt/openhop_repeater|WorkingDirectory=/var/lib/openhop_repeater|' "$svc_unit" + sed -i 's|WorkingDirectory=/opt/openhop-repeater|WorkingDirectory=/var/lib/openhop_repeater|' "$svc_unit" echo " ✓ Fixed WorkingDirectory in service unit" fi # 4. Ensure ExecStart uses the venv python @@ -60,15 +128,12 @@ migrate_to_venv() { fi # 5. Remove the package from system python (best-effort) - python3 -m pip uninstall -y pymc_repeater 2>/dev/null || true - python3 -m pip uninstall -y pymc_core 2>/dev/null || true + python3 -m pip uninstall -y openhop_repeater 2>/dev/null || true + python3 -m pip uninstall -y openhop_core 2>/dev/null || true echo " ✓ Cleaned up system-level packages (if any)" # 6. Remove stale source trees that could shadow the venv package - if [ -d "$INSTALL_DIR/repeater" ]; then - rm -rf "$INSTALL_DIR/repeater" - echo " ✓ Removed stale source tree from $INSTALL_DIR/repeater" - fi + cleanup_stale_source_trees } is_silent_flag() { @@ -117,22 +182,22 @@ fi # Function to show info box show_info() { - $DIALOG --backtitle "pyMC Repeater Management" --title "$1" --msgbox "$2" 12 70 + $DIALOG --backtitle "openHop Repeater Management" --title "$1" --msgbox "$2" 12 70 } # Function to show error box show_error() { - $DIALOG --backtitle "pyMC Repeater Management" --title "Error" --msgbox "$1" 8 60 + $DIALOG --backtitle "openHop Repeater Management" --title "Error" --msgbox "$1" 8 60 } # Function to ask yes/no question ask_yes_no() { - $DIALOG --backtitle "pyMC Repeater Management" --title "$1" --yesno "$2" 10 70 + $DIALOG --backtitle "openHop Repeater Management" --title "$1" --yesno "$2" 10 70 } # Function to show progress show_progress() { - echo "$2" | $DIALOG --backtitle "pyMC Repeater Management" --title "$1" --gauge "$3" 8 70 0 + echo "$2" | $DIALOG --backtitle "openHop Repeater Management" --title "$1" --gauge "$3" 8 70 0 } # Function to check if service exists @@ -159,11 +224,11 @@ is_enabled() { get_version() { # Read version from the pip-installed package in the venv if [ -x "$VENV_PYTHON" ]; then - "$VENV_PYTHON" -c "from importlib.metadata import version; print(version('pymc_repeater'))" 2>/dev/null \ + "$VENV_PYTHON" -c "from importlib.metadata import version; print(version('openhop_repeater'))" 2>/dev/null \ || echo "not installed" else # Fallback: try system python for pre-migration installs - python3 -c "from importlib.metadata import version; print(version('pymc_repeater'))" 2>/dev/null \ + python3 -c "from importlib.metadata import version; print(version('openhop_repeater'))" 2>/dev/null \ || echo "not installed" fi } @@ -183,11 +248,11 @@ get_status_display() { show_main_menu() { local status=$(get_status_display) - CHOICE=$($DIALOG --backtitle "pyMC Repeater Management" --title "pyMC Repeater Management" --menu "\nCurrent Status: $status\n\nChoose an action:" 18 70 9 \ - "install" "Install pyMC Repeater" \ + CHOICE=$($DIALOG --backtitle "openHop Repeater Management" --title "openHop Repeater Management" --menu "\nCurrent Status: $status\n\nChoose an action:" 18 70 9 \ + "install" "Install openHop Repeater" \ "upgrade" "Upgrade existing installation" \ "reset" "reset existing installation to defaults" \ - "uninstall" "Remove pyMC Repeater completely" \ + "uninstall" "Remove openHop Repeater completely" \ "config" "Configure radio settings" \ "start" "Start the service" \ "stop" "Stop the service" \ @@ -199,7 +264,7 @@ show_main_menu() { case $CHOICE in "install") if is_installed; then - show_error "pyMC Repeater is already installed!\n\nUse 'upgrade' to update or 'uninstall' first." + show_error "openHop Repeater is already installed!\n\nUse 'upgrade' to update or 'uninstall' first." else install_repeater fi @@ -208,21 +273,21 @@ show_main_menu() { if is_installed; then upgrade_repeater "false" else - show_error "pyMC Repeater is not installed!\n\nUse 'install' first." + show_error "openHop Repeater is not installed!\n\nUse 'install' first." fi ;; "reset") if is_installed; then reset_repeater else - show_error "pyMC Repeater is not installed!\n\nUse 'install' first." + show_error "openHop Repeater is not installed!\n\nUse 'install' first." fi ;; "uninstall") if is_installed; then uninstall_repeater else - show_error "pyMC Repeater is not installed." + show_error "openHop Repeater is not installed." fi ;; "config") @@ -240,7 +305,7 @@ show_main_menu() { "logs") clear echo -e "\033[1;36m╔══════════════════════════════════════════════════════════════════════╗\033[0m" - echo -e "\033[1;36m║\033[0m \033[1;37mpyMC Repeater - Live Logs\033[0m \033[1;36m║\033[0m" + echo -e "\033[1;36m║\033[0m \033[1;37mopenHop Repeater - Live Logs\033[0m \033[1;36m║\033[0m" echo -e "\033[1;36m║\033[0m \033[0;90m(Press Ctrl+C to return)\033[0m \033[1;36m║\033[0m" echo -e "\033[1;36m╚══════════════════════════════════════════════════════════════════════╝\033[0m" echo "" @@ -265,7 +330,7 @@ install_repeater() { # Welcome screen (Bypass if the script was passd with the "install" option, assume we want a silent install) if [[ "${1:-}" != "install" ]]; then - $DIALOG --backtitle "pyMC Repeater Management" --title "Welcome" --msgbox "\nWelcome to pyMC Repeater Setup\n\nThis installer will configure your Linux system as a LoRa mesh network repeater.\n\nPress OK to continue..." 12 70 + $DIALOG --backtitle "openHop Repeater Management" --title "Welcome" --msgbox "\nWelcome to openHop Repeater Setup\n\nThis installer will configure your Linux system as a LoRa mesh network repeater.\n\nPress OK to continue..." 12 70 fi # SPI Check - Universal approach that works on all boards (skip for CH341 USB-SPI adapter) @@ -321,13 +386,13 @@ install_repeater() { # Installation progress echo "" echo "═══════════════════════════════════════════════════════════════" - echo " Installing pyMC Repeater" + echo " Installing openHop Repeater" echo "═══════════════════════════════════════════════════════════════" echo "" echo ">>> Creating service user..." if ! id "$SERVICE_USER" &>/dev/null; then - useradd --system --home /var/lib/pymc_repeater --shell /sbin/nologin "$SERVICE_USER" + useradd --system --home "$DATA_DIR" --shell /sbin/nologin "$SERVICE_USER" fi ( @@ -336,8 +401,12 @@ install_repeater() { getent group "$grp" >/dev/null 2>&1 && usermod -a -G "$grp" "$SERVICE_USER" 2>/dev/null || true done - echo "20"; echo "# Creating directories..." - mkdir -p "$INSTALL_DIR" "$CONFIG_DIR" "$LOG_DIR" /var/lib/pymc_repeater + echo "20"; echo "# Migrating legacy paths..." + migrate_legacy_paths + cleanup_stale_source_trees + + echo "23"; echo "# Creating directories..." + mkdir -p "$INSTALL_DIR" "$CONFIG_DIR" "$LOG_DIR" "$DATA_DIR" echo "25"; echo "# Installing system dependencies..." apt-get update -qq @@ -367,9 +436,9 @@ install_repeater() { echo "29"; echo "# Installing files..." cp "$SCRIPT_DIR/manage.sh" "$INSTALL_DIR/" 2>/dev/null || true - cp "$SCRIPT_DIR/pymc-repeater.service" "$INSTALL_DIR/" 2>/dev/null || true - cp "$SCRIPT_DIR/radio-settings.json" /var/lib/pymc_repeater/ 2>/dev/null || true - cp "$SCRIPT_DIR/radio-presets.json" /var/lib/pymc_repeater/ 2>/dev/null || true + cp "$SCRIPT_DIR/openhop-repeater.service" "$INSTALL_DIR/" 2>/dev/null || true + cp "$SCRIPT_DIR/radio-settings.json" "$DATA_DIR/" 2>/dev/null || true + cp "$SCRIPT_DIR/radio-presets.json" "$DATA_DIR/" 2>/dev/null || true echo "45"; echo "# Installing configuration..." cp "$SCRIPT_DIR/config.yaml.example" "$CONFIG_DIR/config.yaml.example" @@ -378,28 +447,28 @@ install_repeater() { fi echo "55"; echo "# Installing systemd service..." - cp "$SCRIPT_DIR/pymc-repeater.service" /etc/systemd/system/ + cp "$SCRIPT_DIR/openhop-repeater.service" /etc/systemd/system/ systemctl daemon-reload echo "58"; echo "# Installing udev rules for CH341..." - if [ -f "$SCRIPT_DIR/../pyMC_core/99-ch341.rules" ]; then - cp "$SCRIPT_DIR/../pyMC_core/99-ch341.rules" /etc/udev/rules.d/99-ch341.rules + if [ -f "$SCRIPT_DIR/../openhop-core/99-ch341.rules" ]; then + cp "$SCRIPT_DIR/../openhop-core/99-ch341.rules" /etc/udev/rules.d/99-ch341.rules udevadm control --reload-rules 2>/dev/null || true udevadm trigger 2>/dev/null || true fi echo "65"; echo "# Setting permissions..." # Venv stays root-owned (pip runs as root); service user only needs read+execute - chown -R "$SERVICE_USER:$SERVICE_USER" "$CONFIG_DIR" "$LOG_DIR" /var/lib/pymc_repeater - chmod 750 "$CONFIG_DIR" "$LOG_DIR" /var/lib/pymc_repeater + chown -R "$SERVICE_USER:$SERVICE_USER" "$CONFIG_DIR" "$LOG_DIR" "$DATA_DIR" + chmod 750 "$CONFIG_DIR" "$LOG_DIR" "$DATA_DIR" # Ensure manage.sh and support files in INSTALL_DIR are accessible chown root:root "$INSTALL_DIR" chmod 755 "$INSTALL_DIR" # Ensure the service user can create subdirectories in their home directory - chmod 755 /var/lib/pymc_repeater + chmod 755 "$DATA_DIR" # Pre-create the .config directory that the service will need - mkdir -p /var/lib/pymc_repeater/.config/pymc_repeater - chown -R "$SERVICE_USER:$SERVICE_USER" /var/lib/pymc_repeater/.config + mkdir -p "$DATA_DIR/.config/openhop_repeater" + chown -R "$SERVICE_USER:$SERVICE_USER" "$DATA_DIR/.config" # Configure polkit for passwordless service restart @@ -410,38 +479,38 @@ install_repeater() { echo "Polkit 0.106 or greater detected, using rules file" echo ">>> Configuring polkit for service management..." mkdir -p /etc/polkit-1/rules.d - cat > /etc/polkit-1/rules.d/10-pymc-repeater.rules <<'EOF' + cat > /etc/polkit-1/rules.d/10-openhop-repeater.rules <<'EOF' polkit.addRule(function(action, subject) { if (action.id == "org.freedesktop.systemd1.manage-units" && - action.lookup("unit") == "pymc-repeater.service" && + action.lookup("unit") == "openhop-repeater.service" && subject.user == "repeater") { return polkit.Result.YES; } }); EOF - chmod 0644 /etc/polkit-1/rules.d/10-pymc-repeater.rules + chmod 0644 /etc/polkit-1/rules.d/10-openhop-repeater.rules else echo "Polkit 0.105 or less detected, using pkla file" mkdir -p /etc/polkit-1/localauthority/50-local.d - cat > /etc/polkit-1/localauthority/50-local.d/10-pymc-repeater.pkla <<'EOF' -[Allow repeater to restart pymc-repeater service] + cat > /etc/polkit-1/localauthority/50-local.d/10-openhop-repeater.pkla <<'EOF' +[Allow repeater to restart openhop-repeater service] Identity=unix-user:repeater Action=org.freedesktop.systemd1.manage-units ResultAny=yes ResultInactive=yes ResultActive=yes EOF - chmod 0644 /etc/polkit-1/localauthority/50-local.d/10-pymc-repeater.pkla + chmod 0644 /etc/polkit-1/localauthority/50-local.d/10-openhop-repeater.pkla fi # Also configure sudoers as fallback for service restart echo ">>> Configuring sudoers for service management..." mkdir -p /etc/sudoers.d - cat > /etc/sudoers.d/pymc-repeater <<'EOF' -# Allow repeater user to manage the pymc-repeater service without password -repeater ALL=(root) NOPASSWD: /usr/bin/systemctl restart pymc-repeater, /usr/bin/systemctl stop pymc-repeater, /usr/bin/systemctl start pymc-repeater, /usr/bin/systemctl status pymc-repeater, /usr/local/bin/pymc-do-upgrade + cat > /etc/sudoers.d/openhop-repeater <<'EOF' +# Allow repeater user to manage the openhop-repeater service without password +repeater ALL=(root) NOPASSWD: /usr/bin/systemctl restart openhop-repeater, /usr/bin/systemctl stop openhop-repeater, /usr/bin/systemctl start openhop-repeater, /usr/bin/systemctl status openhop-repeater, /usr/local/bin/pymc-do-upgrade EOF - chmod 0440 /etc/sudoers.d/pymc-repeater + chmod 0440 /etc/sudoers.d/openhop-repeater echo ">>> Installing OTA upgrade wrapper..." cat > /usr/local/bin/pymc-do-upgrade <<'UPGRADEEOF' @@ -451,7 +520,7 @@ EOF set -e CHANNEL="${1:-main}" PRETEND_VERSION="${2:-}" -VENV_DIR="/opt/pymc_repeater/venv" +VENV_DIR="/opt/openhop_repeater/venv" VENV_PIP="$VENV_DIR/bin/pip" VENV_PYTHON="$VENV_DIR/bin/python" # Validate: only allow safe git ref characters @@ -468,14 +537,22 @@ if [ ! -x "$VENV_PYTHON" ]; then python3 -m venv --system-site-packages "$VENV_DIR" "$VENV_PIP" install --upgrade pip setuptools wheel >/dev/null 2>&1 || true fi +# ---- Legacy path migration: openhop-repeater -> openhop_repeater ---- +if [ -d /opt/openhop-repeater ] && [ ! -e /opt/openhop_repeater ]; then + mv /opt/openhop-repeater /opt/openhop_repeater +fi # ---- Migration: clean up legacy service unit issues ---- -SVC_UNIT=/etc/systemd/system/pymc-repeater.service +SVC_UNIT=/etc/systemd/system/openhop-repeater.service if grep -q 'PYTHONPATH' "$SVC_UNIT" 2>/dev/null; then sed -i '/^Environment=.*PYTHONPATH/d' "$SVC_UNIT" systemctl daemon-reload fi -if grep -q 'WorkingDirectory=/opt/pymc_repeater' "$SVC_UNIT" 2>/dev/null; then - sed -i 's|WorkingDirectory=/opt/pymc_repeater|WorkingDirectory=/var/lib/pymc_repeater|' "$SVC_UNIT" +if grep -q 'WorkingDirectory=/opt/openhop_repeater' "$SVC_UNIT" 2>/dev/null; then + sed -i 's|WorkingDirectory=/opt/openhop_repeater|WorkingDirectory=/var/lib/openhop_repeater|' "$SVC_UNIT" + systemctl daemon-reload +fi +if grep -q 'WorkingDirectory=/opt/openhop-repeater' "$SVC_UNIT" 2>/dev/null; then + sed -i 's|WorkingDirectory=/opt/openhop-repeater|WorkingDirectory=/var/lib/openhop_repeater|' "$SVC_UNIT" systemctl daemon-reload fi if grep -q 'ExecStart=/usr/bin/python3' "$SVC_UNIT" 2>/dev/null; then @@ -483,10 +560,13 @@ if grep -q 'ExecStart=/usr/bin/python3' "$SVC_UNIT" 2>/dev/null; then systemctl daemon-reload fi # ---- Remove stale source trees that shadow the venv package ---- -[ -d /opt/pymc_repeater/repeater ] && rm -rf /opt/pymc_repeater/repeater +[ -d /opt/openhop_repeater/repeater ] && rm -rf /opt/openhop_repeater/repeater +[ -d /opt/openhop-repeater/repeater ] && rm -rf /opt/openhop-repeater/repeater +[ -d /opt/openhop_repeater/openhop-repeater ] && rm -rf /opt/openhop_repeater/openhop-repeater +[ -d /opt/openhop-repeater/openhop-repeater ] && rm -rf /opt/openhop-repeater/openhop-repeater # ---- Remove old system-level packages to avoid confusion ---- -python3 -m pip uninstall -y pymc_repeater 2>/dev/null || true -python3 -m pip uninstall -y pymc_core 2>/dev/null || true +python3 -m pip uninstall -y openhop_repeater 2>/dev/null || true +python3 -m pip uninstall -y openhop_core 2>/dev/null || true # ---- Try R2 wheels first for faster OTA upgrades ---- R2_BASE_URL="https://wheel.pymc.dev/pymc_build_deps" MACHINE_ARCH=$(uname -m) @@ -502,14 +582,14 @@ if [ -n "$ARCH_TAG" ]; then echo "[pymc-do-upgrade] Trying dependencies from R2 wheels..." "$VENV_PIP" install --find-links "${WHEEL_BASE}/index.html" --no-cache-dir "pycryptodome>=3.23.0" "PyNaCl>=1.5.0" cffi "pyyaml>=6.0.0" 2>/dev/null || true fi -# ---- Install pymc_repeater from git ---- +# ---- Install openhop_repeater from git ---- if "$VENV_PIP" install \ --upgrade \ --no-cache-dir \ - "pymc_repeater[hardware] @ git+https://github.com/rightup/pyMC_Repeater.git@${CHANNEL}"; then + "openhop_repeater[hardware] @ git+https://github.com/rightup/openhop-repeater.git@${CHANNEL}"; then # Keep web/OTA updates aligned with manage.sh install/upgrade defaults. - RADIO_BASE_URL="https://raw.githubusercontent.com/rightup/pyMC_Repeater/${CHANNEL}" - RADIO_STORAGE_DIR="/var/lib/pymc_repeater" + RADIO_BASE_URL="https://raw.githubusercontent.com/rightup/openhop-repeater/${CHANNEL}" + RADIO_STORAGE_DIR="/var/lib/openhop_repeater" mkdir -p "$RADIO_STORAGE_DIR" wget -qO "$RADIO_STORAGE_DIR/radio-settings.json" "${RADIO_BASE_URL}/radio-settings.json" 2>/dev/null || true wget -qO "$RADIO_STORAGE_DIR/radio-presets.json" "${RADIO_BASE_URL}/radio-presets.json" 2>/dev/null || true @@ -523,13 +603,13 @@ UPGRADEEOF systemctl enable "$SERVICE_NAME" echo "90"; echo "# Installation files complete..." - ) | $DIALOG --backtitle "pyMC Repeater Management" --title "Installing" --gauge "Setting up pyMC Repeater..." 8 70 0 + ) | $DIALOG --backtitle "openHop Repeater Management" --title "Installing" --gauge "Setting up openHop Repeater..." 8 70 0 # Install Python package outside of progress gauge for better error handling clear echo "=== Installing Python Dependencies ===" echo "" - echo "Installing pymc_repeater and dependencies (including pymc_core from PyPI)..." + echo "Installing openhop_repeater and dependencies (including openhop_core from PyPI)..." echo "This may take a few minutes..." echo "" @@ -556,7 +636,7 @@ UPGRADEEOF # Ensure venv exists ensure_venv - echo "Installing pymc_repeater into venv ($VENV_DIR)..." + echo "Installing openhop_repeater into venv ($VENV_DIR)..." # Attempt R2 wheels first for faster installation if [ "$R2_ENABLED" -eq 1 ]; then @@ -660,7 +740,7 @@ reset_repeater() { local current_version=$(get_version) - if ask_yes_no "Confirm Reset of pyMC Repeater restoring to default configuration.\n\nContinue?"; then + if ask_yes_no "Confirm Reset of openHop Repeater restoring to default configuration.\n\nContinue?"; then # Show info that upgrade is starting show_info "Reseting" "Starting reset process...\n\nProgress will be shown in the terminal." @@ -731,7 +811,7 @@ upgrade_repeater() { local current_version=$(get_version) if [[ "$silent" != "true" ]]; then - if ! ask_yes_no "Confirm Upgrade" "Current version: $current_version\n\nThis will upgrade pyMC Repeater while preserving your configuration.\n\nContinue?"; then + if ! ask_yes_no "Confirm Upgrade" "Current version: $current_version\n\nThis will upgrade openHop Repeater while preserving your configuration.\n\nContinue?"; then return 0 fi @@ -746,6 +826,10 @@ upgrade_repeater() { echo "[1/9] Stopping service..." systemctl stop "$SERVICE_NAME" 2>/dev/null || true + echo "[1.5/9] Migrating legacy paths..." + migrate_legacy_paths + cleanup_stale_source_trees + echo "[2/9] Backing up configuration..." if [ -d "$CONFIG_DIR" ]; then cp -r "$CONFIG_DIR" "$CONFIG_DIR.backup.$(date +%Y%m%d_%H%M%S)" 2>/dev/null || true @@ -777,11 +861,11 @@ upgrade_repeater() { echo "[4/9] Installing files..." SCRIPT_DIR="$(dirname "$0")" - if ! cp "$SCRIPT_DIR/pymc-repeater.service" /etc/systemd/system/; then + if ! cp "$SCRIPT_DIR/openhop-repeater.service" /etc/systemd/system/; then echo " ⚠ Warning: Failed to update service file – old service file may remain" fi - cp "$SCRIPT_DIR/radio-settings.json" /var/lib/pymc_repeater/ 2>/dev/null || true - cp "$SCRIPT_DIR/radio-presets.json" /var/lib/pymc_repeater/ 2>/dev/null || true + cp "$SCRIPT_DIR/radio-settings.json" "$DATA_DIR/" 2>/dev/null || true + cp "$SCRIPT_DIR/radio-presets.json" "$DATA_DIR/" 2>/dev/null || true echo " ✓ Files updated" echo "[5/9] Validating and updating configuration..." @@ -797,8 +881,8 @@ upgrade_repeater() { done # Install/update CH341 udev rules SCRIPT_DIR_UPGRADE="$(cd "$(dirname "$0")" && pwd)" - if [ -f "$SCRIPT_DIR_UPGRADE/../pyMC_core/99-ch341.rules" ]; then - cp "$SCRIPT_DIR_UPGRADE/../pyMC_core/99-ch341.rules" /etc/udev/rules.d/99-ch341.rules + if [ -f "$SCRIPT_DIR_UPGRADE/../openhop-core/99-ch341.rules" ]; then + cp "$SCRIPT_DIR_UPGRADE/../openhop-core/99-ch341.rules" /etc/udev/rules.d/99-ch341.rules udevadm control --reload-rules 2>/dev/null || true udevadm trigger 2>/dev/null || true echo " ✓ CH341 udev rules updated" @@ -810,15 +894,15 @@ upgrade_repeater() { echo "[6/9] Fixing permissions..." # Venv stays root-owned (pip runs as root); service user only needs read+execute - chown -R "$SERVICE_USER:$SERVICE_USER" "$CONFIG_DIR" "$LOG_DIR" /var/lib/pymc_repeater 2>/dev/null || true + chown -R "$SERVICE_USER:$SERVICE_USER" "$CONFIG_DIR" "$LOG_DIR" "$DATA_DIR" 2>/dev/null || true chown root:root "$INSTALL_DIR" 2>/dev/null || true chmod 755 "$INSTALL_DIR" 2>/dev/null || true chmod 750 "$CONFIG_DIR" "$LOG_DIR" 2>/dev/null || true - chmod 755 /var/lib/pymc_repeater 2>/dev/null || true + chmod 755 "$DATA_DIR" 2>/dev/null || true # Pre-create the .config directory that the service will need - mkdir -p /var/lib/pymc_repeater/.config/pymc_repeater 2>/dev/null || true - chown -R "$SERVICE_USER:$SERVICE_USER" /var/lib/pymc_repeater/.config 2>/dev/null || true + mkdir -p "$DATA_DIR/.config/openhop_repeater" 2>/dev/null || true + chown -R "$SERVICE_USER:$SERVICE_USER" "$DATA_DIR/.config" 2>/dev/null || true # Configure polkit for passwordless service restart POLKIT_VERSION=$(pkaction --version 2>/dev/null | awk '{print $NF}') @@ -826,36 +910,36 @@ upgrade_repeater() { echo "Polkit 0.106 or greater detected, using rules file" echo ">>> Configuring polkit for service management..." mkdir -p /etc/polkit-1/rules.d - cat > /etc/polkit-1/rules.d/10-pymc-repeater.rules <<'EOF' + cat > /etc/polkit-1/rules.d/10-openhop-repeater.rules <<'EOF' polkit.addRule(function(action, subject) { if (action.id == "org.freedesktop.systemd1.manage-units" && - action.lookup("unit") == "pymc-repeater.service" && + action.lookup("unit") == "openhop-repeater.service" && subject.user == "repeater") { return polkit.Result.YES; } }); EOF - chmod 0644 /etc/polkit-1/rules.d/10-pymc-repeater.rules + chmod 0644 /etc/polkit-1/rules.d/10-openhop-repeater.rules else echo "Polkit 0.105 or less detected, using pkla file" mkdir -p /etc/polkit-1/localauthority/50-local.d - cat > /etc/polkit-1/localauthority/50-local.d/10-pymc-repeater.pkla <<'EOF' -[Allow repeater to restart pymc-repeater service] + cat > /etc/polkit-1/localauthority/50-local.d/10-openhop-repeater.pkla <<'EOF' +[Allow repeater to restart openhop-repeater service] Identity=unix-user:repeater Action=org.freedesktop.systemd1.manage-units ResultAny=yes ResultInactive=yes ResultActive=yes EOF - chmod 0644 /etc/polkit-1/localauthority/50-local.d/10-pymc-repeater.pkla + chmod 0644 /etc/polkit-1/localauthority/50-local.d/10-openhop-repeater.pkla fi # Also configure sudoers as fallback for service restart mkdir -p /etc/sudoers.d - cat > /etc/sudoers.d/pymc-repeater <<'EOF' -# Allow repeater user to manage the pymc-repeater service without password -repeater ALL=(root) NOPASSWD: /usr/bin/systemctl restart pymc-repeater, /usr/bin/systemctl stop pymc-repeater, /usr/bin/systemctl start pymc-repeater, /usr/bin/systemctl status pymc-repeater, /usr/local/bin/pymc-do-upgrade + cat > /etc/sudoers.d/openhop-repeater <<'EOF' +# Allow repeater user to manage the openhop-repeater service without password +repeater ALL=(root) NOPASSWD: /usr/bin/systemctl restart openhop-repeater, /usr/bin/systemctl stop openhop-repeater, /usr/bin/systemctl start openhop-repeater, /usr/bin/systemctl status openhop-repeater, /usr/local/bin/pymc-do-upgrade EOF - chmod 0440 /etc/sudoers.d/pymc-repeater + chmod 0440 /etc/sudoers.d/openhop-repeater # Install / refresh OTA upgrade wrapper cat > /usr/local/bin/pymc-do-upgrade <<'UPGRADEEOF' #!/bin/bash @@ -864,7 +948,7 @@ EOF set -e CHANNEL="${1:-main}" PRETEND_VERSION="${2:-}" -VENV_DIR="/opt/pymc_repeater/venv" +VENV_DIR="/opt/openhop_repeater/venv" VENV_PIP="$VENV_DIR/bin/pip" VENV_PYTHON="$VENV_DIR/bin/python" # Validate: only allow safe git ref characters @@ -881,14 +965,22 @@ if [ ! -x "$VENV_PYTHON" ]; then python3 -m venv --system-site-packages "$VENV_DIR" "$VENV_PIP" install --upgrade pip setuptools wheel >/dev/null 2>&1 || true fi +# ---- Legacy path migration: openhop-repeater -> openhop_repeater ---- +if [ -d /opt/openhop-repeater ] && [ ! -e /opt/openhop_repeater ]; then + mv /opt/openhop-repeater /opt/openhop_repeater +fi # ---- Migration: clean up legacy service unit issues ---- -SVC_UNIT=/etc/systemd/system/pymc-repeater.service +SVC_UNIT=/etc/systemd/system/openhop-repeater.service if grep -q 'PYTHONPATH' "$SVC_UNIT" 2>/dev/null; then sed -i '/^Environment=.*PYTHONPATH/d' "$SVC_UNIT" systemctl daemon-reload fi -if grep -q 'WorkingDirectory=/opt/pymc_repeater' "$SVC_UNIT" 2>/dev/null; then - sed -i 's|WorkingDirectory=/opt/pymc_repeater|WorkingDirectory=/var/lib/pymc_repeater|' "$SVC_UNIT" +if grep -q 'WorkingDirectory=/opt/openhop_repeater' "$SVC_UNIT" 2>/dev/null; then + sed -i 's|WorkingDirectory=/opt/openhop_repeater|WorkingDirectory=/var/lib/openhop_repeater|' "$SVC_UNIT" + systemctl daemon-reload +fi +if grep -q 'WorkingDirectory=/opt/openhop-repeater' "$SVC_UNIT" 2>/dev/null; then + sed -i 's|WorkingDirectory=/opt/openhop-repeater|WorkingDirectory=/var/lib/openhop_repeater|' "$SVC_UNIT" systemctl daemon-reload fi if grep -q 'ExecStart=/usr/bin/python3' "$SVC_UNIT" 2>/dev/null; then @@ -896,10 +988,13 @@ if grep -q 'ExecStart=/usr/bin/python3' "$SVC_UNIT" 2>/dev/null; then systemctl daemon-reload fi # ---- Remove stale source trees that shadow the venv package ---- -[ -d /opt/pymc_repeater/repeater ] && rm -rf /opt/pymc_repeater/repeater +[ -d /opt/openhop_repeater/repeater ] && rm -rf /opt/openhop_repeater/repeater +[ -d /opt/openhop-repeater/repeater ] && rm -rf /opt/openhop-repeater/repeater +[ -d /opt/openhop_repeater/openhop-repeater ] && rm -rf /opt/openhop_repeater/openhop-repeater +[ -d /opt/openhop-repeater/openhop-repeater ] && rm -rf /opt/openhop-repeater/openhop-repeater # ---- Remove old system-level packages to avoid confusion ---- -python3 -m pip uninstall -y pymc_repeater 2>/dev/null || true -python3 -m pip uninstall -y pymc_core 2>/dev/null || true +python3 -m pip uninstall -y openhop_repeater 2>/dev/null || true +python3 -m pip uninstall -y openhop_core 2>/dev/null || true # ---- Try R2 wheels first for faster OTA upgrades ---- R2_BASE_URL="https://wheel.pymc.dev/pymc_build_deps" MACHINE_ARCH=$(uname -m) @@ -915,14 +1010,14 @@ python3 -m pip uninstall -y pymc_core 2>/dev/null || true echo "[pymc-do-upgrade] Trying dependencies from R2 wheels..." "$VENV_PIP" install --find-links "${WHEEL_BASE}/index.html" --no-cache-dir "pycryptodome>=3.23.0" "PyNaCl>=1.5.0" cffi "pyyaml>=6.0.0" 2>/dev/null || true fi - # ---- Install pymc_repeater from git ---- + # ---- Install openhop_repeater from git ---- if "$VENV_PIP" install \ --upgrade \ --no-cache-dir \ - "pymc_repeater[hardware] @ git+https://github.com/rightup/pyMC_Repeater.git@${CHANNEL}"; then + "openhop_repeater[hardware] @ git+https://github.com/rightup/openhop-repeater.git@${CHANNEL}"; then # Keep web/OTA updates aligned with manage.sh install/upgrade defaults. - RADIO_BASE_URL="https://raw.githubusercontent.com/rightup/pyMC_Repeater/${CHANNEL}" - RADIO_STORAGE_DIR="/var/lib/pymc_repeater" + RADIO_BASE_URL="https://raw.githubusercontent.com/rightup/openhop-repeater/${CHANNEL}" + RADIO_STORAGE_DIR="/var/lib/openhop_repeater" mkdir -p "$RADIO_STORAGE_DIR" wget -qO "$RADIO_STORAGE_DIR/radio-settings.json" "${RADIO_BASE_URL}/radio-settings.json" 2>/dev/null || true wget -qO "$RADIO_STORAGE_DIR/radio-presets.json" "${RADIO_BASE_URL}/radio-presets.json" 2>/dev/null || true @@ -939,7 +1034,7 @@ UPGRADEEOF echo "=== Installing Python Dependencies ===" echo "" - echo "Updating pymc_repeater and dependencies (including pymc_core from PyPI)..." + echo "Updating openhop_repeater and dependencies (including openhop_core from PyPI)..." echo "This may take a few minutes..." echo "" @@ -969,7 +1064,7 @@ UPGRADEEOF migrate_to_venv # Install into the venv (clean, no system-packages flags needed) - echo "Upgrading pymc_repeater into venv ($VENV_DIR)..." + echo "Upgrading openhop_repeater into venv ($VENV_DIR)..." # Attempt R2 wheels first for faster installation if [ "$R2_ENABLED" -eq 1 ]; then @@ -1091,10 +1186,10 @@ uninstall_repeater() { return fi - if ask_yes_no "Confirm Uninstall" "This will completely remove pyMC Repeater including:\n\n- Service and files\n- Configuration (backup will be created)\n- Logs and data\n\nThis action cannot be undone!\n\nContinue?"; then + if ask_yes_no "Confirm Uninstall" "This will completely remove openHop Repeater including:\n\n- Service and files\n- Configuration (backup will be created)\n- Logs and data\n\nThis action cannot be undone!\n\nContinue?"; then echo "" echo "═══════════════════════════════════════════════════════════════" - echo " Uninstalling pyMC Repeater" + echo " Uninstalling openHop Repeater" echo "═══════════════════════════════════════════════════════════════" echo "" @@ -1105,24 +1200,28 @@ uninstall_repeater() { ( echo "20"; echo "# Backing up configuration..." if [ -d "$CONFIG_DIR" ]; then - cp -r "$CONFIG_DIR" "/tmp/pymc_repeater_config_backup_$(date +%Y%m%d_%H%M%S)" 2>/dev/null || true + cp -r "$CONFIG_DIR" "/tmp/openhop_repeater_config_backup_$(date +%Y%m%d_%H%M%S)" 2>/dev/null || true fi echo "40"; echo "# Removing service files..." - rm -f /etc/systemd/system/pymc-repeater.service + rm -f /etc/systemd/system/openhop-repeater.service systemctl daemon-reload echo "50"; echo "# Removing polkit and sudoers rules..." - rm -f /etc/polkit-1/rules.d/10-pymc-repeater.rules || true - rm -f /etc/polkit-1/localauthority/50-local.d/10-pymc-repeater.pkla || true - rm -f /etc/sudoers.d/pymc-repeater + rm -f /etc/polkit-1/rules.d/10-openhop-repeater.rules || true + rm -f /etc/polkit-1/localauthority/50-local.d/10-openhop-repeater.pkla || true + rm -f /etc/sudoers.d/openhop-repeater rm -f /usr/local/bin/pymc-do-upgrade echo "60"; echo "# Removing installation..." rm -rf "$INSTALL_DIR" rm -rf "$CONFIG_DIR" rm -rf "$LOG_DIR" - rm -rf /var/lib/pymc_repeater + rm -rf "$DATA_DIR" + rm -rf "$LEGACY_INSTALL_DIR" + rm -rf "$LEGACY_CONFIG_DIR" + rm -rf "$LEGACY_LOG_DIR" + rm -rf "$LEGACY_DATA_DIR" echo "80"; echo "# Removing service user..." if id "$SERVICE_USER" &>/dev/null; then @@ -1130,9 +1229,9 @@ uninstall_repeater() { fi echo "100"; echo "# Uninstall complete!" - ) | $DIALOG --backtitle "pyMC Repeater Management" --title "Uninstalling" --gauge "Removing pyMC Repeater..." 8 70 0 + ) | $DIALOG --backtitle "openHop Repeater Management" --title "Uninstalling" --gauge "Removing openHop Repeater..." 8 70 0 - show_info "Uninstall Complete" "\npyMC Repeater has been completely removed.\n\nConfiguration backup saved to /tmp/\n\nThank you for using pyMC Repeater!" + show_info "Uninstall Complete" "\nopenHop Repeater has been completely removed.\n\nConfiguration backup saved to /tmp/\n\nThank you for using openHop Repeater!" fi } @@ -1167,9 +1266,9 @@ manage_service() { systemctl start "$SERVICE_NAME" if is_running; then if [[ "$silent" == "true" ]]; then - echo "✓ pyMC Repeater service has been started successfully." + echo "✓ openHop Repeater service has been started successfully." else - show_info "Service Started" "\n✓ pyMC Repeater service has been started successfully." + show_info "Service Started" "\n✓ openHop Repeater service has been started successfully." fi else if [[ "$silent" == "true" ]]; then @@ -1183,18 +1282,18 @@ manage_service() { "stop") systemctl stop "$SERVICE_NAME" if [[ "$silent" == "true" ]]; then - echo "✓ pyMC Repeater service has been stopped." + echo "✓ openHop Repeater service has been stopped." else - show_info "Service Stopped" "\n✓ pyMC Repeater service has been stopped." + show_info "Service Stopped" "\n✓ openHop Repeater service has been stopped." fi ;; "restart") systemctl restart "$SERVICE_NAME" if is_running; then if [[ "$silent" == "true" ]]; then - echo "✓ pyMC Repeater service has been restarted successfully." + echo "✓ openHop Repeater service has been restarted successfully." else - show_info "Service Restarted" "\n✓ pyMC Repeater service has been restarted successfully." + show_info "Service Restarted" "\n✓ openHop Repeater service has been restarted successfully." fi else if [[ "$silent" == "true" ]]; then @@ -1324,14 +1423,14 @@ validate_and_update_config() { # Main script logic if [ "$1" = "--help" ] || [ "$1" = "-h" ]; then - echo "pyMC Repeater Management Script" + echo "openHop Repeater Management Script" echo "" echo "Usage: $0 [action]" echo "" echo "Actions:" - echo " install - Install pyMC Repeater" + echo " install - Install openHop Repeater" echo " upgrade - Upgrade existing installation (CLI is silent by default; use --interactive to show dialogs)" - echo " uninstall - Remove pyMC Repeater" + echo " uninstall - Remove openHop Repeater" echo " config - Configure radio settings" echo " start - Start the service (CLI is silent by default; use --interactive to show dialogs)" echo " stop - Stop the service (CLI is silent by default; use --interactive to show dialogs)" @@ -1355,7 +1454,7 @@ if [ "$1" = "debug" ]; then echo "Script: $0" echo "" echo "Testing dialog..." - $DIALOG --backtitle "pyMC Repeater Management" --title "Test" --msgbox "Dialog test successful!" 8 40 + $DIALOG --backtitle "openHop Repeater Management" --title "Test" --msgbox "Dialog test successful!" 8 40 echo "Dialog test completed." exit 0 fi @@ -1393,7 +1492,7 @@ case "$1" in "logs") clear echo -e "\033[1;36m╔══════════════════════════════════════════════════════════════════════╗\033[0m" - echo -e "\033[1;36m║\033[0m \033[1;37mpyMC Repeater - Live Logs\033[0m \033[1;36m║\033[0m" + echo -e "\033[1;36m║\033[0m \033[1;37mopenHop Repeater - Live Logs\033[0m \033[1;36m║\033[0m" echo -e "\033[1;36m║\033[0m \033[0;90m(Press Ctrl+C to return)\033[0m \033[1;36m║\033[0m" echo -e "\033[1;36m╚══════════════════════════════════════════════════════════════════════╝\033[0m" echo "" diff --git a/pymc-repeater.service b/openhop-repeater.service similarity index 63% rename from pymc-repeater.service rename to openhop-repeater.service index cc7715d..a9026eb 100644 --- a/pymc-repeater.service +++ b/openhop-repeater.service @@ -1,8 +1,8 @@ -#Systemd service file template for Py MC - Meshcore Repeater Daemon. -#Install as /etc/systemd/system/pymc-repeater.service +#Systemd service file template for openHop - Meshcore Repeater Daemon. +#Install as /etc/systemd/system/openhop-repeater.service [Unit] -Description=pyMC Repeater Daemon +Description=openHop Repeater Daemon After=network-online.target dbus.socket Wants=network-online.target Requires=dbus.socket @@ -11,10 +11,10 @@ Requires=dbus.socket Type=simple User=repeater Group=repeater -WorkingDirectory=/var/lib/pymc_repeater +WorkingDirectory=/var/lib/openhop_repeater # Start command - use venv python to avoid system package conflicts -ExecStart=/opt/pymc_repeater/venv/bin/python -m repeater.main --config /etc/pymc_repeater/config.yaml +ExecStart=/opt/openhop_repeater/venv/bin/python -m repeater.main --config /etc/openhop_repeater/config.yaml # Restart on failure Restart=on-failure @@ -29,10 +29,10 @@ MemoryHigh=256M # Logging StandardOutput=journal StandardError=journal -SyslogIdentifier=pymc-repeater +SyslogIdentifier=openhop-repeater # Security (relaxed for service self-restart via sudo) -ReadWritePaths=/var/log/pymc_repeater /var/lib/pymc_repeater /etc/pymc_repeater +ReadWritePaths=/var/log/openhop_repeater /var/lib/openhop_repeater /etc/openhop_repeater SupplementaryGroups=plugdev dialout # Allow GPS time sync to update CLOCK_REALTIME without running as root, also allow changing user via polkit or sudo diff --git a/pyproject.toml b/pyproject.toml index 13b4ef3..02019d6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ requires = ["setuptools>=61.0", "wheel", "setuptools_scm>=8.0"] build-backend = "setuptools.build_meta" [project] -name = "pymc_repeater" +name = "openhop_repeater" dynamic = ["version"] authors = [ {name = "Rightup", email = "rightup@pymc.dev"}, @@ -29,7 +29,7 @@ keywords = ["mesh", "networking", "lora", "repeater", "daemon", "iot"] dependencies = [ - "pymc_core[hardware] @ git+https://github.com/rightup/pyMC_core.git@dev", + "openhop_core[hardware]", "pyyaml>=6.0.0", "cherrypy>=18.0.0", "paho-mqtt>=1.6.0", @@ -45,7 +45,7 @@ dependencies = [ [project.optional-dependencies] # SX1262/SPI support (Linux only; required for Raspberry Pi HATs) hardware = [ - "pymc_core[hardware] @ git+https://github.com/rightup/pyMC_core.git@dev", + "openhop_core[hardware] @ git+https://github.com/rightup/openhop-core.git@dev", ] # RRD metrics (Performance Metrics chart); system librrd required (e.g. apt install rrdtool) rrd = [ @@ -59,7 +59,7 @@ dev = [ ] [project.scripts] -pymc-repeater = "repeater.main:main" +openhop-repeater = "repeater.main:main" pymc-cli = "repeater.local_cli:main" [tool.setuptools.packages.find] diff --git a/radio-settings.json b/radio-settings.json index b6ed4bf..2c2e640 100644 --- a/radio-settings.json +++ b/radio-settings.json @@ -411,7 +411,7 @@ }, "kiss": { "name": "KISS modem (serial)", - "description": "MeshCore KISS modem over serial - requires pyMC_core with KISS support.", + "description": "MeshCore KISS modem over serial - requires openhop-core with KISS support.", "connection_type": "usb", "radio_type": "kiss", "tx_power": 14, diff --git a/repeater/__init__.py b/repeater/__init__.py index 19b87fa..f381003 100644 --- a/repeater/__init__.py +++ b/repeater/__init__.py @@ -4,6 +4,6 @@ except ImportError: try: from importlib.metadata import version - __version__ = version("pymc_repeater") + __version__ = version("openhop_repeater") except Exception: __version__ = "unknown" diff --git a/repeater/companion/__init__.py b/repeater/companion/__init__.py index f104252..5bdcf5d 100644 --- a/repeater/companion/__init__.py +++ b/repeater/companion/__init__.py @@ -1,4 +1,4 @@ -"""Companion identity support for pyMC Repeater. +"""Companion identity support for openHop Repeater. Exposes the MeshCore companion frame protocol over TCP for standard clients. """ diff --git a/repeater/companion/bridge.py b/repeater/companion/bridge.py index d33003d..406bd34 100644 --- a/repeater/companion/bridge.py +++ b/repeater/companion/bridge.py @@ -13,7 +13,7 @@ import logging from enum import Enum from typing import Any, Callable, Optional -from pymc_core.companion import CompanionBridge +from openhop_core.companion import CompanionBridge logger = logging.getLogger("RepeaterCompanionBridge") diff --git a/repeater/companion/constants.py b/repeater/companion/constants.py index 2fa3b16..0fe345a 100644 --- a/repeater/companion/constants.py +++ b/repeater/companion/constants.py @@ -1,11 +1,11 @@ -"""Companion frame protocol constants — re-exported from pyMC_core. +"""Companion frame protocol constants — re-exported from openhop-core. -All protocol constants now live in :mod:`pymc_core.companion.constants`. +All protocol constants now live in :mod:`openhop_core.companion.constants`. This module re-exports them so existing repeater imports continue to work. """ # Re-exports; F401 ignored for re-exported names. -from pymc_core.companion.constants import ( # noqa: F401 +from openhop_core.companion.constants import ( # noqa: F401 ADV_TYPE_CHAT, ADV_TYPE_REPEATER, ADV_TYPE_ROOM, diff --git a/repeater/companion/frame_server.py b/repeater/companion/frame_server.py index 1764694..63d0edb 100644 --- a/repeater/companion/frame_server.py +++ b/repeater/companion/frame_server.py @@ -1,7 +1,7 @@ """ Repeater-specific CompanionFrameServer with SQLite persistence. -Thin subclass of :class:`pymc_core.companion.frame_server.CompanionFrameServer` +Thin subclass of :class:`openhop_core.companion.frame_server.CompanionFrameServer` that adds SQLite-backed message, contact, and channel persistence via a ``sqlite_handler`` dependency. """ @@ -12,9 +12,9 @@ import asyncio import logging from typing import Optional -from pymc_core.companion.constants import RESP_CODE_NO_MORE_MESSAGES -from pymc_core.companion.frame_server import CompanionFrameServer as _BaseFrameServer -from pymc_core.companion.models import QueuedMessage +from openhop_core.companion.constants import RESP_CODE_NO_MORE_MESSAGES +from openhop_core.companion.frame_server import CompanionFrameServer as _BaseFrameServer +from openhop_core.companion.models import QueuedMessage logger = logging.getLogger("CompanionFrameServer") @@ -45,8 +45,8 @@ class CompanionFrameServer(_BaseFrameServer): port=port, bind_address=bind_address, client_idle_timeout_sec=client_idle_timeout_sec, - device_model="pyMC-Repeater-Companion", - device_version=None, # use FIRMWARE_VER_CODE from pyMC_core + device_model="openHop-Repeater-Companion", + device_version=None, # use FIRMWARE_VER_CODE from openhop-core build_date="13 Feb 2026", local_hash=local_hash, stats_getter=stats_getter, diff --git a/repeater/companion/identity_resolve.py b/repeater/companion/identity_resolve.py index a5b78ef..3321c38 100644 --- a/repeater/companion/identity_resolve.py +++ b/repeater/companion/identity_resolve.py @@ -50,7 +50,7 @@ def derive_companion_public_key_hex(identity_key: Any) -> Optional[str]: if raw is None: return None try: - from pymc_core import LocalIdentity + from openhop_core import LocalIdentity identity = LocalIdentity(seed=raw) return identity.get_public_key().hex() diff --git a/repeater/companion/utils.py b/repeater/companion/utils.py index ca64092..c13a569 100644 --- a/repeater/companion/utils.py +++ b/repeater/companion/utils.py @@ -5,7 +5,7 @@ from __future__ import annotations import logging from typing import Any, Dict, Optional -from pymc_core.companion.constants import DEFAULT_MAX_CONTACTS +from openhop_core.companion.constants import DEFAULT_MAX_CONTACTS logger = logging.getLogger(__name__) @@ -14,7 +14,7 @@ _INVALID_NODE_NAME_CHARS = "\n\r\x00" # Optional per-companion RepeaterCompanionBridge constructor settings (power-user). COMPANION_BRIDGE_SETTING_KEYS = frozenset({"max_contacts", "offline_queue_size"}) -# Settings that must not be applied from config (fixed at pymc_core defaults). +# Settings that must not be applied from config (fixed at openhop_core defaults). _COMPANION_IGNORED_BRIDGE_KEYS = frozenset({"max_channels", "adv_type"}) # Contact flag bit 0 marks a favourite (protected from forced-trim eviction). @@ -102,7 +102,7 @@ def parse_companion_bridge_kwargs(settings: dict) -> Dict[str, int]: def effective_max_contacts(bridge_kwargs: Dict[str, int]) -> int: - """Return max_contacts from parsed kwargs or pymc_core default.""" + """Return max_contacts from parsed kwargs or openhop_core default.""" return bridge_kwargs.get("max_contacts", DEFAULT_MAX_CONTACTS) @@ -253,7 +253,7 @@ def format_companion_bridge_limits(bridge_kwargs: Dict[str, int]) -> str: def companion_hash_str_from_identity_key(identity_key: Any) -> str: """Derive companion_hash storage key (0xHH) from an identity_key config value.""" - from pymc_core import LocalIdentity + from openhop_core import LocalIdentity if isinstance(identity_key, str): key_bytes = bytes.fromhex(normalize_companion_identity_key(identity_key)) diff --git a/repeater/config.py b/repeater/config.py index ffea57f..a0c5063 100644 --- a/repeater/config.py +++ b/repeater/config.py @@ -140,7 +140,7 @@ def resolve_storage_dir( config: Dict[str, Any], *, config_path: Optional[str] = None, - default: str = "/var/lib/pymc_repeater", + default: str = "/var/lib/openhop_repeater", ) -> Path: storage_dir_cfg = ( @@ -195,7 +195,7 @@ def get_node_info(config: Dict[str, Any]) -> Dict[str, Any]: def load_config(config_path: Optional[str] = None) -> Dict[str, Any]: if config_path is None: - config_path = os.getenv("PYMC_REPEATER_CONFIG", "/etc/pymc_repeater/config.yaml") + config_path = os.getenv("PYMC_REPEATER_CONFIG", "/etc/openhop_repeater/config.yaml") # Check if config file exists if not Path(config_path).exists(): @@ -235,7 +235,7 @@ def load_config(config_path: Optional[str] = None) -> Dict[str, Any]: "request_timeout_seconds": 10, "verify_tls": True, "api_token": None, - "cert_store_dir": "/etc/pymc_repeater/glass", + "cert_store_dir": "/etc/openhop_repeater/glass", } if "gps" not in config: @@ -323,7 +323,7 @@ def save_config(config_data: Dict[str, Any], config_path: Optional[str] = None) True if successful, False otherwise """ if config_path is None: - config_path = os.getenv("PYMC_REPEATER_CONFIG", "/etc/pymc_repeater/config.yaml") + config_path = os.getenv("PYMC_REPEATER_CONFIG", "/etc/openhop_repeater/config.yaml") try: # Create backup of existing config @@ -387,16 +387,16 @@ def _load_or_create_identity_key(path: Optional[str] = None) -> bytes: if path is None: # Check system-wide location first (matches config.yaml location) - system_key_path = Path("/etc/pymc_repeater/identity.key") + system_key_path = Path("/etc/openhop_repeater/identity.key") if system_key_path.exists(): key_path = system_key_path else: # Follow XDG spec xdg_config_home = os.environ.get("XDG_CONFIG_HOME") if xdg_config_home: - config_dir = Path(xdg_config_home) / "pymc_repeater" + config_dir = Path(xdg_config_home) / "openhop_repeater" else: - config_dir = Path.home() / ".config" / "pymc_repeater" + config_dir = Path.home() / ".config" / "openhop_repeater" key_path = config_dir / "identity.key" else: key_path = Path(path) @@ -475,7 +475,7 @@ def get_radio_for_board(board_config: dict): radio_type = "kiss" if radio_type in ("sx1262", "sx1262_ch341"): - from pymc_core.hardware.sx1262_wrapper import SX1262Radio + from openhop_core.hardware.sx1262_wrapper import SX1262Radio # Get radio and SPI configuration - all settings must be in config file spi_config = board_config.get("sx1262") @@ -492,8 +492,8 @@ def get_radio_for_board(board_config: dict): if not ch341_cfg: raise ValueError("Missing 'ch341' section in configuration file") - from pymc_core.hardware.lora.LoRaRF.SX126x import set_spi_transport - from pymc_core.hardware.transports.ch341_spi_transport import CH341SPITransport + from openhop_core.hardware.lora.LoRaRF.SX126x import set_spi_transport + from openhop_core.hardware.transports.ch341_spi_transport import CH341SPITransport vid = _parse_int(ch341_cfg.get("vid"), default=0x1A86) pid = _parse_int(ch341_cfg.get("pid"), default=0x5512) @@ -534,7 +534,7 @@ def get_radio_for_board(board_config: dict): combined_config["en_pins"] = en_pins # Add optional GPIO parameters if specified in config - # These wont be supported by older versions of pymc_core + # These wont be supported by older versions of openhop_core if "gpio_chip" in spi_config: combined_config["gpio_chip"] = _parse_int(spi_config["gpio_chip"], default=0) if "use_gpiod_backend" in spi_config: @@ -554,16 +554,16 @@ def get_radio_for_board(board_config: dict): elif radio_type == "kiss": try: - from pymc_core.hardware.kiss_modem_wrapper import KissModemWrapper + from openhop_core.hardware.kiss_modem_wrapper import KissModemWrapper except ImportError: try: - from pymc_core.hardware.kiss_serial_wrapper import ( + from openhop_core.hardware.kiss_serial_wrapper import ( KissSerialWrapper as KissModemWrapper, ) except ImportError: raise RuntimeError( - "KISS modem support requires pyMC_core with KISS support. " - "Install your fork with: pip install -e /path/to/pyMC_core" + "KISS modem support requires openhop-core with KISS support. " + "Install your fork with: pip install -e /path/to/openhop-core" ) from None kiss_config = board_config.get("kiss") @@ -613,11 +613,11 @@ def get_radio_for_board(board_config: dict): elif radio_type == "pymc_tcp": try: - from pymc_core.hardware.tcp_radio import TCPLoRaRadio + from openhop_core.hardware.tcp_radio import TCPLoRaRadio except ImportError: raise RuntimeError( - "pymc_tcp radio requires pyMC_core >= the release that includes " - "PR pyMC-dev/pyMC_core#68 (merged 2026-05-13). " + "pymc_tcp radio requires openhop-core >= the release that includes " + "PR pyMC-dev/openhop-core#68 (merged 2026-05-13). " "Reinstall the [hardware] extra to pick it up." ) from None @@ -657,11 +657,11 @@ def get_radio_for_board(board_config: dict): elif radio_type == "pymc_usb": try: - from pymc_core.hardware.usb_radio import USBLoRaRadio + from openhop_core.hardware.usb_radio import USBLoRaRadio except ImportError: raise RuntimeError( - "pymc_usb radio requires pyMC_core >= the release that includes " - "PR pyMC-dev/pyMC_core#68 (merged 2026-05-13). " + "pymc_usb radio requires openhop-core >= the release that includes " + "PR pyMC-dev/openhop-core#68 (merged 2026-05-13). " "Reinstall the [hardware] extra to pick it up." ) from None diff --git a/repeater/data_acquisition/glass_handler.py b/repeater/data_acquisition/glass_handler.py index e80ebd6..61e8f27 100644 --- a/repeater/data_acquisition/glass_handler.py +++ b/repeater/data_acquisition/glass_handler.py @@ -47,7 +47,7 @@ class GlassHandler: self.verify_tls = True self.api_token = "" # nosec - runtime config value, not a hardcoded credential self.inform_interval_seconds = 30 - self.cert_store_dir = "/etc/pymc_repeater/glass" + self.cert_store_dir = "/etc/openhop_repeater/glass" self._cert_expires_at: Optional[str] = None self.mqtt_enabled = False self.mqtt_broker_host = "localhost" @@ -132,8 +132,8 @@ class GlassHandler: int(glass_cfg.get("inform_interval_seconds", self.inform_interval_seconds)) ) self.cert_store_dir = str( - glass_cfg.get("cert_store_dir", "/etc/pymc_repeater/glass") - or "/etc/pymc_repeater/glass" + glass_cfg.get("cert_store_dir", "/etc/openhop_repeater/glass") + or "/etc/openhop_repeater/glass" ) self.client_cert_path = ( str(glass_cfg.get("client_cert_path")).strip() diff --git a/repeater/data_acquisition/mqtt_handler.py b/repeater/data_acquisition/mqtt_handler.py index fda3c08..d0730c2 100644 --- a/repeater/data_acquisition/mqtt_handler.py +++ b/repeater/data_acquisition/mqtt_handler.py @@ -255,7 +255,7 @@ class _BrokerConnection: ) self.base_topic = f"meshcore/{self.iata_code}/{self.public_key}" - from pymc_core.protocol.utils import PAYLOAD_TYPES + from openhop_core.protocol.utils import PAYLOAD_TYPES disallowed_types = broker.get("disallowed_packet_types", []) type_name_map = {name: code for code, name in PAYLOAD_TYPES.items()} @@ -973,7 +973,7 @@ class MeshCoreToMqttPusher: "model": "PyMC-Repeater", "firmware_version": self.app_version, "radio": radio_config or self.radio_config, - "client_version": f"pyMC_repeater/{self.app_version}", + "client_version": f"openhop_repeater/{self.app_version}", "stats": {**live_stats, "errors": 0, "queue_len": 0, **(extra_stats or {})}, } diff --git a/repeater/data_acquisition/sqlite_handler.py b/repeater/data_acquisition/sqlite_handler.py index b36bfc3..833755e 100644 --- a/repeater/data_acquisition/sqlite_handler.py +++ b/repeater/data_acquisition/sqlite_handler.py @@ -1201,9 +1201,9 @@ class SQLiteHandler: return cached["value"] cutoff = now - (hours * 3600) - # Align with pyMC_core feat/newRadios PAYLOAD_TYPES (0x0B = CONTROL) + # Align with openhop-core feat/newRadios PAYLOAD_TYPES (0x0B = CONTROL) try: - from pymc_core.protocol.utils import PAYLOAD_TYPES as _PT + from openhop_core.protocol.utils import PAYLOAD_TYPES as _PT _human = { "REQ": "Request", @@ -1764,7 +1764,7 @@ class SQLiteHandler: A base64-encoded transport key derived from the name """ try: - from pymc_core.protocol.transport_keys import get_auto_key_for + from openhop_core.protocol.transport_keys import get_auto_key_for key_bytes = get_auto_key_for(name) diff --git a/repeater/engine.py b/repeater/engine.py index 8e4fb6c..94b4f68 100644 --- a/repeater/engine.py +++ b/repeater/engine.py @@ -6,9 +6,9 @@ import time from collections import OrderedDict, deque from typing import Optional, Tuple -from pymc_core.node.handlers.base import BaseHandler -from pymc_core.protocol import Packet -from pymc_core.protocol.constants import ( +from openhop_core.node.handlers.base import BaseHandler +from openhop_core.protocol import Packet +from openhop_core.protocol.constants import ( MAX_PATH_SIZE, PAYLOAD_TYPE_ADVERT, PAYLOAD_TYPE_ANON_REQ, @@ -19,7 +19,7 @@ from pymc_core.protocol.constants import ( ROUTE_TYPE_TRANSPORT_DIRECT, ROUTE_TYPE_TRANSPORT_FLOOD, ) -from pymc_core.protocol.packet_utils import PacketHeaderUtils, PathUtils +from openhop_core.protocol.packet_utils import PacketHeaderUtils, PathUtils from repeater.airtime import AirtimeManager from repeater.data_acquisition import StorageCollector @@ -820,7 +820,7 @@ class RepeaterHandler(BaseHandler): return False, "No storage available for transport key validation" try: - from pymc_core.protocol.transport_keys import calc_transport_code + from openhop_core.protocol.transport_keys import calc_transport_code # Check cache validity current_time = time.time() diff --git a/repeater/handler_helpers/__init__.py b/repeater/handler_helpers/__init__.py index 3518ca2..444658b 100644 --- a/repeater/handler_helpers/__init__.py +++ b/repeater/handler_helpers/__init__.py @@ -1,4 +1,4 @@ -"""Handler helper modules for pyMC Repeater.""" +"""Handler helper modules for openHop Repeater.""" from .advert import AdvertHelper from .discovery import DiscoveryHelper diff --git a/repeater/handler_helpers/acl.py b/repeater/handler_helpers/acl.py index 3b61491..e2ad745 100644 --- a/repeater/handler_helpers/acl.py +++ b/repeater/handler_helpers/acl.py @@ -2,8 +2,8 @@ import logging import time from typing import Dict, Optional -from pymc_core.protocol import Identity -from pymc_core.protocol.constants import PUB_KEY_SIZE +from openhop_core.protocol import Identity +from openhop_core.protocol.constants import PUB_KEY_SIZE logger = logging.getLogger("ACL") diff --git a/repeater/handler_helpers/advert.py b/repeater/handler_helpers/advert.py index a91e9e5..63da6dc 100644 --- a/repeater/handler_helpers/advert.py +++ b/repeater/handler_helpers/advert.py @@ -1,5 +1,5 @@ """ -Advertisement packet handling helper for pyMC Repeater. +Advertisement packet handling helper for openHop Repeater. This module processes advertisement packets for neighbor tracking and discovery. Includes adaptive rate limiting based on mesh activity. @@ -13,7 +13,7 @@ from collections import OrderedDict, deque from enum import Enum from typing import Dict, Optional, Tuple -from pymc_core.node.handlers.advert import AdvertHandler +from openhop_core.node.handlers.advert import AdvertHandler logger = logging.getLogger("AdvertHelper") @@ -603,7 +603,7 @@ class AdvertHelper: return # Get route type from packet header - from pymc_core.protocol.constants import PH_ROUTE_MASK + from openhop_core.protocol.constants import PH_ROUTE_MASK route_type = packet.header & PH_ROUTE_MASK diff --git a/repeater/handler_helpers/discovery.py b/repeater/handler_helpers/discovery.py index fab9d76..d31c742 100644 --- a/repeater/handler_helpers/discovery.py +++ b/repeater/handler_helpers/discovery.py @@ -1,5 +1,5 @@ """ -Discovery request/response handling helper for pyMC Repeater. +Discovery request/response handling helper for openHop Repeater. This module handles the processing and response to discovery requests, allowing other nodes to discover repeaters on the mesh network. @@ -9,7 +9,7 @@ import asyncio import logging import secrets -from pymc_core.node.handlers.control import ControlHandler +from openhop_core.node.handlers.control import ControlHandler logger = logging.getLogger("DiscoveryHelper") @@ -133,7 +133,7 @@ class DiscoveryHelper: try: our_pub_key = self.local_identity.get_public_key() - from pymc_core.protocol.packet_builder import PacketBuilder + from openhop_core.protocol.packet_builder import PacketBuilder response_packet = PacketBuilder.create_discovery_response( tag=tag, diff --git a/repeater/handler_helpers/login.py b/repeater/handler_helpers/login.py index 4db7c98..216e6db 100644 --- a/repeater/handler_helpers/login.py +++ b/repeater/handler_helpers/login.py @@ -1,5 +1,5 @@ """ -Login/ANON_REQ packet handling helper for pyMC Repeater. +Login/ANON_REQ packet handling helper for openHop Repeater. This module processes login requests and manages authentication for all identities. """ @@ -8,9 +8,9 @@ import asyncio import logging import time -from pymc_core.node.handlers.anon_request import AnonRateLimiter, AnonRequestHandler -from pymc_core.node.handlers.login_server import LoginServerHandler -from pymc_core.protocol.constants import PAYLOAD_TYPE_ANON_REQ +from openhop_core.node.handlers.anon_request import AnonRateLimiter, AnonRequestHandler +from openhop_core.node.handlers.login_server import LoginServerHandler +from openhop_core.protocol.constants import PAYLOAD_TYPE_ANON_REQ logger = logging.getLogger("LoginHelper") @@ -167,7 +167,7 @@ class LoginHelper: emit the ``*`` wildcard region first (unless unscoped flood is denied), then each allow-flood named region with a leading ``#`` stripped, with no trailing comma. The firmware wildcard is the always-present default flood - scope; pyMC_repeater models that via ``mesh.unscoped_flood_allow`` + scope; openhop_repeater models that via ``mesh.unscoped_flood_allow`` (falling back to ``mesh.global_flood_allow``, default allow). """ parts = [] diff --git a/repeater/handler_helpers/path.py b/repeater/handler_helpers/path.py index f9055c2..98e8e1b 100644 --- a/repeater/handler_helpers/path.py +++ b/repeater/handler_helpers/path.py @@ -12,7 +12,7 @@ class PathHelper: async def process_path_packet(self, packet): - from pymc_core.protocol.crypto import CryptoUtils + from openhop_core.protocol.crypto import CryptoUtils try: if len(packet.payload) < 2: diff --git a/repeater/handler_helpers/protocol_request.py b/repeater/handler_helpers/protocol_request.py index 28a29ac..ae1f6c2 100644 --- a/repeater/handler_helpers/protocol_request.py +++ b/repeater/handler_helpers/protocol_request.py @@ -1,5 +1,5 @@ """ -Protocol request (REQ) handling helper for pyMC Repeater. +Protocol request (REQ) handling helper for openHop Repeater. Provides repeater-specific callbacks for status and telemetry requests. """ @@ -9,7 +9,7 @@ import logging import struct import time -from pymc_core.node.handlers.protocol_request import ( +from openhop_core.node.handlers.protocol_request import ( REQ_TYPE_GET_ACCESS_LIST, REQ_TYPE_GET_NEIGHBOURS, REQ_TYPE_GET_OWNER_INFO, @@ -368,14 +368,14 @@ class ProtocolRequestHelper: Matches C++ simple_repeater: sprintf("%s\\n%s\\n%s", FIRMWARE_VERSION, node_name, owner_info) """ repeater_cfg = self.config.get("repeater", {}) - node_name = repeater_cfg.get("node_name", "pyMC_Repeater") + node_name = repeater_cfg.get("node_name", "openhop-repeater") owner_info = repeater_cfg.get("owner_info", "") # Version: use package version if available, fallback to "pyMC" try: from importlib.metadata import version as pkg_version - fw_version = pkg_version("pymc-repeater") + fw_version = pkg_version("openhop-repeater") except Exception: fw_version = "pyMC" diff --git a/repeater/handler_helpers/room_server.py b/repeater/handler_helpers/room_server.py index 632e82c..7b8d45d 100644 --- a/repeater/handler_helpers/room_server.py +++ b/repeater/handler_helpers/room_server.py @@ -4,8 +4,8 @@ import secrets import time from typing import Dict -from pymc_core.protocol import CryptoUtils, PacketBuilder -from pymc_core.protocol.constants import PAYLOAD_TYPE_TXT_MSG +from openhop_core.protocol import CryptoUtils, PacketBuilder +from openhop_core.protocol.constants import PAYLOAD_TYPE_TXT_MSG logger = logging.getLogger("RoomServer") @@ -102,8 +102,8 @@ class RoomServer: return False try: - from pymc_core.protocol import PacketBuilder - from pymc_core.protocol.constants import ( + from openhop_core.protocol import PacketBuilder + from openhop_core.protocol.constants import ( ADVERT_FLAG_HAS_NAME, ADVERT_FLAG_IS_ROOM_SERVER, ) @@ -357,7 +357,7 @@ class RoomServer: timestamp.to_bytes(4, "little") + bytes([flags]) + author_prefix + message_bytes ) - # Calculate expected ACK (same algorithm as pymc_core) + # Calculate expected ACK (same algorithm as openhop_core) attempt = 0 pack_data = PacketBuilder._pack_timestamp_data(timestamp, attempt, message_bytes) ack_hash = CryptoUtils.sha256(pack_data + client_info.id.get_public_key())[:4] diff --git a/repeater/handler_helpers/text.py b/repeater/handler_helpers/text.py index 2ec327c..d263562 100644 --- a/repeater/handler_helpers/text.py +++ b/repeater/handler_helpers/text.py @@ -1,5 +1,5 @@ """ -Text message (TXT_MSG) handling helper for pyMC Repeater. +Text message (TXT_MSG) handling helper for openHop Repeater. This module processes incoming text messages for all managed identities (repeater identity + identity manager identities). @@ -10,8 +10,8 @@ import asyncio import logging import time -from pymc_core.node.handlers.text import TextMessageHandler -from pymc_core.protocol import CryptoUtils, Identity +from openhop_core.node.handlers.text import TextMessageHandler +from openhop_core.protocol import CryptoUtils, Identity from .mesh_cli import MeshCLI from .room_server import RoomServer @@ -603,8 +603,8 @@ class TextHelper: """ import time - from pymc_core.protocol import PacketBuilder - from pymc_core.protocol.constants import PAYLOAD_TYPE_TXT_MSG + from openhop_core.protocol import PacketBuilder + from openhop_core.protocol.constants import PAYLOAD_TYPE_TXT_MSG try: src_hash = original_packet.payload[1] diff --git a/repeater/handler_helpers/trace.py b/repeater/handler_helpers/trace.py index 80ce715..b2eac52 100644 --- a/repeater/handler_helpers/trace.py +++ b/repeater/handler_helpers/trace.py @@ -1,5 +1,5 @@ """ -Trace packet handling helper for pyMC Repeater. +Trace packet handling helper for openHop Repeater. This module handles the processing and forwarding of trace packets, which are used for network diagnostics to track the path and SNR @@ -11,10 +11,10 @@ import logging import time from typing import Any, Dict, List -from pymc_core.hardware.signal_utils import snr_register_to_db -from pymc_core.node.handlers.trace import TraceHandler -from pymc_core.protocol.constants import MAX_PATH_SIZE, ROUTE_TYPE_DIRECT -from pymc_core.protocol.packet_utils import PathUtils +from openhop_core.hardware.signal_utils import snr_register_to_db +from openhop_core.node.handlers.trace import TraceHandler +from openhop_core.protocol.constants import MAX_PATH_SIZE, ROUTE_TYPE_DIRECT +from openhop_core.protocol.packet_utils import PathUtils logger = logging.getLogger("TraceHelper") diff --git a/repeater/local_cli.py b/repeater/local_cli.py index 59225a6..6b4dc37 100644 --- a/repeater/local_cli.py +++ b/repeater/local_cli.py @@ -1,5 +1,5 @@ """ -CLI client for pyMC Repeater. +CLI client for openHop Repeater. Connects to an already-running repeater daemon via its HTTP API. Reads admin password and HTTP port from the local config.yaml automatically. """ @@ -9,7 +9,7 @@ from typing import Optional from urllib.parse import urlparse CONFIG_PATHS = [ - "/etc/pymc_repeater/config.yaml", + "/etc/openhop_repeater/config.yaml", "config.yaml", ] @@ -79,7 +79,7 @@ def run_client_cli(host: str = "127.0.0.1", port: int = 8000, password: Optional print("Error: Authentication failed. Check password or repeater status.") sys.exit(1) - print(f"\npyMC Repeater CLI (connected to {base_url})") + print(f"\nopenHop Repeater CLI (connected to {base_url})") print("Type 'help' for available commands, 'exit' to quit.\n") while True: @@ -123,7 +123,7 @@ def main(): import argparse parser = argparse.ArgumentParser( - description="Connect to a running pyMC Repeater and issue CLI commands" + description="Connect to a running openHop Repeater and issue CLI commands" ) parser.add_argument( "--config", diff --git a/repeater/main.py b/repeater/main.py index 3297e0b..01ead47 100644 --- a/repeater/main.py +++ b/repeater/main.py @@ -175,8 +175,8 @@ class RepeaterDaemon: self.radio = NullRadio() try: - from pymc_core import LocalIdentity - from pymc_core.node.dispatcher import Dispatcher + from openhop_core import LocalIdentity + from openhop_core.node.dispatcher import Dispatcher self.dispatcher = Dispatcher(self.radio) logger.info("Dispatcher initialized") @@ -307,7 +307,7 @@ class RepeaterDaemon: # Initialize ConfigManager for centralized config management self.config_manager = ConfigManager( - config_path=getattr(self, "config_path", "/etc/pymc_repeater/config.yaml"), + config_path=getattr(self, "config_path", "/etc/openhop_repeater/config.yaml"), config=self.config, daemon_instance=self, ) @@ -395,7 +395,7 @@ class RepeaterDaemon: # Load companion identities (CompanionBridge + frame server per companion) await self._load_companion_identities() - # Subscribe to raw RX in pyMC_core so we can push PUSH_CODE_LOG_RX_DATA to companion clients + # Subscribe to raw RX in openhop-core so we can push PUSH_CODE_LOG_RX_DATA to companion clients self.dispatcher.add_raw_rx_subscriber(self._on_raw_rx_for_companions) n = len(getattr(self, "companion_frame_servers", [])) logger.info( @@ -431,7 +431,7 @@ class RepeaterDaemon: raise async def _load_additional_identities(self): - from pymc_core import LocalIdentity + from openhop_core import LocalIdentity identities_config = self.config.get("identities", {}) @@ -493,8 +493,8 @@ class RepeaterDaemon: async def _load_companion_identities(self) -> None: """Load companion identities from config and create CompanionBridge + frame server for each.""" - from pymc_core import LocalIdentity - from pymc_core.companion.models import Channel + from openhop_core import LocalIdentity + from openhop_core.companion.models import Channel from repeater.companion import CompanionFrameServer, RepeaterCompanionBridge @@ -651,7 +651,7 @@ class RepeaterDaemon: for msg_dict in sqlite_handler.companion_load_messages( companion_hash_str, limit=retention or 100 ): - from pymc_core.companion.models import QueuedMessage + from openhop_core.companion.models import QueuedMessage sk = msg_dict.get("sender_key", b"") if isinstance(sk, str): @@ -717,8 +717,8 @@ class RepeaterDaemon: Creates RepeaterCompanionBridge, CompanionFrameServer, starts the server, and registers with identity_manager. Raises on error. """ - from pymc_core import LocalIdentity - from pymc_core.companion.models import Channel + from openhop_core import LocalIdentity + from openhop_core.companion.models import Channel from repeater.companion import CompanionFrameServer, RepeaterCompanionBridge from repeater.companion.constants import DEFAULT_PUBLIC_CHANNEL_SECRET @@ -837,7 +837,7 @@ class RepeaterDaemon: for msg_dict in sqlite_handler.companion_load_messages( companion_hash_str, limit=retention or 100 ): - from pymc_core.companion.models import QueuedMessage + from openhop_core.companion.models import QueuedMessage sk = msg_dict.get("sender_key", b"") if isinstance(sk, str): @@ -1139,8 +1139,8 @@ class RepeaterDaemon: return False try: - from pymc_core.protocol import PacketBuilder - from pymc_core.protocol.constants import ADVERT_FLAG_HAS_NAME, ADVERT_FLAG_IS_REPEATER + from openhop_core.protocol import PacketBuilder + from openhop_core.protocol.constants import ADVERT_FLAG_HAS_NAME, ADVERT_FLAG_IS_REPEATER # Get node name and location from config repeater_config = self.config.get("repeater", {}) @@ -1332,7 +1332,7 @@ class RepeaterDaemon: radio_type_raw = self.config.get("radio_type") radio_type = "" if radio_type_raw is None else str(radio_type_raw).lower() if radio_type == "sx1262_ch341": - from pymc_core.hardware.ch341.ch341_async import CH341Async + from openhop_core.hardware.ch341.ch341_async import CH341Async CH341Async.reset_instance() except Exception as e: @@ -1402,7 +1402,7 @@ class RepeaterDaemon: config=self.config, event_loop=current_loop, daemon_instance=self, - config_path=getattr(self, "config_path", "/etc/pymc_repeater/config.yaml"), + config_path=getattr(self, "config_path", "/etc/openhop_repeater/config.yaml"), ) try: @@ -1410,7 +1410,7 @@ class RepeaterDaemon: except Exception as e: logger.error(f"Failed to start HTTP server: {e}") - # Run dispatcher (handles RX/TX via pymc_core) + # Run dispatcher (handles RX/TX via openhop_core) try: await self.dispatcher.run_forever() except asyncio.CancelledError: @@ -1441,10 +1441,10 @@ def main(): import argparse - parser = argparse.ArgumentParser(description="pyMC Repeater Daemon") + parser = argparse.ArgumentParser(description="openHop Repeater Daemon") parser.add_argument( "--config", - help="Path to config file (default: /etc/pymc_repeater/config.yaml)", + help="Path to config file (default: /etc/openhop_repeater/config.yaml)", ) parser.add_argument( "--log-level", @@ -1456,7 +1456,7 @@ def main(): # Load configuration config = load_config(args.config) - config_path = args.config if args.config else "/etc/pymc_repeater/config.yaml" + config_path = args.config if args.config else "/etc/openhop_repeater/config.yaml" if args.log_level: if "logging" not in config: diff --git a/repeater/packet_router.py b/repeater/packet_router.py index 13472ca..5cb5c67 100644 --- a/repeater/packet_router.py +++ b/repeater/packet_router.py @@ -2,18 +2,18 @@ import asyncio import logging import time -from pymc_core.node.handlers.ack import AckHandler -from pymc_core.node.handlers.advert import AdvertHandler -from pymc_core.node.handlers.control import ControlHandler -from pymc_core.node.handlers.group_text import GroupTextHandler -from pymc_core.node.handlers.login_response import LoginResponseHandler -from pymc_core.node.handlers.login_server import LoginServerHandler -from pymc_core.node.handlers.path import PathHandler -from pymc_core.node.handlers.protocol_request import ProtocolRequestHandler -from pymc_core.node.handlers.protocol_response import ProtocolResponseHandler -from pymc_core.node.handlers.text import TextMessageHandler -from pymc_core.node.handlers.trace import TraceHandler -from pymc_core.protocol.constants import ( +from openhop_core.node.handlers.ack import AckHandler +from openhop_core.node.handlers.advert import AdvertHandler +from openhop_core.node.handlers.control import ControlHandler +from openhop_core.node.handlers.group_text import GroupTextHandler +from openhop_core.node.handlers.login_response import LoginResponseHandler +from openhop_core.node.handlers.login_server import LoginServerHandler +from openhop_core.node.handlers.path import PathHandler +from openhop_core.node.handlers.protocol_request import ProtocolRequestHandler +from openhop_core.node.handlers.protocol_response import ProtocolResponseHandler +from openhop_core.node.handlers.text import TextMessageHandler +from openhop_core.node.handlers.trace import TraceHandler +from openhop_core.protocol.constants import ( PH_ROUTE_MASK, ROUTE_TYPE_DIRECT, ROUTE_TYPE_TRANSPORT_DIRECT, diff --git a/repeater/policy_engine.py b/repeater/policy_engine.py index a11c485..89e351a 100644 --- a/repeater/policy_engine.py +++ b/repeater/policy_engine.py @@ -5,8 +5,8 @@ import logging from dataclasses import dataclass from typing import Any, Optional -from pymc_core.protocol.constants import PAYLOAD_TYPE_GRP_DATA, PAYLOAD_TYPE_GRP_TXT -from pymc_core.protocol.crypto import CryptoUtils +from openhop_core.protocol.constants import PAYLOAD_TYPE_GRP_DATA, PAYLOAD_TYPE_GRP_TXT +from openhop_core.protocol.crypto import CryptoUtils logger = logging.getLogger("PolicyEngine") diff --git a/repeater/service_utils.py b/repeater/service_utils.py index 2419701..16920da 100644 --- a/repeater/service_utils.py +++ b/repeater/service_utils.py @@ -1,5 +1,5 @@ """ -Service management utilities for pyMC Repeater. +Service management utilities for openHop Repeater. Provides functions for service control operations like restart. """ @@ -12,7 +12,7 @@ import time from typing import Dict, Optional, Tuple logger = logging.getLogger("ServiceUtils") -INIT_SCRIPT = "/etc/init.d/S80pymc-repeater" +INIT_SCRIPT = "/etc/init.d/S80openhop-repeater" BUILDROOT_METADATA_PATH = "/etc/pymc-image-build-id" _CONTAINER_RESTART_DELAY_SECONDS = 1.0 _SH_BIN = shutil.which("sh") or "sh" @@ -91,14 +91,14 @@ def get_container_restart_message() -> str: """Return the user-facing restart message for containerized installs.""" return ( "Container restart initiated. " - "If you are running pyMC Repeater via Docker or Home Assistant, pull or rebuild " + "If you are running openHop Repeater via Docker or Home Assistant, pull or rebuild " "a newer image for packaged image updates to take effect." ) def restart_service() -> Tuple[bool, str]: """ - Restart the pymc-repeater service. + Restart the openhop-repeater service. On Buildroot/Luckfox, use the shipped init script directly. On systemd hosts, try polkit-based restart first (plain systemctl), then @@ -135,7 +135,7 @@ def restart_service() -> Tuple[bool, str]: # Try polkit-based restart first (works on bare metal / VMs with polkit running) try: result = subprocess.run( - [_SYSTEMCTL_BIN, "restart", "pymc-repeater"], + [_SYSTEMCTL_BIN, "restart", "openhop-repeater"], capture_output=True, text=True, timeout=5, @@ -162,10 +162,10 @@ def restart_service() -> Tuple[bool, str]: except Exception as e: logger.warning(f"Polkit restart attempt failed: {e}") - # Fallback: use sudo (requires /etc/sudoers.d/pymc-repeater rule) + # Fallback: use sudo (requires /etc/sudoers.d/openhop-repeater rule) try: result = subprocess.run( - [_SUDO_BIN, "--non-interactive", _SYSTEMCTL_BIN, "restart", "pymc-repeater"], + [_SUDO_BIN, "--non-interactive", _SYSTEMCTL_BIN, "restart", "openhop-repeater"], capture_output=True, text=True, timeout=5, diff --git a/repeater/web/api_endpoints.py b/repeater/web/api_endpoints.py index 39a5185..0ca2149 100644 --- a/repeater/web/api_endpoints.py +++ b/repeater/web/api_endpoints.py @@ -9,7 +9,7 @@ from typing import Callable, Optional import cherrypy import yaml -from pymc_core.protocol import CryptoUtils +from openhop_core.protocol import CryptoUtils from repeater import __version__ from repeater.companion.identity_resolve import ( @@ -198,7 +198,7 @@ class APIEndpoints: self.config = config or {} self.event_loop = event_loop self.daemon_instance = daemon_instance - self._config_path = config_path or "/etc/pymc_repeater/config.yaml" + self._config_path = config_path or "/etc/openhop_repeater/config.yaml" self.cad_calibration = CADCalibrationEngine(daemon_instance, event_loop) # Initialize ConfigManager for centralized config management @@ -1427,9 +1427,9 @@ class APIEndpoints: stats["site_name"] = self.config.get("web", {}).get("site_name", "") stats["version"] = __version__ try: - import pymc_core + import openhop_core - stats["core_version"] = pymc_core.__version__ + stats["core_version"] = openhop_core.__version__ except ImportError: stats["core_version"] = "unknown" image_info = get_buildroot_image_info() @@ -2162,7 +2162,7 @@ class APIEndpoints: @cherrypy.tools.json_out() @cherrypy.tools.json_in() def restart_service(self): - """Restart the pymc-repeater service via systemctl.""" + """Restart the openhop-repeater service via systemctl.""" # Enable CORS for this endpoint only if configured self._set_cors_headers() @@ -3819,7 +3819,7 @@ class APIEndpoints: self.config["mesh"]["unscoped_flood_allow"] = unscoped_flood_allow # Get the actual config path from daemon instance (same as CAD settings) - config_path = getattr(self, "_config_path", "/etc/pymc_repeater/config.yaml") + config_path = getattr(self, "_config_path", "/etc/openhop_repeater/config.yaml") if self.daemon_instance and hasattr(self.daemon_instance, "config_path"): config_path = self.daemon_instance.config_path @@ -3932,7 +3932,7 @@ class APIEndpoints: trace_tag = secrets.randbits(32) # Create trace packet - from pymc_core.protocol import PacketBuilder + from openhop_core.protocol import PacketBuilder path_bytes = list(target_hash.to_bytes(byte_count, "big")) packet = PacketBuilder.create_trace( @@ -4349,7 +4349,7 @@ class APIEndpoints: companion_activation_error = None if identity_type == "room_server" and self.daemon_instance: try: - from pymc_core import LocalIdentity + from openhop_core import LocalIdentity # Create LocalIdentity from the key (convert hex string to bytes) if isinstance(identity_key, bytes): @@ -4673,7 +4673,7 @@ class APIEndpoints: if needs_reload and self.daemon_instance: try: - from pymc_core import LocalIdentity + from openhop_core import LocalIdentity final_name = identity["name"] # Could be new_name identity_key = identity["identity_key"] @@ -4961,8 +4961,8 @@ class APIEndpoints: ): """Send advert for a room server identity""" try: - from pymc_core.protocol import PacketBuilder - from pymc_core.protocol.constants import ( + from openhop_core.protocol import PacketBuilder + from openhop_core.protocol.constants import ( ADVERT_FLAG_HAS_NAME, ADVERT_FLAG_IS_ROOM_SERVER, ) @@ -6588,7 +6588,7 @@ class APIEndpoints: - pyMC Repeater API Documentation + openHop Repeater API Documentation