From 33bc1623f874cf76cbe95cd78f8657f3c372d189 Mon Sep 17 00:00:00 2001 From: Carl Chalmers Date: Tue, 28 Jul 2026 18:37:04 +0100 Subject: [PATCH 1/2] setup: auto-detect FTDI + Victron serial devices on the target Pi The repo bakes in the FTDI serial from the reference deployment (usb-FTDI_FT231X_USB_UART_DU0D4EKZ-if00-port0) and a generic Victron name (usb-VictronEnergy_VE.Direct_cable-if00-port0). Every FTDI adapter has a unique serial burned into its EEPROM, and some Victron cables (e.g. the "BV" variant) enumerate under a different by-id name entirely. On any Pi whose hardware doesn't match those two strings verbatim, `docker-compose up` fails with "no such file or directory" and rest_client logs recurring "VE.Direct port not open" warnings. Add detect_and_patch_local_devices() to sparrow_setup.sh: after onboarding and before the container build, scan /dev/serial/by-id/ for the local FTDI adapter and patch docker-compose.yml, then scan for the local Victron cable and patch VE_DIRECT_PORT in both sparrow.env and starlink.env. Falls back gracefully when either cable is absent. --- setup script/sparrow_setup.sh | 63 +++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/setup script/sparrow_setup.sh b/setup script/sparrow_setup.sh index 2cff66a..9163347 100644 --- a/setup script/sparrow_setup.sh +++ b/setup script/sparrow_setup.sh @@ -481,6 +481,67 @@ create_additional_directories() { mkdir -p "$SYSTEM_FOLDER/starlink"/{logs,config} } +# Patch docker-compose.yml + env files so their by-id serial paths match the +# hardware that's actually plugged into THIS Pi. The repo bakes in a specific +# FTDI serial (from the reference deployment) and a generic Victron VE.Direct +# name, but every FTDI adapter has a unique serial and different Victron cable +# variants (e.g. "BV") enumerate under different names. Without this step, +# `docker-compose up` fails on any Pi whose hardware doesn't match those two +# strings verbatim. +detect_and_patch_local_devices() { + local compose_file="$SYSTEM_FOLDER/docker-compose.yml" + local sparrow_env="$SYSTEM_FOLDER/sparrow.env" + local starlink_env="$SYSTEM_FOLDER/starlink.env" + + # ---- FTDI (XBee) — patch docker-compose.yml -------------------------- + local actual_ftdi="" + if [[ -d /dev/serial/by-id ]]; then + actual_ftdi=$(find /dev/serial/by-id -maxdepth 1 -iname "usb-FTDI_*-if00-port0" -print -quit 2>/dev/null || true) + fi + + if [[ -n "$actual_ftdi" ]]; then + if [[ -f "$compose_file" ]] && ! grep -q "$actual_ftdi" "$compose_file"; then + log "Patching docker-compose.yml FTDI path -> $actual_ftdi" + sed -i.bak -E "s|/dev/serial/by-id/usb-FTDI_[^:[:space:]]+|$actual_ftdi|g" "$compose_file" + else + log "docker-compose.yml already references local FTDI adapter" + fi + else + log "WARN: no FTDI adapter found; commenting out the XBee device mapping so containers can still start." + [[ -f "$compose_file" ]] && sed -i.bak -E '/^\s*-\s*\/dev\/serial\/by-id\/usb-FTDI_.*:\/dev\/xbee_serial\s*$/s/^(\s*)-/\1# -/' "$compose_file" + fi + + # ---- Victron VE.Direct — patch sparrow.env + starlink.env ------------ + local victron_dev="" + if [[ -d /dev/serial/by-id ]]; then + victron_dev=$(find /dev/serial/by-id -maxdepth 1 -iname "usb-VictronEnergy_*-if00-port0" -print -quit 2>/dev/null || true) + fi + + _patch_ve_direct_in_env() { + local envfile="$1" wanted="$2" + [[ -f "$envfile" ]] || return 0 + local current + current=$(grep -E '^VE_DIRECT_PORT=' "$envfile" | tail -1 | cut -d= -f2- || true) + if [[ "$current" == "$wanted" ]]; then + log "$envfile: VE_DIRECT_PORT already matches" + elif grep -qE '^VE_DIRECT_PORT=' "$envfile"; then + log "Patching $envfile: VE_DIRECT_PORT -> $wanted" + sed -i.bak -E "s|^VE_DIRECT_PORT=.*|VE_DIRECT_PORT=$wanted|" "$envfile" + else + log "Appending VE_DIRECT_PORT=$wanted to $envfile" + echo "VE_DIRECT_PORT=$wanted" >>"$envfile" + fi + } + + if [[ -n "$victron_dev" ]]; then + log "Found local Victron VE.Direct cable: $victron_dev" + _patch_ve_direct_in_env "$sparrow_env" "$victron_dev" + _patch_ve_direct_in_env "$starlink_env" "$victron_dev" + else + log "No Victron VE.Direct cable found; leaving VE_DIRECT_PORT alone (harmless 'port not open' log until a cable is plugged in)." + fi +} + create_folders() { local uh uh=$(eval echo ~"${SUDO_USER:-$USER}") @@ -717,6 +778,8 @@ install_smbus2 configure_access_key onboard_device || log "Onboarding did not complete successfully; continuing setup." +detect_and_patch_local_devices + log "Building Sparrow containers..." cd "$SYSTEM_FOLDER" From 925781dc5c8dfec2921a86c54335179cb5a07a03 Mon Sep 17 00:00:00 2001 From: Carl Chalmers Date: Tue, 28 Jul 2026 18:59:10 +0100 Subject: [PATCH 2/2] =?UTF-8?q?setup:=20address=20PR=20#74=20Copilot=20rev?= =?UTF-8?q?iew=20=E2=80=94=20idempotent=20FTDI=20un-comment,=20reuse=20set?= =?UTF-8?q?=5Fdotenv=5Fkv=20for=20VE=5FDIRECT=5FPORT?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup script/sparrow_setup.sh | 41 +++++++++++++++-------------------- 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/setup script/sparrow_setup.sh b/setup script/sparrow_setup.sh index 9163347..7878130 100644 --- a/setup script/sparrow_setup.sh +++ b/setup script/sparrow_setup.sh @@ -500,15 +500,24 @@ detect_and_patch_local_devices() { fi if [[ -n "$actual_ftdi" ]]; then - if [[ -f "$compose_file" ]] && ! grep -q "$actual_ftdi" "$compose_file"; then - log "Patching docker-compose.yml FTDI path -> $actual_ftdi" - sed -i.bak -E "s|/dev/serial/by-id/usb-FTDI_[^:[:space:]]+|$actual_ftdi|g" "$compose_file" - else - log "docker-compose.yml already references local FTDI adapter" + if [[ -f "$compose_file" ]]; then + # If a previous run (with no FTDI plugged in) commented out the xbee_serial + # mapping, re-enable it now that an adapter is present. Idempotent: no-op if + # the line is already uncommented. + if grep -qE '^[[:space:]]*#[[:space:]]*-[[:space:]]*/dev/serial/by-id/usb-FTDI_.*:/dev/xbee_serial[[:space:]]*$' "$compose_file"; then + log "Re-enabling previously-disabled XBee device mapping" + sed -i.bak -E 's|^([[:space:]]*)#[[:space:]]*-[[:space:]]*(/dev/serial/by-id/usb-FTDI_[^:[:space:]]+:/dev/xbee_serial)[[:space:]]*$|\1- \2|' "$compose_file" + fi + if ! grep -q "$actual_ftdi" "$compose_file"; then + log "Patching docker-compose.yml FTDI path -> $actual_ftdi" + sed -i.bak -E "s|/dev/serial/by-id/usb-FTDI_[^:[:space:]]+|$actual_ftdi|g" "$compose_file" + else + log "docker-compose.yml already references local FTDI adapter" + fi fi else log "WARN: no FTDI adapter found; commenting out the XBee device mapping so containers can still start." - [[ -f "$compose_file" ]] && sed -i.bak -E '/^\s*-\s*\/dev\/serial\/by-id\/usb-FTDI_.*:\/dev\/xbee_serial\s*$/s/^(\s*)-/\1# -/' "$compose_file" + [[ -f "$compose_file" ]] && sed -i.bak -E '/^[[:space:]]*-[[:space:]]*\/dev\/serial\/by-id\/usb-FTDI_.*:\/dev\/xbee_serial[[:space:]]*$/s/^([[:space:]]*)-/\1# -/' "$compose_file" fi # ---- Victron VE.Direct — patch sparrow.env + starlink.env ------------ @@ -517,26 +526,10 @@ detect_and_patch_local_devices() { victron_dev=$(find /dev/serial/by-id -maxdepth 1 -iname "usb-VictronEnergy_*-if00-port0" -print -quit 2>/dev/null || true) fi - _patch_ve_direct_in_env() { - local envfile="$1" wanted="$2" - [[ -f "$envfile" ]] || return 0 - local current - current=$(grep -E '^VE_DIRECT_PORT=' "$envfile" | tail -1 | cut -d= -f2- || true) - if [[ "$current" == "$wanted" ]]; then - log "$envfile: VE_DIRECT_PORT already matches" - elif grep -qE '^VE_DIRECT_PORT=' "$envfile"; then - log "Patching $envfile: VE_DIRECT_PORT -> $wanted" - sed -i.bak -E "s|^VE_DIRECT_PORT=.*|VE_DIRECT_PORT=$wanted|" "$envfile" - else - log "Appending VE_DIRECT_PORT=$wanted to $envfile" - echo "VE_DIRECT_PORT=$wanted" >>"$envfile" - fi - } - if [[ -n "$victron_dev" ]]; then log "Found local Victron VE.Direct cable: $victron_dev" - _patch_ve_direct_in_env "$sparrow_env" "$victron_dev" - _patch_ve_direct_in_env "$starlink_env" "$victron_dev" + [[ -f "$sparrow_env" ]] && set_dotenv_kv "$sparrow_env" VE_DIRECT_PORT "$victron_dev" + [[ -f "$starlink_env" ]] && set_dotenv_kv "$starlink_env" VE_DIRECT_PORT "$victron_dev" else log "No Victron VE.Direct cable found; leaving VE_DIRECT_PORT alone (harmless 'port not open' log until a cable is plugged in)." fi