From e91e305950e0402da8fff28e360a605ebb2fc649 Mon Sep 17 00:00:00 2001 From: Luc Busquin <133058544+Cybis320@users.noreply.github.com> Date: Tue, 9 Jun 2026 22:48:32 -0700 Subject: [PATCH 01/17] Offer conversion to MultiCam data structure during RMS_FirstRun RMS_FirstRun now offers a double-confirmed step 5 that converts new installs to the Stations data structure by chaining to add_Pi_Station.sh. add_Pi_Station.sh changes: - Support single-camera platforms (RPi4 and older) with MaxStation=1 instead of refusing to run; skip multi-camera-only tweaks (wayfire cascade, reboot disable, extra_space scaling) on those systems - Migrate camera_settings.json to the station folder and point camera_settings_path at it - Move all existing captured data (including hidden files) with find instead of a hardcoded list, avoiding the mv-into-itself error - Restore pristine defaults via git checkout with a wget fallback so an offline run cannot delete .config without a replacement - Add an Edit_configs desktop helper for editing station configs RMS_StartCapture_MCP.sh starts a single-camera system with a 2 s delay instead of 70 s; the long delay is only needed to serialize first-start updates across multiple stations. Autorun is now enabled only after the optional conversion finishes so an interrupted conversion is not skipped over by the autorun path on the next boot. --- .../MultiCamLinux/Pi/RMS_StartCapture_MCP.sh | 10 +- Scripts/MultiCamLinux/Pi/add_Pi_Station.sh | 181 ++++++++++++------ Scripts/RMS_FirstRun.sh | 70 ++++++- 3 files changed, 199 insertions(+), 62 deletions(-) diff --git a/Scripts/MultiCamLinux/Pi/RMS_StartCapture_MCP.sh b/Scripts/MultiCamLinux/Pi/RMS_StartCapture_MCP.sh index 1911bbb25..1afc9fbca 100755 --- a/Scripts/MultiCamLinux/Pi/RMS_StartCapture_MCP.sh +++ b/Scripts/MultiCamLinux/Pi/RMS_StartCapture_MCP.sh @@ -1,6 +1,14 @@ #!/bin/bash -seconds=70 +# On a single camera system there is no risk of concurrent first-start updates, +# so the long delay is only needed when more than one station is configured +dircount=$(find ~/source/Stations -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l) +if [[ $dircount -le 1 ]]; then + seconds=2 +else + seconds=70 +fi + echo " Starting all configured stations post-update..." loop=0 diff --git a/Scripts/MultiCamLinux/Pi/add_Pi_Station.sh b/Scripts/MultiCamLinux/Pi/add_Pi_Station.sh index 40eeb88fd..1cb8a3df7 100755 --- a/Scripts/MultiCamLinux/Pi/add_Pi_Station.sh +++ b/Scripts/MultiCamLinux/Pi/add_Pi_Station.sh @@ -1,20 +1,24 @@ #!/bin/bash # This software is part of the Linux port of RMS # Copyright (C) 2023 Ed Harman -# +# # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program. If not, see . # +# Version 1.9 - support single camera platforms (RPi4 and older) so RMS_FirstRun +# can offer migration to the Stations data structure on all systems, +# also migrate camera_settings.json and all captured data +# # Version 1.8 - replaced Desktop link with one that starts all captures # # Version 1.7 - changed desktop launcher display names and added conkyrc1 mod @@ -45,11 +49,21 @@ then Model=$(lscpu| grep -o 'Cortex.*'|grep -o '[0-9]*') if [[ $Model -lt 76 ]] # RPi5 uses Cortex A76 - use 72 for script testing on an RPi4 then - echo "This platform will not support multiple cameras, an RPi5 or similar is required" - exit + echo "This platform only supports 1 camera, an RPi5 or similar is required for multiple cameras" + MaxStation=1 + # quit if a camera is already configured under ~/source/Stations + if [[ -d ~/source/Stations ]] + then + dircount=$(find ~/source/Stations -mindepth 1 -maxdepth 1 -type d | wc -l) + if [[ $dircount -gt 0 ]] + then + read -n1 -r -p 'You are already running a camera on this platform, press ENTER to quit now' key + exit + fi + fi fi - DefStation=$(awk ' /stationID:/ {print toupper($2)}' ~/source/RMS/.config) # force uppercase + DefStation=$(awk ' /stationID:/ {print toupper($2)}' ~/source/RMS/.config) # force uppercase if [[ $DefStation == XX0001 && ! -d ~/source/Stations/ ]] then echo "Please run RMS_Firstrun and configure your 1st station" @@ -61,28 +75,26 @@ then cat < - -It appears you have already configured your first station - id ${DefStation} -so its config files will now be relocated to - +You have already configured your first station as id ${DefStation} +Its camera specific files will now be relocated to ~/source/Stations/${DefStation} -Captured data from your default station are stored by default in +Any previously captured data will be moved from ~/RMS_data - -This data will be moved to - +to ~/RMS_data/${DefStation} EOF - # First station has been configured, so move it to ~/source/Stations/, - # and move any captured data to ~/RMS_data/. + # First station has been configured, so move it to ~/source/Stations/, + # and move any captured data to ~/RMS_data/. # tweak conky station title sed -i 's/\(source\)\/RMS/\1\/Stations\/'"$DefStation"'/g' /home/rms/.conkyrc1 @@ -93,7 +105,7 @@ EOF mkdir ~/source/Stations/${DefStation} cp ~/source/RMS/.config ~/source/Stations/${DefStation} cd ~/source/RMS - sed -i "s,data_dir.*$,data_dir: ~/RMS_data/${DefStation},g" ~/source/Stations/${DefStation}/.config + sed -i "s,^data_dir.*$,data_dir: ~/RMS_data/${DefStation},g" ~/source/Stations/${DefStation}/.config if [[ -e ~/source/RMS/platepar_cmn2010.cal ]] then mv ~/source/RMS/platepar_cmn2010.cal ~/source/Stations/${DefStation}/ @@ -101,15 +113,26 @@ EOF if [[ -e ~/source/RMS/mask.bmp ]] then mv ~/source/RMS/mask.bmp ~/source/Stations/${DefStation}/ - #replace with copy from master - cd ~/source/RMS - wget -q https://raw.githubusercontent.com/CroatianMeteorNetwork/RMS/master/mask.bmp fi - mkdir ~/RMS_data/${DefStation} - cd ~/RMS_data/${DefStation} - mv ../ArchivedFiles . - mv ../CapturedFiles . - mv ../logs . + if [[ -e ~/source/RMS/camera_settings.json ]] + then + mv ~/source/RMS/camera_settings.json ~/source/Stations/${DefStation}/ + # point the station .config at the relocated camera settings file + sed -i "s,^camera_settings_path:.*$,camera_settings_path: ~/source/Stations/${DefStation}/camera_settings.json,g" ~/source/Stations/${DefStation}/.config + fi + + # restore pristine default copies in ~/source/RMS, these are used as + # templates when additional stations are added + cd ~/source/RMS + for file in mask.bmp camera_settings.json + do + git checkout -- "$file" 2>/dev/null || \ + wget -q -O "$file" "https://raw.githubusercontent.com/CroatianMeteorNetwork/RMS/master/$file" + done + + # move any existing captured data into the station folder + mkdir -p ~/RMS_data/${DefStation} + find ~/RMS_data -mindepth 1 -maxdepth 1 ! -name "${DefStation}" -exec mv -t ~/RMS_data/${DefStation}/ {} + cat <<- EOF > ~/Desktop/${DefStation}_StartCapture.desktop [Desktop Entry] @@ -139,21 +162,25 @@ EOF Desktop=`xdg-user-dir DESKTOP` fi - # cleanup erroneous Desktop shortcuts + # cleanup unneeded Desktop shortcuts if [[ -f ~/Desktop/RMS_FirstRun.sh ]] then - rm ~/Desktop/CMNbinViewer.sh ~/Desktop/RMS_ShowLiveStream.sh ~/Desktop/RMS_StartCapture.sh - rm ~/Desktop/RMS_config.txt ~/Desktop/TunnelIPCamera.sh ~/Desktop/DownloadOpenVPNconfig.sh + rm -f ~/Desktop/CMNbinViewer.sh ~/Desktop/RMS_ShowLiveStream.sh + rm -f ~/Desktop/RMS_StartCapture.sh ~/Desktop/RMS_config.txt + rm -f ~/Desktop/TunnelIPCamera.sh ~/Desktop/DownloadOpenVPNconfig.sh + fi + + if [[ $MaxStation != 1 ]] + then + # remove comment from last line of wayfire.ini to enable window cascade + sed -i s/#mode/mode/ ~/.config/wayfire.ini fi - - # remove comment from last line of wayfire.ini to enable window cascade - sed -i s/#mode/mode/ ~/.config/wayfire.ini fi # if [[ ! -d ~/source/Stations/ ]] fi # if [[ $(uname -m ) == aarch64 ]] -# need to count the number of directories under ~/source/Stations and only +# need to count the number of directories under ~/source/Stations and only # prompt for adding the correct number of new cameras, so subtract that number # from MaxStation defined above @@ -168,7 +195,7 @@ do if [[ ${#Station[@]} == $Remaining ]] then echo "" - echo "Done, This platform has a maximum of $numdirs cameras" + echo "Done, this platform supports a maximum of $MaxStation camera(s)" break fi read -p "Enter station ID, to end: " this_Station @@ -181,13 +208,16 @@ do # echo $Station done -echo -e "\nNew stations to add -" -printf '%s\n' "${Station[@]}" +if [[ $MaxStation != 1 ]] +then + echo -e "\nNew stations to add -" + printf '%s\n' "${Station[@]}" +fi -# check if ~/${RMS_data} exists and if not create it.... -if [[ ! -d "${RMS_data}/" ]] +# check if ~/RMS_data exists and if not create it.... +if [[ ! -d ~/RMS_data ]] then - mkdir ${RMS_data} + mkdir ~/RMS_data fi No_Stations=${#Station[@]} @@ -199,8 +229,8 @@ do exit else echo "making dir Stations/${item}" - mkdir ~/source/Stations/${item} - if [[ ! -d ${RMS_data}/$item ]] + mkdir ~/source/Stations/${item} + if [[ ! -d ~/RMS_data/${item} ]] then mkdir ~/RMS_data/${item} fi @@ -239,12 +269,15 @@ do # customise each .config # set the station_id - sed -i "s/D:.*$/D: $item/g" ~/source/Stations/${item}/.config - # set the ${RMS_data} dir - sed -i "s,data_dir.*$,data_dir: ~/RMS_data/${item},g" ~/source/Stations/${item}/.config + sed -i "s/D:.*$/D: $item/g" ~/source/Stations/${item}/.config + # set the data_dir + sed -i "s,^data_dir.*$,data_dir: ~/RMS_data/${item},g" ~/source/Stations/${item}/.config echo -e "\n\nAdded station $item\n\n" - # disable daily post processing reboot - sed -i "s/\(reboot_after_processing:\).*/\1 false/g" ~/source/Stations/${item}/.config + if [[ $MaxStation != 1 ]] + then + # disable daily post processing reboot + sed -i "s/\(reboot_after_processing:\).*/\1 false/g" ~/source/Stations/${item}/.config + fi done # check if keys are already present @@ -268,20 +301,47 @@ then fi echo -e "\n\nStation configuration complete\n\n" -user=`id -u -n` # Set up shortcut for CMNbinViewer cat <<- EOF > ~/Desktop/CMNbinViewer.desktop [Desktop Entry] Name=CMNbinViewer Type=Application -Exec=/home/${user}/source/RMS/Scripts/CMNbinViewer_env.sh +Exec=${HOME}/source/RMS/Scripts/CMNbinViewer_env.sh Hidden=false NoDisplay=false -Icon=/home/${user}/source/RMS/Scripts/MultiCamLinux/icon.png +Icon=${HOME}/source/RMS/Scripts/MultiCamLinux/icon.png EOF chmod +x ~/Desktop/CMNbinViewer.desktop +# Create Desktop shortcut for editing the station .config files +cat << 'EOF' > ~/Desktop/Edit_configs +#!/bin/bash + +echo "" +echo "One by one, each config file under ~/source/Stations will open for editing..." +read -n1 -r -p 'Press ENTER to proceed, any other key to quit: ' key +if [[ "${key}" != "" ]]; then + exit +fi + +for Dir in ~/source/Stations/*/ +do + Station=$(basename "${Dir}") + echo "" + echo "Editing .config file for ${Station}" + read -n1 -r -p 'Press ENTER to edit, any other key to skip: ' key + if [[ "${key}" = "" ]]; then + mousepad ~/source/Stations/${Station}/.config + fi +done + +echo "" +echo "Done editing .config files" +sleep 2 +EOF +chmod +x ~/Desktop/Edit_configs + # set the option to allow launching of desktop shortcuts #sed -i s/quick_exec=0/quick_exec=1/ ~/.config/libfm/libfm.conf @@ -290,18 +350,21 @@ then sudo timedatectl set-timezone UTC fi -# set the extra_space for all configured stations - -mult=$(ls -1 /home/${USER}/source/Stations/|wc -l) -for Dir in /home/${USER}/source/Stations/* - do - sed -i "s/extra_space_gb: .*$/extra_space_gb: $(( ${mult} * 30 ))/g" ${Dir}/.config -done +if [[ $MaxStation != 1 ]] +then + # set the extra_space for all configured stations - + mult=$(ls -1 /home/${USER}/source/Stations/|wc -l) + for Dir in /home/${USER}/source/Stations/* + do + sed -i "s/extra_space_gb: .*$/extra_space_gb: $(( ${mult} * 30 ))/g" ${Dir}/.config + done +fi # set up so RMS_FirstRun can autostart all captures rm -f /home/${USER}/Desktop/RMS_StartCapture.sh ln -s /home/${USER}/source/RMS/Scripts/MultiCamLinux/Pi/RMS_StartCapture_MCP.sh /home/${USER}/Desktop/RMS_StartCapture.sh -# get a copy of the default .config +# replace the potentially modified .config in ~/source/RMS with a pristine default cd ~/source/RMS -rm .config #delete potentially modified .config and replace with new.. -wget -q https://raw.githubusercontent.com/CroatianMeteorNetwork/RMS/master/.config +git checkout -- .config 2>/dev/null || \ + wget -q -O .config https://raw.githubusercontent.com/CroatianMeteorNetwork/RMS/master/.config diff --git a/Scripts/RMS_FirstRun.sh b/Scripts/RMS_FirstRun.sh index 7787ab169..56f076f71 100755 --- a/Scripts/RMS_FirstRun.sh +++ b/Scripts/RMS_FirstRun.sh @@ -117,7 +117,9 @@ echo " 1. Connect your Pi to the Internet. " echo " 2. Change the default password for security reasons." echo " 3. Generate a new SSH key." echo " 4. Edit the RMS config file. " - +echo " 5. Convert to the multi-camera data structure (recommended)." +echo "" +echo "At the end of these steps the first data capture session will start." echo "" echo " @@ -249,9 +251,73 @@ else fi done -# Enable autorun +# Offer the conversion to the multi-camera data structure. The conversion +# script only supports 64-bit Pi platforms, so skip this step elsewhere. +if [[ $(uname -m) == aarch64 ]]; then + +echo " +5) Converting the data structure +-------------------------------- +The new recommended data structure keeps important camera files in a safer +place. Converting to this multi-camera data structure is recommended for +both single and multiple camera systems. + +The important files (.config, mask.bmp, platepar_cmn2010.cal and +camera_settings.json) will be moved to ~/source/Stations/, +and captured data will be stored in ~/RMS_data/, where + is the unique station code you were given by GMN. +" +sleep 1 + +# clear the input buffer +while read -t 0.01; do :; done + +read -n1 -r -p 'Press ENTER to convert to the new data structure, or Q to stay with the legacy structure... ' key +if [[ "$key" != "" ]]; then + echo "" + echo "" + echo "Are you sure you want to stay with the legacy structure?" + read -n1 -r -p 'Press Q again to confirm, or ENTER to convert to the new data structure... ' key + echo "" +fi + +if [[ "$key" = "" ]]; then + echo "" + echo "Next, this script will run to convert to the recommended data structure:" + echo " ~/source/RMS/Scripts/MultiCamLinux/Pi/add_Pi_Station.sh" + echo "" + echo "Note: if you have a Pi5 or similar, you can run the same script later to add more cameras." + echo "" + sleep 2 + read -n1 -r -p 'Press ENTER a second time to confirm the conversion, or Q to keep the legacy structure... ' key +fi +echo "" + +if [[ "$key" = "" ]]; then + echo "" + echo "Converting to the recommended data structure..." + echo "" + bash ~/source/RMS/Scripts/MultiCamLinux/Pi/add_Pi_Station.sh +else + echo "" + echo "You have chosen to keep the legacy data structure." + echo "If you decide to convert in the future, run this command from a terminal:" + echo " ~/source/RMS/Scripts/MultiCamLinux/Pi/add_Pi_Station.sh" +fi + +fi # if aarch64 + +# Enable autorun. This is done only after the optional data structure +# conversion so an interrupted conversion does not get skipped over by +# the autorun path on the next boot. echo "1" > $RMSAUTORUNFILE +sleep 2 +echo "" +echo "Configuration is done" +echo "" +read -n1 -r -p 'Press ENTER when you are ready to start camera data capture... ' +echo "" # If the configuration was done, run recording bash $RMSSTARTCAPTURE From 3bfa5837f896f1d396638173f083bd5271064bca Mon Sep 17 00:00:00 2001 From: Luc Busquin <133058544+Cybis320@users.noreply.github.com> Date: Tue, 9 Jun 2026 23:00:53 -0700 Subject: [PATCH 02/17] Support CSV-driven station creation in add_GStation.sh Stations can now be created in bulk from a stations.csv file. The header row names the columns: station_id (required), camera_ip (spliced into the rtsp device URL), and any other .config key, which is applied verbatim to the station config. Usage: add_GStation.sh [RMS_data_path] [stations.csv] With no arguments the script uses ~/source/RMS/stations.csv if present (the file is gitignored), otherwise it falls back to the previous interactive prompts, so the GRMSL_Install.sh flow keeps working. Desktop and autostart entries now use gnome-terminal (an optional StartCapture profile customises the windows) instead of installing and configuring lxterminal. Robustness fixes over the old version: config seds are anchored at line start so commented examples are not rewritten, CSV fields are trimmed without xargs (handles CRLF endings and quotes in values), a final CSV row without a trailing newline is not dropped, and the CMNbinViewer icon points at the RMS copy instead of a cmn_binviewer path that only exists via an install-time symlink. The script is also slimmed down to station creation: the GNOME sleep/screen-lock prevention moved to RMS_Installer.sh as one-time machine setup (guarded for headless installs), the duplicated UTC timezone setting is dropped (RMS_Installer.sh already does it), and the interactive optional installs (pcmanfm, mousepad, AnyDesk), cron-based update scheduling, and AnyDesk Wayland workaround are removed. --- .gitignore | 4 +- Scripts/MultiCamLinux/add_GStation.sh | 501 ++++++++++++-------------- Scripts/RMS_Installer.sh | 8 + 3 files changed, 238 insertions(+), 275 deletions(-) diff --git a/.gitignore b/.gitignore index a20bf3960..a1710c8d2 100644 --- a/.gitignore +++ b/.gitignore @@ -115,4 +115,6 @@ Catalogs/GMN_StarCatalog_LM12.0.bin # Agents .agents/ -skills-lock.json \ No newline at end of file +skills-lock.json +# Station list for Scripts/MultiCamLinux/add_GStation.sh +stations.csv diff --git a/Scripts/MultiCamLinux/add_GStation.sh b/Scripts/MultiCamLinux/add_GStation.sh index 4b26275a6..607b346e8 100755 --- a/Scripts/MultiCamLinux/add_GStation.sh +++ b/Scripts/MultiCamLinux/add_GStation.sh @@ -1,20 +1,24 @@ #!/bin/bash # This software is part of the Linux port of RMS # Copyright (C) 2023 Ed Harman -# +# # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program. If not, see . # +# Version 1.7 - stations can be created in bulk from a stations.csv file, +# desktop entries use gnome-terminal instead of lxterminal, +# interactive prompts remain the fallback when no CSV is given +# # Version 1.6 - added logic to prevent running this script on a Raspberry Pi # added by Peter E., June 2024 # @@ -30,311 +34,260 @@ # Version 1.1 # Changes - added station arguments to Launch scripts # - changed desktop links for StartCapture to symbolic links of the scripts within .config/autostart -# -# -# Prevent running this script on a Raspberry Pi, because -# add_Pi_Station.sh should be used to add cameras on a Pi. +# + +# Prevent running this script on a Raspberry Pi file=/sys/firmware/devicetree/base/model if [[ -f "$file" ]]; then - echo "" contents=$(tr -d '\0' < $file) - echo "The file $file reads: $contents" if [[ $contents == 'Raspberry'* ]]; then - echo "The add_GStation.sh script should not be used on Raspberry Pi." - echo "Please use add_Pi_Station.sh to add cameras on a Pi5." - echo "" - exit 1 + echo "The add_GStation.sh script should not be used on Raspberry Pi." + echo "Please use add_Pi_Station.sh to add cameras on a Pi5." + exit 1 fi fi -# Check if the user's desktop directory environment variable is set -if [ -n "$xdg-user-dir DESKTOP" ]; then - Desktop=`xdg-user-dir DESKTOP` -fi -# -if [[ "$#" -eq 0 ]] #called with no arg -then - RMS_data=~/RMS_data - else - RMS_data=${1} -fi -# cleanup erroneous Desktop shortcuts -if [[ -f ~/Desktop/RMS_FirstRun.sh ]] - then - rm ~/Desktop/RMS_FirstRun.sh ~/Desktop/RMS_StartCapture.sh ~/Desktop/RMS_ShowLiveStream.sh ~/Desktop/TunnelIPCamera.sh ~/Desktop/CMNbinViewer.sh -fi +# Get user's desktop directory +Desktop=$(xdg-user-dir DESKTOP 2>/dev/null || echo "$HOME/Desktop") -# Install the Buster RMS default terminal - lxterminal -if ! command -v lxterminal &> /dev/null -then - sudo apt-get install -y lxterminal & - wait %1 - echo -e "\n\nlaunching lxterminal\n\n" -# we need to launch lxterminal at least once for it to write its default preferences file - echo -e "\n\nclosed lxterminal\n\n" - # now lxterminal is installed and written it's config we can customise it - sed -i "s/scrollback=.*/scrollback=10000/g" ~/.config/lxterminal/lxterminal.conf - sed -i "s/geometry_columns=.*/geometry_columns=120/g" ~/.config/lxterminal/lxterminal.conf - sed -i "s/geometry_rows=.*/geometry_rows=25/g" ~/.config/lxterminal/lxterminal.conf - echo -e "\n\nlxterminal installed and preferences set\n\n" -fi -if [[ ! -d ~/source/Stations ]] - then - mkdir ~/source/Stations -fi -declare Station -while : -do - read -p "Enter station ID, to end: " this_Station - if [[ -z $this_Station ]] - then - break - fi - Station+=("$this_Station") -# echo $Station -done +# Strip leading/trailing whitespace (including CR from Windows-edited CSVs) +trim() { + local s="$*" + s="${s#"${s%%[![:space:]]*}"}" + s="${s%"${s##*[![:space:]]}"}" + printf '%s' "$s" +} -echo -e "\nNew stations to add -" -printf '%s\n' "${Station[@]}" +# Create a station directory with config, mask and desktop entries. +# Returns 1 if the station already exists. +create_station() { + local item="$1" + local config_file=~/source/Stations/${item}/.config -# check if ~/${RMS_data} exists and if not create it.... -if [[ ! -d "${RMS_data}/" ]] - then - mkdir ${RMS_data} -fi + if [[ -d ~/source/Stations/${item} ]]; then + echo -e "\nNot creating station ${item} - it already exists" + return 1 + fi -# check if user has an autostart dir in their .config directory and if not create it... -if [[ ! -d ~/.config/autostart ]] - then - mkdir ~/.config/autostart -fi + echo "Creating station ${item}..." + mkdir -p ~/source/Stations/${item} + mkdir -p "${RMS_data}/${item}" + + # Copy config template + cp ~/source/RMS/.config ~/source/Stations/${item} -No_Stations=${#Station[@]} -for item in "${Station[@]}" -do - if [[ -d ~/source/Stations/${item} ]] - then - echo -e "\n\nNot creating station ${item} - it already exists\n" - exit - else - echo "making dir Stations/${item}" - mkdir ~/source/Stations/${item} - if [[ ! -d ${RMS_data}/$item ]] - then - mkdir ${RMS_data}/${item} - fi - - fi - cp ~/source/RMS/.config ~/source/Stations/${item} - # create autostart entry for the station - cat <<- EOF > ~/.config/autostart/${item}_StartCap.desktop + # Create autostart .desktop entry using gnome-terminal. + # The StartCapture profile is optional - create one to customise the + # capture window; without it gnome-terminal uses the default profile. + cat <<- EOF > ~/.config/autostart/${item}_StartCap.desktop [Desktop Entry] - Name=${item}-Startcapture + Name=${item}_StartCap Type=Application - Exec=lxterminal --title=${item} -e "~/source/RMS/Scripts/MultiCamLinux/StartCapture.sh ${item}" + Exec=gnome-terminal --profile=StartCapture --title=${item} -- bash -c "~/source/RMS/Scripts/MultiCamLinux/StartCapture.sh ${item}" Hidden=false NoDisplay=false - Icon=lxterminal + Icon=gnome-terminal EOF - # create Desktop softlink for StartCapture - chmod +x ~/.config/autostart/${item}_StartCap.desktop - ln -s ~/.config/autostart/${item}_StartCap.desktop ~/Desktop/${item}_StartCap.desktop - chmod -x ~/Desktop/${item}_StartCap.desktop - gio set ~/Desktop/${item}_StartCap.desktop metadata::trusted true - chmod +x ~/Desktop/${item}_StartCap.desktop - cp ~/source/RMS/mask.bmp ~/source/Stations/${item} - # create a ShowLiveStream- ~/Desktop/Show_LiveStream-${item}.desktop + + chmod +x ~/.config/autostart/${item}_StartCap.desktop + + # Create Desktop symlink for StartCapture + ln -sf ~/.config/autostart/${item}_StartCap.desktop "${Desktop}/${item}_StartCap.desktop" + gio set "${Desktop}/${item}_StartCap.desktop" metadata::trusted true 2>/dev/null + chmod +x "${Desktop}/${item}_StartCap.desktop" + + # Copy mask file + cp ~/source/RMS/mask.bmp ~/source/Stations/${item} 2>/dev/null + + # Create ShowLiveStream desktop shortcut using gnome-terminal + cat <<- EOF > "${Desktop}/Show_LiveStream-${item}.desktop" [Desktop Entry] Name=${item}-ShowLiveStream Type=Application - Exec=lxterminal --title=Stream-${item} -e "~/source/RMS/Scripts/MultiCamLinux/LiveStream.sh ${item}" + Exec=gnome-terminal --profile=StartCapture --title=Stream-${item} -- bash -c "~/source/RMS/Scripts/MultiCamLinux/LiveStream.sh ${item}" Hidden=false NoDisplay=false - Icon=lxterminal + Icon=gnome-terminal EOF - chmod -x ~/Desktop/Show_LiveStream-${item}.desktop - gio set ~/Desktop/Show_LiveStream-${item}.desktop metadata::trusted true - chmod +x ~/Desktop/Show_LiveStream-${item}.desktop - # - # customise each .config - # - # set the station_id - sed -i "s/D:.*$/D: $item/g" ~/source/Stations/${item}/.config - # set the ${RMS_data} dir - sed -i "s,data_dir.*$,data_dir: ${RMS_data}/${item},g" ~/source/Stations/${item}/.config - # update the free space in ratio of number of stations being added, using a 20Gb factor for now - sed -i "s/extra_space_gb: .*$/extra_space_gb: $(( ${No_Stations} * 20 ))/g" ~/source/Stations/${item}/.config - echo -e "\n\nAdded station $item\n\n" - # disable daily post processing reboot - sed -i "s/\(reboot_after_processing:\).*/\1 false/g" ~/source/Stations/${item}/.config -done -# check if keys are already present -if [[ -f ~/.ssh/id_rsa ]] - then - if [ ! -z ${Configured} ] - then - echo "SSH keys already exist, not overwriting them" - fi - else - ssh-keygen -t rsa -f ~/.ssh/id_rsa -q -P "" - echo "SSH keys successfully generated in ~/.ssh" -fi -timedatectl set-timezone UTC -gsettings set org.gnome.settings-daemon.plugins.power sleep-inactive-ac-type 'nothing' # disables inactivity lock -gsettings set org.gnome.settings-daemon.plugins.power sleep-inactive-ac-timeout 0 # sleep inactivity 0=='never' -gsettings set org.gnome.desktop.session idle-delay 600 # screen blank after xxx secs, adjust to taste 0=='never' -gsettings set org.gnome.desktop.screensaver lock-enabled false # disable screensaver lock - -# only prompt for these optional installs if the user has run this for the 1st time - -# launch the vRMS environment at logon -if ( ! grep -q RMS ~/.bashrc) -then - cat <>~/.bashrc - # Activate RMS - cd ~/source/RMS - source ~/vRMS/bin/activate -EOF + + gio set "${Desktop}/Show_LiveStream-${item}.desktop" metadata::trusted true 2>/dev/null + chmod +x "${Desktop}/Show_LiveStream-${item}.desktop" + + # Apply default config customizations + sed -i "s/^stationID:.*$/stationID: $item/g" "${config_file}" + sed -i "s,^data_dir:.*$,data_dir: ${RMS_data}/${item},g" "${config_file}" + sed -i "s/^extra_space_gb:.*$/extra_space_gb: $(( ${No_Stations} * 20 ))/g" "${config_file}" + sed -i "s/^\(reboot_after_processing:\).*/\1 false/g" "${config_file}" + + return 0 +} + +# Parse arguments: [RMS_data_path] [stations.csv] +# Smart detection: if single arg ends in .csv, treat as CSV file +RMS_data=~/RMS_data +CSV_FILE="" + +if [[ "$#" -eq 0 ]]; then + # No args: use the default CSV if one exists + if [[ -f ~/source/RMS/stations.csv ]]; then + CSV_FILE=~/source/RMS/stations.csv + fi +elif [[ "$#" -eq 1 ]]; then + # Single arg: check if it's a CSV file or a directory + if [[ "${1}" == *.csv ]]; then + CSV_FILE="${1}" + else + RMS_data="${1}" + if [[ -f ~/source/RMS/stations.csv ]]; then + CSV_FILE=~/source/RMS/stations.csv + fi + fi +else + # Two args: first is RMS_data, second is CSV + RMS_data="${1}" + CSV_FILE="${2}" fi -if ( ! command -v pcmanfm &> /dev/null ) -then - echo -e "Install pcmanfm file manager ?\n" - while true; do - read -p "Y/N: " Ans - case $Ans in - [yY]* ) sudo apt install -y pcmanfm; break;; - [nN] ) break ;; - esac - done + +# A CSV that was explicitly requested must exist +if [[ -n "${CSV_FILE}" && ! -f "${CSV_FILE}" ]]; then + echo "Error: CSV file not found: ${CSV_FILE}" + echo "Usage: $0 [RMS_data_path] [stations.csv]" + exit 1 fi -if (! command -v mousepad &> /dev/null ) -then - echo -e "Install mousepad ?\n" - while true; do - read -p "Y/N: " Ans - case $Ans in - [yY]* ) sudo apt install -y mousepad; break;; - [nN] ) break ;; - esac - done + +# Ensure required directories exist +mkdir -p ~/source/Stations +mkdir -p "${RMS_data}" +mkdir -p ~/.config/autostart + +if [[ -n "${CSV_FILE}" ]]; then + + echo "Reading stations from: ${CSV_FILE}" + + # Read CSV header to get column names + IFS=',' read -r -a HEADERS < "${CSV_FILE}" + + # Trim whitespace from headers + for i in "${!HEADERS[@]}"; do + HEADERS[$i]=$(trim "${HEADERS[$i]}") + done + + # Find required column indices + station_id_col=-1 + camera_ip_col=-1 + for i in "${!HEADERS[@]}"; do + case "${HEADERS[$i]}" in + station_id|stationID) station_id_col=$i ;; + camera_ip) camera_ip_col=$i ;; + esac + done + + if [[ $station_id_col -eq -1 ]]; then + echo "Error: CSV must have a 'station_id' or 'stationID' column" + exit 1 + fi + + # Count stations for extra_space_gb calculation + No_Stations=$(tail -n +2 "${CSV_FILE}" | grep -c -v '^[[:space:]]*$') + + echo -e "\nStations to add from CSV: ${No_Stations}" + + # Process each data row (the || keeps a final line without a trailing newline) + tail -n +2 "${CSV_FILE}" | while IFS=',' read -r -a VALUES || [[ ${#VALUES[@]} -gt 0 ]]; do + # Skip empty lines + [[ ${#VALUES[@]} -eq 0 ]] && continue + + # Trim whitespace from values + for i in "${!VALUES[@]}"; do + VALUES[$i]=$(trim "${VALUES[$i]}") + done + + item="${VALUES[$station_id_col]}" + [[ -z "$item" ]] && continue + + create_station "$item" || continue + + config_file=~/source/Stations/${item}/.config + + # Apply camera_ip to device URL if column exists + if [[ $camera_ip_col -ne -1 && -n "${VALUES[$camera_ip_col]}" ]]; then + camera_ip="${VALUES[$camera_ip_col]}" + # Replace IP address in the device URL (matches IP pattern in rtsp:// URL) + sed -i -E "s|^(device:.*rtsp://)[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+|\1${camera_ip}|g" "${config_file}" + echo " Set camera IP: ${camera_ip}" + fi + + # Apply any additional CSV columns that match .config keys + for i in "${!HEADERS[@]}"; do + header="${HEADERS[$i]}" + value="${VALUES[$i]}" + + # Skip station_id, camera_ip (already handled), and empty values + [[ "$header" == "station_id" || "$header" == "stationID" || "$header" == "camera_ip" ]] && continue + [[ -z "$value" ]] && continue + + # Check if this header exists as a key in .config + if grep -q "^${header}:" "${config_file}"; then + sed -i "s|^${header}:.*$|${header}: ${value}|g" "${config_file}" + echo " Set ${header}: ${value}" + fi + done + + echo "Added station ${item}" + done + +else + + # No CSV available - prompt for station IDs interactively + declare -a Station + while : + do + read -p "Enter station ID, to end: " this_Station + this_Station=$(trim "${this_Station^^}") + if [[ -z $this_Station ]]; then + break + fi + Station+=("$this_Station") + done + + No_Stations=${#Station[@]} + echo -e "\nNew stations to add -" + printf '%s\n' "${Station[@]}" + + for item in "${Station[@]}"; do + create_station "$item" && echo "Added station ${item}" + done fi -if ( ! command -v anydesk &> /dev/null ) -then - echo -e "Install AnyDesk ?" - echo -e "This will require sudo privileges \n" - while true; do - read -p "Y/N: " Ans - case $Ans in - [yY]* ) - wget -qO - https://keys.anydesk.com/repos/DEB-GPG-KEY | sudo apt-key add - - echo "deb http://deb.anydesk.com/ all main" > anydesk-stable.list - sudo mv anydesk-stable.list /etc/apt/sources.list.d/ - sudo apt update - sudo apt install -y anydesk - break;; - [nN] ) break ;; - esac - done +# Generate SSH keys if not present +if [[ ! -f ~/.ssh/id_rsa ]]; then + ssh-keygen -t rsa -f ~/.ssh/id_rsa -q -P "" + echo "SSH keys generated in ~/.ssh" fi -echo -e "\n\nStation configuration complete\n\n" -user=`id -u -n` - -# Optionaly create a crontab entry for the user to automate regular RMS updates I've chosen midday as a default but this can easily be changed -echo -e "Do you wish to schedule a regular cron job to stop all running instances," -echo -e "perform an update of RMS, and finally restart all configured instances? " -while true; do - read -p "Y/N: " Ans - case $Ans in - [yY]* ) - echo " The following template entry will schedule the action to run @3pm every Sunday" - echo " feel free to edit the line if you want to alter the timing and/or frequency, when you are happy with" - echo "the schedule, just hit return" - echo " If you want some examples of the required format check out https://crontab.guru/examples.html" - echo "0 15 * * SUN /home/${user}/source/RMS/Scripts/MultiCamLinux/GRMSUpdate.sh" | read Schedule - if sudo test -f "/var/spool/cron/crontabs/$user" # does user have an existing crontab? - then - if sudo grep -q RMSUpdater /var/spool/cron/crontabs/$user # check there isn't an existing entry - then - echo -e "\nNot modifying ${user}'s crontab -that user has an existing entry\n" - else - read -e -i "0 15 * * SUN" Schedule - # can't edit users crontab in-place without messing with dir permissions... - sudo mv /var/spool/cron/crontabs/$user $user.tmp - set -o noglob - sudo echo "$Schedule /home/$user/source/RMS/Scripts/MultiCamLinux/GRMSUpdater.sh" >> ${user}.tmp - set +o noglob - sudo mv ${user}.tmp /var/spool/cron/crontabs/${user} - echo -e "\n User ${user}'s crontab has been updated, if you subsequently wish to edit it you can do so" - echo -e "by issuing the following cmd in a terminal- crontab -e\n" - fi -else - read -e -i "0 15 * * SUN" Schedule - set -o noglob - # user does not have a crontab so create one with the standard boilerplate stuff - touch ${user}.tmp - cat <<- EOF >${user}.tmp - # Edit this file to introduce tasks to be run by cron. - # - # Each task to run has to be defined through a single line - # indicating with different fields when the task will be run - # and what command to run for the task - # - # To define the time you can provide concrete values for - # minute (m), hour (h), day of month (dom), month (mon), - # and day of week (dow) or use '*' in these fields (for 'any'). - # - # Notice that tasks will be started based on the cron's system - # daemon's notion of time and timezones. - # - # Output of the crontab jobs (including errors) is sent through - # email to the user the crontab file belongs to (unless redirected). - # - # For example, you can run a backup of all your user accounts - # at 5 a.m every week with: - # 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/ - # - # For more information see the manual pages of crontab(5) and cron(8) - # - # m h dom mon dow command - $Schedule /home/$user/source/RMS/Scripts/MultiCamLinux/GRMSUpdater.sh - EOF +# Add RMS activation to .bashrc if not present +if ! grep -q "source ~/vRMS/bin/activate" ~/.bashrc; then + cat <> ~/.bashrc + +# Activate RMS +cd ~/source/RMS +source ~/vRMS/bin/activate +EOF +fi - set -o noglob - sudo mv ${user}.tmp /var/spool/cron/crontabs/${user} - echo -e "\nA new crontab has been installed for ${user}, if you subsequently wish to change it in any way" - echo -e "you can edit it in a terminal with the cmd - crontab -e\n" - fi - break ;; - [nN]* ) break ;; - esac - done - -# Set up shortcut for CMNbinViewer -cat <<- EOF > ~/Desktop/CMNbinViewer.desktop +# Create CMNbinViewer desktop shortcut +cat <<- EOF > "${Desktop}/CMNbinViewer.desktop" [Desktop Entry] Name=CMNbinViewer Type=Application -Exec=/home/${user}/source/RMS/Scripts/CMNbinViewer_env.sh +Exec=${HOME}/source/RMS/Scripts/CMNbinViewer_env.sh Hidden=false NoDisplay=false -Icon=/home/${user}/source/cmn_binviewer/icon.png +Icon=${HOME}/source/RMS/Scripts/MultiCamLinux/icon.png EOF -# set the 'allow launching' feature -chmod -x ~/Desktop/CMNbinViewer.desktop -gio set ~/Desktop/CMNbinViewer.desktop metadata::trusted true -chmod +x ~/Desktop/CMNbinViewer.desktop -if grep -Fqv "UTC" /etc/timezone -then -sudo timedatectl set-timezone UTC -fi -# Fix AnyDesk issue with Wayland ... requires sudo -if grep -q '#Way' /etc/gdm3/custom.conf -then - sudo sed -i 's/#\(WaylandEnable.*\)/\1/g' /etc/gdm3/custom.conf -fi -exit + +gio set "${Desktop}/CMNbinViewer.desktop" metadata::trusted true 2>/dev/null +chmod +x "${Desktop}/CMNbinViewer.desktop" + +echo -e "\nStation configuration complete" diff --git a/Scripts/RMS_Installer.sh b/Scripts/RMS_Installer.sh index 4dde5a263..03cc8c12a 100755 --- a/Scripts/RMS_Installer.sh +++ b/Scripts/RMS_Installer.sh @@ -59,6 +59,14 @@ sudo systemctl enable chrony sudo systemctl start ssh sudo systemctl enable ssh +# On GNOME desktops, prevent sleep and screen lock so captures are not interrupted +if command -v gsettings >/dev/null 2>&1; then + gsettings set org.gnome.settings-daemon.plugins.power sleep-inactive-ac-type 'nothing' 2>/dev/null + gsettings set org.gnome.settings-daemon.plugins.power sleep-inactive-ac-timeout 0 2>/dev/null + gsettings set org.gnome.desktop.session idle-delay 600 2>/dev/null + gsettings set org.gnome.desktop.screensaver lock-enabled false 2>/dev/null +fi + # Clone repositories git clone https://github.com/CroatianMeteorNetwork/RMS.git git clone https://github.com/CroatianMeteorNetwork/cmn_binviewer.git From 345c12d622b1e61d8e58f023615e2c7896be3421 Mon Sep 17 00:00:00 2001 From: Luc Busquin <133058544+Cybis320@users.noreply.github.com> Date: Wed, 10 Jun 2026 00:28:36 -0700 Subject: [PATCH 03/17] Unify station tooling into a universal add_Station.sh add_Pi_Station.sh and add_GStation.sh duplicated the same station creation logic with platform assumptions baked in, and the copies had drifted (different extra_space factors, entry names, and bug fixes applied to one but not the other). Both are now thin compatibility wrappers around a single universal Scripts/MultiCamLinux/add_Station.sh that works on any Linux platform, Raspberry Pi included: - Platform detection reads /sys/firmware/devicetree/base/model instead of uname/lscpu, so 32-bit Pi installs are detected and can migrate too - The Pi camera limit (1, or 4 on a Pi5 or later) is now advisory: the script warns that it is the tested ceiling and lets the operator override it, instead of hard-stopping. Future more capable Pis and experiments are not blocked. Generic Linux has no limit, as before - The legacy single-camera relocation is universal, so a PC that was set up as a single camera can convert just like a Pi. Pi image cosmetics (conky, wayfire, image desktop shortcuts) only run when the files exist - Launch entries use whichever of lxterminal/gnome-terminal fits the desktop. On FirstRun-managed systems (the Pi images) captures are started by RMS_StartCapture_MCP.sh as before; elsewhere per-station XDG autostart entries start captures at login - CSV-driven bulk creation and the interactive prompts are available on every platform RMS_FirstRun.sh now offers the conversion on any platform that has not converted yet (the raspi-config step is skipped where raspi-config does not exist, and the config editor falls back to gedit/gnome-text-editor on GNOME), and chains to add_Station.sh, which shows the relocation details and asks the final confirmation. RMS_StartCapture_MCP.sh picks the terminal emulator the same way. --- .../MultiCamLinux/Pi/RMS_StartCapture_MCP.sh | 25 +- Scripts/MultiCamLinux/Pi/add_Pi_Station.sh | 372 +------------ Scripts/MultiCamLinux/add_GStation.sh | 295 +--------- Scripts/MultiCamLinux/add_Station.sh | 521 ++++++++++++++++++ Scripts/RMS_FirstRun.sh | 58 +- 5 files changed, 575 insertions(+), 696 deletions(-) create mode 100755 Scripts/MultiCamLinux/add_Station.sh diff --git a/Scripts/MultiCamLinux/Pi/RMS_StartCapture_MCP.sh b/Scripts/MultiCamLinux/Pi/RMS_StartCapture_MCP.sh index 1afc9fbca..d079de974 100755 --- a/Scripts/MultiCamLinux/Pi/RMS_StartCapture_MCP.sh +++ b/Scripts/MultiCamLinux/Pi/RMS_StartCapture_MCP.sh @@ -1,7 +1,9 @@ #!/bin/bash -# On a single camera system there is no risk of concurrent first-start updates, -# so the long delay is only needed when more than one station is configured +# Start capture on all configured stations, staggered so concurrent +# first-start updates cannot collide. On a single camera system there is +# no such risk, so the long delay is skipped + dircount=$(find ~/source/Stations -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l) if [[ $dircount -le 1 ]]; then seconds=2 @@ -9,14 +11,27 @@ else seconds=70 fi +# Detect the terminal emulator +if [[ "${XDG_CURRENT_DESKTOP:-}" == *GNOME* ]] && command -v gnome-terminal >/dev/null 2>&1; then + TERMINAL=gnome-terminal +elif command -v lxterminal >/dev/null 2>&1; then + TERMINAL=lxterminal +else + TERMINAL=gnome-terminal +fi + echo " Starting all configured stations post-update..." loop=0 -for Dir in ~/source/Stations/* +for Dir in ~/source/Stations/*/ do - Station=$(basename $Dir) + Station=$(basename "$Dir") echo " Starting camera ${Station}" - lxterminal --title=${Station} -e "$HOME/source/RMS/Scripts/MultiCamLinux/StartCapture.sh ${Station}" & + if [[ "$TERMINAL" == "gnome-terminal" ]]; then + gnome-terminal --profile=StartCapture --title=${Station} -- bash -c "$HOME/source/RMS/Scripts/MultiCamLinux/StartCapture.sh ${Station}" & + else + lxterminal --title=${Station} -e "$HOME/source/RMS/Scripts/MultiCamLinux/StartCapture.sh ${Station}" & + fi echo " waiting $seconds seconds..." sleep ${seconds} if [[ $loop = 0 ]] ; then diff --git a/Scripts/MultiCamLinux/Pi/add_Pi_Station.sh b/Scripts/MultiCamLinux/Pi/add_Pi_Station.sh index 1cb8a3df7..9e2e7180c 100755 --- a/Scripts/MultiCamLinux/Pi/add_Pi_Station.sh +++ b/Scripts/MultiCamLinux/Pi/add_Pi_Station.sh @@ -1,370 +1,4 @@ #!/bin/bash -# This software is part of the Linux port of RMS -# Copyright (C) 2023 Ed Harman -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# Version 1.9 - support single camera platforms (RPi4 and older) so RMS_FirstRun -# can offer migration to the Stations data structure on all systems, -# also migrate camera_settings.json and all captured data -# -# Version 1.8 - replaced Desktop link with one that starts all captures -# -# Version 1.7 - changed desktop launcher display names and added conkyrc1 mod -# -# Version 1.6 - added support for RPi-5 platforms that can run max 4 cameras -# -# Version 1.5 - added support for non English locales where user user directories may not include a directory named Desktop -# i.e. this enables support of RMS on a non English distro install -# -# Version 1.4 - moved codebase into RMS/Scripts/MultiCamLinux -# -# Version 1.3 - fixed path to CMN desktop shortcut -# -# Version 1.2 - added a change to the flag reboot_after_processing from true to false -# -# Version 1.1 -# Changes - added station arguments to Launch scripts -# - changed desktop links for StartCapture to symbolic links of the scripts within .config/autostart -# -# Version 1.2 - Peter E. took over dev of this script and any blame going forward -# - white space and indents added -# - -MaxStation=4 # maximum number of stations allowed on a Pi5 - -if [[ $(uname -m ) == aarch64 ]] -then - Model=$(lscpu| grep -o 'Cortex.*'|grep -o '[0-9]*') - if [[ $Model -lt 76 ]] # RPi5 uses Cortex A76 - use 72 for script testing on an RPi4 - then - echo "This platform only supports 1 camera, an RPi5 or similar is required for multiple cameras" - MaxStation=1 - # quit if a camera is already configured under ~/source/Stations - if [[ -d ~/source/Stations ]] - then - dircount=$(find ~/source/Stations -mindepth 1 -maxdepth 1 -type d | wc -l) - if [[ $dircount -gt 0 ]] - then - read -n1 -r -p 'You are already running a camera on this platform, press ENTER to quit now' key - exit - fi - fi - fi - - DefStation=$(awk ' /stationID:/ {print toupper($2)}' ~/source/RMS/.config) # force uppercase - if [[ $DefStation == XX0001 && ! -d ~/source/Stations/ ]] - then - echo "Please run RMS_Firstrun and configure your 1st station" - echo "then if you wish to configure additional stations execute add_Pi_Station again" - exit - fi - if [[ ! -d ~/source/Stations/ ]] - then - -cat <, - # and move any captured data to ~/RMS_data/. - - # tweak conky station title - sed -i 's/\(source\)\/RMS/\1\/Stations\/'"$DefStation"'/g' /home/rms/.conkyrc1 - # tweak conky data source path for log data - sed -i "s/\(.*RMS_data\)/\1\/${DefStation}/g" /home/rms/.conkyrc1 - - mkdir ~/source/Stations/ - mkdir ~/source/Stations/${DefStation} - cp ~/source/RMS/.config ~/source/Stations/${DefStation} - cd ~/source/RMS - sed -i "s,^data_dir.*$,data_dir: ~/RMS_data/${DefStation},g" ~/source/Stations/${DefStation}/.config - if [[ -e ~/source/RMS/platepar_cmn2010.cal ]] - then - mv ~/source/RMS/platepar_cmn2010.cal ~/source/Stations/${DefStation}/ - fi - if [[ -e ~/source/RMS/mask.bmp ]] - then - mv ~/source/RMS/mask.bmp ~/source/Stations/${DefStation}/ - fi - if [[ -e ~/source/RMS/camera_settings.json ]] - then - mv ~/source/RMS/camera_settings.json ~/source/Stations/${DefStation}/ - # point the station .config at the relocated camera settings file - sed -i "s,^camera_settings_path:.*$,camera_settings_path: ~/source/Stations/${DefStation}/camera_settings.json,g" ~/source/Stations/${DefStation}/.config - fi - - # restore pristine default copies in ~/source/RMS, these are used as - # templates when additional stations are added - cd ~/source/RMS - for file in mask.bmp camera_settings.json - do - git checkout -- "$file" 2>/dev/null || \ - wget -q -O "$file" "https://raw.githubusercontent.com/CroatianMeteorNetwork/RMS/master/$file" - done - - # move any existing captured data into the station folder - mkdir -p ~/RMS_data/${DefStation} - find ~/RMS_data -mindepth 1 -maxdepth 1 ! -name "${DefStation}" -exec mv -t ~/RMS_data/${DefStation}/ {} + - - cat <<- EOF > ~/Desktop/${DefStation}_StartCapture.desktop - [Desktop Entry] - Name=${DefStation}-StartCapture - Type=Application - Exec=lxterminal --title=${DefStation} -e "~/source/RMS/Scripts/MultiCamLinux/StartCapture.sh ${DefStation}" - Hidden=false - NoDisplay=false - Icon=lxterminal - EOF - chmod +x ~/Desktop/${DefStation}_StartCapture.desktop - - cat <<- EOF > ~/Desktop/${DefStation}-Show_LiveStream.desktop - [Desktop Entry] - Name=${DefStation}-ShowLiveStream - Type=Application - Exec=lxterminal --title=${DefStation}-LiveStream -e "~/source/RMS/Scripts/MultiCamLinux/LiveStream.sh ${DefStation}" - Hidden=false - NoDisplay=false - Icon=lxterminal - EOF - chmod +x ~/Desktop/${DefStation}-Show_LiveStream.desktop - - # Check if the user's desktop directory environment variable is set - if [ -n "$xdg-user-dir DESKTOP" ] - then - Desktop=`xdg-user-dir DESKTOP` - fi - - # cleanup unneeded Desktop shortcuts - if [[ -f ~/Desktop/RMS_FirstRun.sh ]] - then - rm -f ~/Desktop/CMNbinViewer.sh ~/Desktop/RMS_ShowLiveStream.sh - rm -f ~/Desktop/RMS_StartCapture.sh ~/Desktop/RMS_config.txt - rm -f ~/Desktop/TunnelIPCamera.sh ~/Desktop/DownloadOpenVPNconfig.sh - fi - - if [[ $MaxStation != 1 ]] - then - # remove comment from last line of wayfire.ini to enable window cascade - sed -i s/#mode/mode/ ~/.config/wayfire.ini - fi - - fi # if [[ ! -d ~/source/Stations/ ]] - -fi # if [[ $(uname -m ) == aarch64 ]] - -# need to count the number of directories under ~/source/Stations and only -# prompt for adding the correct number of new cameras, so subtract that number -# from MaxStation defined above - -numdirs=$(find ~/source/Stations/ -maxdepth 1 -type d | wc -l) -numdirs=$((numdirs-1)) -Remaining=$((MaxStation-numdirs)) -#echo MaxStation:$MaxStation NumDirs:$numdirs Remaining:$Remaining - -declare Station -while : -do - if [[ ${#Station[@]} == $Remaining ]] - then - echo "" - echo "Done, this platform supports a maximum of $MaxStation camera(s)" - break - fi - read -p "Enter station ID, to end: " this_Station - this_Station="${this_Station^^}" #uppercase it.. - if [[ -z $this_Station ]] - then - break - fi - Station+=("$this_Station") - # echo $Station -done - -if [[ $MaxStation != 1 ]] -then - echo -e "\nNew stations to add -" - printf '%s\n' "${Station[@]}" -fi - -# check if ~/RMS_data exists and if not create it.... -if [[ ! -d ~/RMS_data ]] -then - mkdir ~/RMS_data -fi - -No_Stations=${#Station[@]} -for item in "${Station[@]}" -do - if [[ -d ~/source/Stations/${item} ]] - then - echo -e "\n\nNot creating station ${item} - it already exists\n" - exit - else - echo "making dir Stations/${item}" - mkdir ~/source/Stations/${item} - if [[ ! -d ~/RMS_data/${item} ]] - then - mkdir ~/RMS_data/${item} - fi - fi - - cp ~/source/RMS/.config ~/source/Stations/${item} - - # create autostart entry for the station - cat <<- EOF > ~/Desktop/${item}_StartCapture.desktop - [Desktop Entry] - Name=${item}-StartCapture - Type=Application - Exec=lxterminal --title=${item} -e "~/source/RMS/Scripts/MultiCamLinux/StartCapture.sh ${item}" - Hidden=false - NoDisplay=false - Icon=lxterminal - EOF - chmod +x ~/Desktop/${item}_StartCapture.desktop - - if [[ -e ~/source/RMS/mask.bmp ]] - then - cp ~/source/RMS/mask.bmp ~/source/Stations/${item}/ - fi - - # create a ShowLiveStream- ~/Desktop/${item}-Show_LiveStream.desktop - [Desktop Entry] - Name=${item}-ShowLiveStream - Type=Application - Exec=lxterminal --title=Stream-${item} -e "~/source/RMS/Scripts/MultiCamLinux/LiveStream.sh ${item}" - Hidden=false - NoDisplay=false - Icon=lxterminal - EOF - chmod +x ~/Desktop/${item}-Show_LiveStream.desktop - - # customise each .config - # set the station_id - sed -i "s/D:.*$/D: $item/g" ~/source/Stations/${item}/.config - # set the data_dir - sed -i "s,^data_dir.*$,data_dir: ~/RMS_data/${item},g" ~/source/Stations/${item}/.config - echo -e "\n\nAdded station $item\n\n" - if [[ $MaxStation != 1 ]] - then - # disable daily post processing reboot - sed -i "s/\(reboot_after_processing:\).*/\1 false/g" ~/source/Stations/${item}/.config - fi -done - -# check if keys are already present -if [[ -f ~/.ssh/id_rsa ]] -then - if [ ! -z ${Configured} ] - then - echo "SSH keys already exist, not overwriting them" - fi -else - ssh-keygen -t rsa -f ~/.ssh/id_rsa -q -P "" - cp ~/.ssh/id_rsa.pub ~/Desktop - echo "SSH keys successfully generated in ~/.ssh" - echo "your new id_rsa.pub public key file now placed on the Desktop" - echo "Be sure to send a copy of this file to Denis" -fi - -if [[ ! -v Model ]] -then - timedatectl set-timezone UTC -fi - -echo -e "\n\nStation configuration complete\n\n" - -# Set up shortcut for CMNbinViewer -cat <<- EOF > ~/Desktop/CMNbinViewer.desktop -[Desktop Entry] -Name=CMNbinViewer -Type=Application -Exec=${HOME}/source/RMS/Scripts/CMNbinViewer_env.sh -Hidden=false -NoDisplay=false -Icon=${HOME}/source/RMS/Scripts/MultiCamLinux/icon.png -EOF -chmod +x ~/Desktop/CMNbinViewer.desktop - -# Create Desktop shortcut for editing the station .config files -cat << 'EOF' > ~/Desktop/Edit_configs -#!/bin/bash - -echo "" -echo "One by one, each config file under ~/source/Stations will open for editing..." -read -n1 -r -p 'Press ENTER to proceed, any other key to quit: ' key -if [[ "${key}" != "" ]]; then - exit -fi - -for Dir in ~/source/Stations/*/ -do - Station=$(basename "${Dir}") - echo "" - echo "Editing .config file for ${Station}" - read -n1 -r -p 'Press ENTER to edit, any other key to skip: ' key - if [[ "${key}" = "" ]]; then - mousepad ~/source/Stations/${Station}/.config - fi -done - -echo "" -echo "Done editing .config files" -sleep 2 -EOF -chmod +x ~/Desktop/Edit_configs - -# set the option to allow launching of desktop shortcuts -#sed -i s/quick_exec=0/quick_exec=1/ ~/.config/libfm/libfm.conf - -if grep -Fqv "UTC" /etc/timezone -then - sudo timedatectl set-timezone UTC -fi - -if [[ $MaxStation != 1 ]] -then - # set the extra_space for all configured stations - - mult=$(ls -1 /home/${USER}/source/Stations/|wc -l) - for Dir in /home/${USER}/source/Stations/* - do - sed -i "s/extra_space_gb: .*$/extra_space_gb: $(( ${mult} * 30 ))/g" ${Dir}/.config - done -fi - -# set up so RMS_FirstRun can autostart all captures -rm -f /home/${USER}/Desktop/RMS_StartCapture.sh -ln -s /home/${USER}/source/RMS/Scripts/MultiCamLinux/Pi/RMS_StartCapture_MCP.sh /home/${USER}/Desktop/RMS_StartCapture.sh - -# replace the potentially modified .config in ~/source/RMS with a pristine default -cd ~/source/RMS -git checkout -- .config 2>/dev/null || \ - wget -q -O .config https://raw.githubusercontent.com/CroatianMeteorNetwork/RMS/master/.config +# Kept for compatibility with existing documentation and older images. +# The station tooling is now universal - see add_Station.sh. +exec "$(dirname "$0")/../add_Station.sh" "$@" diff --git a/Scripts/MultiCamLinux/add_GStation.sh b/Scripts/MultiCamLinux/add_GStation.sh index 607b346e8..1ed202c0a 100755 --- a/Scripts/MultiCamLinux/add_GStation.sh +++ b/Scripts/MultiCamLinux/add_GStation.sh @@ -1,293 +1,4 @@ #!/bin/bash -# This software is part of the Linux port of RMS -# Copyright (C) 2023 Ed Harman -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# Version 1.7 - stations can be created in bulk from a stations.csv file, -# desktop entries use gnome-terminal instead of lxterminal, -# interactive prompts remain the fallback when no CSV is given -# -# Version 1.6 - added logic to prevent running this script on a Raspberry Pi -# added by Peter E., June 2024 -# -# Version 1.5 - added support for non English locales where user user directories may not include a directory named Desktop -# i.e. this enables support of RMS on a non English distro install -# -# Version 1.4 - moved codebase into RMS/Scripts/MultiCamLinux -# -# Version 1.3 - fixed path to CMN desktop shortcut -# -# Version 1.2 - added a change to the flag reboot_after_processing from true to false -# -# Version 1.1 -# Changes - added station arguments to Launch scripts -# - changed desktop links for StartCapture to symbolic links of the scripts within .config/autostart -# - -# Prevent running this script on a Raspberry Pi -file=/sys/firmware/devicetree/base/model -if [[ -f "$file" ]]; then - contents=$(tr -d '\0' < $file) - if [[ $contents == 'Raspberry'* ]]; then - echo "The add_GStation.sh script should not be used on Raspberry Pi." - echo "Please use add_Pi_Station.sh to add cameras on a Pi5." - exit 1 - fi -fi - -# Get user's desktop directory -Desktop=$(xdg-user-dir DESKTOP 2>/dev/null || echo "$HOME/Desktop") - -# Strip leading/trailing whitespace (including CR from Windows-edited CSVs) -trim() { - local s="$*" - s="${s#"${s%%[![:space:]]*}"}" - s="${s%"${s##*[![:space:]]}"}" - printf '%s' "$s" -} - -# Create a station directory with config, mask and desktop entries. -# Returns 1 if the station already exists. -create_station() { - local item="$1" - local config_file=~/source/Stations/${item}/.config - - if [[ -d ~/source/Stations/${item} ]]; then - echo -e "\nNot creating station ${item} - it already exists" - return 1 - fi - - echo "Creating station ${item}..." - mkdir -p ~/source/Stations/${item} - mkdir -p "${RMS_data}/${item}" - - # Copy config template - cp ~/source/RMS/.config ~/source/Stations/${item} - - # Create autostart .desktop entry using gnome-terminal. - # The StartCapture profile is optional - create one to customise the - # capture window; without it gnome-terminal uses the default profile. - cat <<- EOF > ~/.config/autostart/${item}_StartCap.desktop - [Desktop Entry] - Name=${item}_StartCap - Type=Application - Exec=gnome-terminal --profile=StartCapture --title=${item} -- bash -c "~/source/RMS/Scripts/MultiCamLinux/StartCapture.sh ${item}" - Hidden=false - NoDisplay=false - Icon=gnome-terminal - EOF - - chmod +x ~/.config/autostart/${item}_StartCap.desktop - - # Create Desktop symlink for StartCapture - ln -sf ~/.config/autostart/${item}_StartCap.desktop "${Desktop}/${item}_StartCap.desktop" - gio set "${Desktop}/${item}_StartCap.desktop" metadata::trusted true 2>/dev/null - chmod +x "${Desktop}/${item}_StartCap.desktop" - - # Copy mask file - cp ~/source/RMS/mask.bmp ~/source/Stations/${item} 2>/dev/null - - # Create ShowLiveStream desktop shortcut using gnome-terminal - cat <<- EOF > "${Desktop}/Show_LiveStream-${item}.desktop" - [Desktop Entry] - Name=${item}-ShowLiveStream - Type=Application - Exec=gnome-terminal --profile=StartCapture --title=Stream-${item} -- bash -c "~/source/RMS/Scripts/MultiCamLinux/LiveStream.sh ${item}" - Hidden=false - NoDisplay=false - Icon=gnome-terminal - EOF - - gio set "${Desktop}/Show_LiveStream-${item}.desktop" metadata::trusted true 2>/dev/null - chmod +x "${Desktop}/Show_LiveStream-${item}.desktop" - - # Apply default config customizations - sed -i "s/^stationID:.*$/stationID: $item/g" "${config_file}" - sed -i "s,^data_dir:.*$,data_dir: ${RMS_data}/${item},g" "${config_file}" - sed -i "s/^extra_space_gb:.*$/extra_space_gb: $(( ${No_Stations} * 20 ))/g" "${config_file}" - sed -i "s/^\(reboot_after_processing:\).*/\1 false/g" "${config_file}" - - return 0 -} - -# Parse arguments: [RMS_data_path] [stations.csv] -# Smart detection: if single arg ends in .csv, treat as CSV file -RMS_data=~/RMS_data -CSV_FILE="" - -if [[ "$#" -eq 0 ]]; then - # No args: use the default CSV if one exists - if [[ -f ~/source/RMS/stations.csv ]]; then - CSV_FILE=~/source/RMS/stations.csv - fi -elif [[ "$#" -eq 1 ]]; then - # Single arg: check if it's a CSV file or a directory - if [[ "${1}" == *.csv ]]; then - CSV_FILE="${1}" - else - RMS_data="${1}" - if [[ -f ~/source/RMS/stations.csv ]]; then - CSV_FILE=~/source/RMS/stations.csv - fi - fi -else - # Two args: first is RMS_data, second is CSV - RMS_data="${1}" - CSV_FILE="${2}" -fi - -# A CSV that was explicitly requested must exist -if [[ -n "${CSV_FILE}" && ! -f "${CSV_FILE}" ]]; then - echo "Error: CSV file not found: ${CSV_FILE}" - echo "Usage: $0 [RMS_data_path] [stations.csv]" - exit 1 -fi - -# Ensure required directories exist -mkdir -p ~/source/Stations -mkdir -p "${RMS_data}" -mkdir -p ~/.config/autostart - -if [[ -n "${CSV_FILE}" ]]; then - - echo "Reading stations from: ${CSV_FILE}" - - # Read CSV header to get column names - IFS=',' read -r -a HEADERS < "${CSV_FILE}" - - # Trim whitespace from headers - for i in "${!HEADERS[@]}"; do - HEADERS[$i]=$(trim "${HEADERS[$i]}") - done - - # Find required column indices - station_id_col=-1 - camera_ip_col=-1 - for i in "${!HEADERS[@]}"; do - case "${HEADERS[$i]}" in - station_id|stationID) station_id_col=$i ;; - camera_ip) camera_ip_col=$i ;; - esac - done - - if [[ $station_id_col -eq -1 ]]; then - echo "Error: CSV must have a 'station_id' or 'stationID' column" - exit 1 - fi - - # Count stations for extra_space_gb calculation - No_Stations=$(tail -n +2 "${CSV_FILE}" | grep -c -v '^[[:space:]]*$') - - echo -e "\nStations to add from CSV: ${No_Stations}" - - # Process each data row (the || keeps a final line without a trailing newline) - tail -n +2 "${CSV_FILE}" | while IFS=',' read -r -a VALUES || [[ ${#VALUES[@]} -gt 0 ]]; do - # Skip empty lines - [[ ${#VALUES[@]} -eq 0 ]] && continue - - # Trim whitespace from values - for i in "${!VALUES[@]}"; do - VALUES[$i]=$(trim "${VALUES[$i]}") - done - - item="${VALUES[$station_id_col]}" - [[ -z "$item" ]] && continue - - create_station "$item" || continue - - config_file=~/source/Stations/${item}/.config - - # Apply camera_ip to device URL if column exists - if [[ $camera_ip_col -ne -1 && -n "${VALUES[$camera_ip_col]}" ]]; then - camera_ip="${VALUES[$camera_ip_col]}" - # Replace IP address in the device URL (matches IP pattern in rtsp:// URL) - sed -i -E "s|^(device:.*rtsp://)[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+|\1${camera_ip}|g" "${config_file}" - echo " Set camera IP: ${camera_ip}" - fi - - # Apply any additional CSV columns that match .config keys - for i in "${!HEADERS[@]}"; do - header="${HEADERS[$i]}" - value="${VALUES[$i]}" - - # Skip station_id, camera_ip (already handled), and empty values - [[ "$header" == "station_id" || "$header" == "stationID" || "$header" == "camera_ip" ]] && continue - [[ -z "$value" ]] && continue - - # Check if this header exists as a key in .config - if grep -q "^${header}:" "${config_file}"; then - sed -i "s|^${header}:.*$|${header}: ${value}|g" "${config_file}" - echo " Set ${header}: ${value}" - fi - done - - echo "Added station ${item}" - done - -else - - # No CSV available - prompt for station IDs interactively - declare -a Station - while : - do - read -p "Enter station ID, to end: " this_Station - this_Station=$(trim "${this_Station^^}") - if [[ -z $this_Station ]]; then - break - fi - Station+=("$this_Station") - done - - No_Stations=${#Station[@]} - echo -e "\nNew stations to add -" - printf '%s\n' "${Station[@]}" - - for item in "${Station[@]}"; do - create_station "$item" && echo "Added station ${item}" - done - -fi - -# Generate SSH keys if not present -if [[ ! -f ~/.ssh/id_rsa ]]; then - ssh-keygen -t rsa -f ~/.ssh/id_rsa -q -P "" - echo "SSH keys generated in ~/.ssh" -fi - -# Add RMS activation to .bashrc if not present -if ! grep -q "source ~/vRMS/bin/activate" ~/.bashrc; then - cat <> ~/.bashrc - -# Activate RMS -cd ~/source/RMS -source ~/vRMS/bin/activate -EOF -fi - -# Create CMNbinViewer desktop shortcut -cat <<- EOF > "${Desktop}/CMNbinViewer.desktop" -[Desktop Entry] -Name=CMNbinViewer -Type=Application -Exec=${HOME}/source/RMS/Scripts/CMNbinViewer_env.sh -Hidden=false -NoDisplay=false -Icon=${HOME}/source/RMS/Scripts/MultiCamLinux/icon.png -EOF - -gio set "${Desktop}/CMNbinViewer.desktop" metadata::trusted true 2>/dev/null -chmod +x "${Desktop}/CMNbinViewer.desktop" - -echo -e "\nStation configuration complete" +# Kept for compatibility with existing documentation. +# The station tooling is now universal - see add_Station.sh. +exec "$(dirname "$0")/add_Station.sh" "$@" diff --git a/Scripts/MultiCamLinux/add_Station.sh b/Scripts/MultiCamLinux/add_Station.sh new file mode 100755 index 000000000..18917c376 --- /dev/null +++ b/Scripts/MultiCamLinux/add_Station.sh @@ -0,0 +1,521 @@ +#!/bin/bash +# This software is part of the Linux port of RMS +# Copyright (C) 2023 Ed Harman +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Universal station tool for any Linux platform, Raspberry Pi included. +# Merged from add_Pi_Station.sh and add_GStation.sh, which are kept as +# compatibility wrappers around this script. +# +# What it does: +# - relocates a legacy single-camera setup (config files in ~/source/RMS, +# data directly in ~/RMS_data) into the per-station layout under +# ~/source/Stations/ and ~/RMS_data/ +# - creates new stations, either interactively or in bulk from a CSV file +# +# Usage: add_Station.sh [RMS_data_path] [stations.csv] +# +# The CSV header row names the columns: station_id (required), camera_ip +# (spliced into the rtsp device URL), and any other .config key, which is +# applied verbatim to that station's config. With no CSV argument the +# script uses ~/source/RMS/stations.csv if present, otherwise it prompts +# for station IDs interactively. +# +# On Raspberry Pi platforms a recommended camera limit applies (1 camera, +# or 4 on a Pi5 or later). The limit is advisory - it reflects what has +# been tested - and can be overridden at the prompt. + +# ---------------------------------------------------------------- helpers + +# Strip leading/trailing whitespace (including CR from Windows-edited CSVs) +trim() { + local s="$*" + s="${s#"${s%%[![:space:]]*}"}" + s="${s%"${s##*[![:space:]]}"}" + printf '%s' "$s" +} + +uppercase() { + printf '%s' "$*" | tr '[:lower:]' '[:upper:]' +} + +# Restore a pristine copy of a file in ~/source/RMS from git, or from +# GitHub when ~/source/RMS is not a git checkout +restore_default() { + ( cd ~/source/RMS && git checkout -- "$1" 2>/dev/null ) || \ + wget -q -O ~/source/RMS/"$1" "https://raw.githubusercontent.com/CroatianMeteorNetwork/RMS/master/$1" +} + +count_stations() { + find ~/source/Stations -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l +} + +# Build the Exec= line for a .desktop entry using the detected terminal. +# The gnome-terminal StartCapture profile is optional - create one to +# customise the capture windows; without it the default profile is used. +term_exec() { # $1 = window title, $2 = command + if [[ "$TERMINAL" == "gnome-terminal" ]]; then + printf 'gnome-terminal --profile=StartCapture --title=%s -- bash -c "%s"' "$1" "$2" + else + printf 'lxterminal --title=%s -e "%s"' "$1" "$2" + fi +} + +# Create a station directory with config, mask and launch entries. +# Returns 1 if the station already exists. +create_station() { + local item="$1" + local config_file=~/source/Stations/${item}/.config + local entry + + if [[ -d ~/source/Stations/${item} ]]; then + echo -e "\nNot creating station ${item} - it already exists" + return 1 + fi + + echo "Creating station ${item}..." + mkdir -p ~/source/Stations/${item} + mkdir -p "${RMS_data}/${item}" + + # Copy config template + cp ~/source/RMS/.config ~/source/Stations/${item} + + # Copy mask file + cp ~/source/RMS/mask.bmp ~/source/Stations/${item} 2>/dev/null + + # Launch entry for the station. On FirstRun-managed systems (the Pi + # images) captures are started by RMS_StartCapture_MCP.sh, so only a + # Desktop launcher is created; elsewhere the entry goes into XDG + # autostart with a Desktop symlink, so captures start at login. + if [[ -f ~/.rmsautorunflag ]]; then + entry="${Desktop}/${item}_StartCapture.desktop" + else + entry=~/.config/autostart/${item}_StartCapture.desktop + fi + + cat <<- EOF > "$entry" + [Desktop Entry] + Name=${item}-StartCapture + Type=Application + Exec=$(term_exec "${item}" "~/source/RMS/Scripts/MultiCamLinux/StartCapture.sh ${item}") + Hidden=false + NoDisplay=false + Icon=${TERMINAL} + EOF + chmod +x "$entry" + + if [[ "$entry" != "${Desktop}/"* ]]; then + ln -sf "$entry" "${Desktop}/${item}_StartCapture.desktop" + gio set "${Desktop}/${item}_StartCapture.desktop" metadata::trusted true 2>/dev/null + chmod +x "${Desktop}/${item}_StartCapture.desktop" + fi + + # ShowLiveStream desktop shortcut + cat <<- EOF > "${Desktop}/${item}-ShowLiveStream.desktop" + [Desktop Entry] + Name=${item}-ShowLiveStream + Type=Application + Exec=$(term_exec "Stream-${item}" "~/source/RMS/Scripts/MultiCamLinux/LiveStream.sh ${item}") + Hidden=false + NoDisplay=false + Icon=${TERMINAL} + EOF + gio set "${Desktop}/${item}-ShowLiveStream.desktop" metadata::trusted true 2>/dev/null + chmod +x "${Desktop}/${item}-ShowLiveStream.desktop" + + # Customise the config + sed -i "s/^stationID:.*$/stationID: $item/g" "${config_file}" + sed -i "s,^data_dir:.*$,data_dir: ${RMS_data}/${item},g" "${config_file}" + + return 0 +} + +# Relocate a legacy single-camera setup into the per-station layout +migrate_legacy() { + +cat </dev/null 2>&1; then + TERMINAL=gnome-terminal +elif command -v lxterminal >/dev/null 2>&1; then + TERMINAL=lxterminal +elif command -v gnome-terminal >/dev/null 2>&1; then + TERMINAL=gnome-terminal +else + TERMINAL=lxterminal + echo "Warning: neither lxterminal nor gnome-terminal was found, launch entries will use lxterminal" +fi + +# Get user's desktop directory +Desktop=$(xdg-user-dir DESKTOP 2>/dev/null || echo "$HOME/Desktop") + +MIGRATED=0 +DefStation=$(uppercase "$(awk '/^stationID:/ {print $2}' ~/source/RMS/.config 2>/dev/null)") + +# A configured legacy single-camera setup is offered relocation first +if [[ ! -d ~/source/Stations && -n "$DefStation" && "$DefStation" != "XX0001" ]]; then + migrate_legacy +elif [[ -f ~/.rmsautorunflag && "$DefStation" == "XX0001" && ! -d ~/source/Stations ]]; then + echo "Please run RMS_FirstRun and configure your 1st station," + echo "then run add_Station again to relocate it or add more cameras." + exit +fi + +# Ensure required directories exist +mkdir -p ~/source/Stations +mkdir -p "${RMS_data}" +mkdir -p ~/.config/autostart + +if [[ -n "${CSV_FILE}" ]]; then + + echo "Reading stations from: ${CSV_FILE}" + + # Read CSV header to get column names + IFS=',' read -r -a HEADERS < "${CSV_FILE}" + + # Trim whitespace from headers + for i in "${!HEADERS[@]}"; do + HEADERS[$i]=$(trim "${HEADERS[$i]}") + done + + # Find required column indices + station_id_col=-1 + camera_ip_col=-1 + for i in "${!HEADERS[@]}"; do + case "${HEADERS[$i]}" in + station_id|stationID) station_id_col=$i ;; + camera_ip) camera_ip_col=$i ;; + esac + done + + if [[ $station_id_col -eq -1 ]]; then + echo "Error: CSV must have a 'station_id' or 'stationID' column" + exit 1 + fi + + No_Stations=$(tail -n +2 "${CSV_FILE}" | grep -c -v '^[[:space:]]*$') + echo -e "\nStations to add from CSV: ${No_Stations}" + + # Warn when the CSV would exceed the recommended camera limit + if [[ $RECOMMENDED -gt 0 && $(( $(count_stations) + No_Stations )) -gt $RECOMMENDED ]]; then + echo "" + echo "This would exceed the recommended maximum of ${RECOMMENDED} camera(s) for this platform" + echo "(the tested limit - more cameras may drop frames during capture and processing)." + read -n1 -r -p 'Press Y to continue anyway, any other key to quit: ' key + echo "" + if [[ "$key" != "y" && "$key" != "Y" ]]; then + exit + fi + fi + + # Process each data row (the || keeps a final line without a trailing newline) + tail -n +2 "${CSV_FILE}" | while IFS=',' read -r -a VALUES || [[ ${#VALUES[@]} -gt 0 ]]; do + # Skip empty lines + [[ ${#VALUES[@]} -eq 0 ]] && continue + + # Trim whitespace from values + for i in "${!VALUES[@]}"; do + VALUES[$i]=$(trim "${VALUES[$i]}") + done + + item="${VALUES[$station_id_col]}" + [[ -z "$item" ]] && continue + + create_station "$item" || continue + + config_file=~/source/Stations/${item}/.config + + # Apply camera_ip to device URL if column exists + if [[ $camera_ip_col -ne -1 && -n "${VALUES[$camera_ip_col]}" ]]; then + camera_ip="${VALUES[$camera_ip_col]}" + # Replace IP address in the device URL (matches IP pattern in rtsp:// URL) + sed -i -E "s|^(device:.*rtsp://)[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+|\1${camera_ip}|g" "${config_file}" + echo " Set camera IP: ${camera_ip}" + fi + + # Apply any additional CSV columns that match .config keys + for i in "${!HEADERS[@]}"; do + header="${HEADERS[$i]}" + value="${VALUES[$i]}" + + # Skip station_id, camera_ip (already handled), and empty values + [[ "$header" == "station_id" || "$header" == "stationID" || "$header" == "camera_ip" ]] && continue + [[ -z "$value" ]] && continue + + # Check if this header exists as a key in .config + if grep -q "^${header}:" "${config_file}"; then + sed -i "s|^${header}:.*$|${header}: ${value}|g" "${config_file}" + echo " Set ${header}: ${value}" + fi + done + + echo "Added station ${item}" + done + +else + + # No CSV available - prompt for station IDs interactively + declare -a Station + while : + do + # Advise when the recommended camera limit is reached, allow override + if [[ $RECOMMENDED -gt 0 && $(( $(count_stations) + ${#Station[@]} )) -ge $RECOMMENDED ]]; then + echo "" + echo "The recommended maximum of ${RECOMMENDED} camera(s) for this platform is configured" + echo "(the tested limit - more cameras may drop frames during capture and processing)." + read -n1 -r -p 'Press ENTER to finish, or A to add another camera anyway: ' key + echo "" + if [[ "$key" != "a" && "$key" != "A" ]]; then + break + fi + fi + read -p "Enter station ID, to end: " this_Station + this_Station=$(uppercase "$(trim "$this_Station")") + if [[ -z "$this_Station" ]]; then + break + fi + Station+=("$this_Station") + done + + if [[ ${#Station[@]} -gt 0 ]]; then + echo -e "\nNew stations to add -" + printf '%s\n' "${Station[@]}" + fi + + for item in "${Station[@]}"; do + create_station "$item" && echo "Added station ${item}" + done + +fi + +# -------------------------------------------------- per-platform settings + +total=$(count_stations) + +if [[ $total -gt 1 ]]; then + for dir in ~/source/Stations/*/; do + # scale the reserved free space with the number of stations + sed -i "s/^extra_space_gb:.*$/extra_space_gb: $(( total * 20 ))/g" "${dir}/.config" + # disable the daily post processing reboot, it would kill the other captures + sed -i "s/^\(reboot_after_processing:\).*/\1 false/g" "${dir}/.config" + done + + # remove comment from last line of wayfire.ini to enable window cascade (Pi image) + if [[ -f ~/.config/wayfire.ini ]]; then + sed -i s/#mode/mode/ ~/.config/wayfire.ini + fi +fi + +# On FirstRun-managed systems (the Pi images) captures are started by +# RMS_StartCapture_MCP.sh after the boot-time update +if [[ -f ~/.rmsautorunflag ]]; then + rm -f "${Desktop}/RMS_StartCapture.sh" + ln -s ~/source/RMS/Scripts/MultiCamLinux/Pi/RMS_StartCapture_MCP.sh "${Desktop}/RMS_StartCapture.sh" +fi + +# Generate SSH keys if not present +if [[ ! -f ~/.ssh/id_rsa ]]; then + ssh-keygen -t rsa -f ~/.ssh/id_rsa -q -P "" + cp ~/.ssh/id_rsa.pub "${Desktop}/" + echo "SSH keys generated in ~/.ssh" + echo "Your new id_rsa.pub public key file is now placed on the Desktop." + echo "Be sure to send a copy of this file to Denis" +fi + +# Add RMS activation to .bashrc if not present +if ! grep -q "source ~/vRMS/bin/activate" ~/.bashrc; then + cat <> ~/.bashrc + +# Activate RMS +cd ~/source/RMS +source ~/vRMS/bin/activate +EOF +fi + +# Create CMNbinViewer desktop shortcut +cat <<- EOF > "${Desktop}/CMNbinViewer.desktop" +[Desktop Entry] +Name=CMNbinViewer +Type=Application +Exec=${HOME}/source/RMS/Scripts/CMNbinViewer_env.sh +Hidden=false +NoDisplay=false +Icon=${HOME}/source/RMS/Scripts/MultiCamLinux/icon.png +EOF +gio set "${Desktop}/CMNbinViewer.desktop" metadata::trusted true 2>/dev/null +chmod +x "${Desktop}/CMNbinViewer.desktop" + +# Create Desktop shortcut for editing the station .config files +cat << 'EOF' > "${Desktop}/Edit_configs" +#!/bin/bash + +echo "" +echo "One by one, each config file under ~/source/Stations will open for editing..." +read -n1 -r -p 'Press ENTER to proceed, any other key to quit: ' key +if [[ "${key}" != "" ]]; then + exit +fi + +editor="" +for e in mousepad leafpad gedit gnome-text-editor nano; do + if command -v "$e" >/dev/null 2>&1; then + editor="$e" + break + fi +done + +for Dir in ~/source/Stations/*/ +do + Station=$(basename "${Dir}") + echo "" + echo "Editing .config file for ${Station}" + read -n1 -r -p 'Press ENTER to edit, any other key to skip: ' key + if [[ "${key}" = "" ]]; then + "$editor" ~/source/Stations/${Station}/.config + fi +done + +echo "" +echo "Done editing .config files" +sleep 2 +EOF +chmod +x "${Desktop}/Edit_configs" + +# After a relocation, replace the now station-specific .config in +# ~/source/RMS with a pristine default. This happens last so stations +# added in the same run used the configured one as their template. +if [[ $MIGRATED -eq 1 ]]; then + restore_default .config +fi + +echo -e "\nStation configuration complete" diff --git a/Scripts/RMS_FirstRun.sh b/Scripts/RMS_FirstRun.sh index 56f076f71..11d478d39 100755 --- a/Scripts/RMS_FirstRun.sh +++ b/Scripts/RMS_FirstRun.sh @@ -25,18 +25,18 @@ RMSUPDATESCRIPT=~/Desktop/RMS_Update.sh # Function for editing the RMS config editRMSConfig () { - # If leafpad is not available (Raspbian Jessie), use mousepad (Raspbian Buster) - if [ $( command -v leafpad ) ]; then + # Use the first available editor (leafpad on Raspbian Jessie, mousepad on + # Raspbian Buster and later, gedit/gnome-text-editor on GNOME desktops) + local editor + for editor in leafpad mousepad gedit gnome-text-editor nano; do + if [ $( command -v $editor ) ]; then - # Open the config file - leafpad $RMSCONFIG + # Open the config file + $editor $RMSCONFIG + return - else - - # Open the config file - mousepad $RMSCONFIG - - fi + fi + done } @@ -122,6 +122,9 @@ echo "" echo "At the end of these steps the first data capture session will start." echo "" +# Expanding the file system only applies to Raspberry Pi SD card images +if command -v raspi-config >/dev/null 2>&1; then + echo " 0) Expanding the file system ---------------------------- @@ -133,12 +136,12 @@ read -n1 -r -p 'If you have flashed this SD card yourself, press ENTER.' key if [[ "$key" = "" ]]; then lxterminal -e "sudo raspi-config" - + echo " Another window has opened where you can expand the file system. - + Go to: - + 7 ADVANCED OPTIONS --> A1 EXPAND FILE SYSTEM --> < OK > @@ -152,6 +155,8 @@ if [[ "$key" = "" ]]; then read -p "Press ENTER to continue..." fi +fi # raspi-config available + echo "" echo "" @@ -251,9 +256,9 @@ else fi done -# Offer the conversion to the multi-camera data structure. The conversion -# script only supports 64-bit Pi platforms, so skip this step elsewhere. -if [[ $(uname -m) == aarch64 ]]; then +# Offer the conversion to the multi-camera data structure, unless the +# system already uses it +if [[ ! -d ~/source/Stations ]]; then echo " 5) Converting the data structure @@ -283,29 +288,22 @@ fi if [[ "$key" = "" ]]; then echo "" - echo "Next, this script will run to convert to the recommended data structure:" - echo " ~/source/RMS/Scripts/MultiCamLinux/Pi/add_Pi_Station.sh" + echo "Next, this script will run to convert to the recommended data structure" + echo "(it will show the details and ask you to confirm):" + echo " ~/source/RMS/Scripts/MultiCamLinux/add_Station.sh" echo "" - echo "Note: if you have a Pi5 or similar, you can run the same script later to add more cameras." + echo "Note: you can run the same script later to add more cameras." echo "" sleep 2 - read -n1 -r -p 'Press ENTER a second time to confirm the conversion, or Q to keep the legacy structure... ' key -fi -echo "" - -if [[ "$key" = "" ]]; then - echo "" - echo "Converting to the recommended data structure..." - echo "" - bash ~/source/RMS/Scripts/MultiCamLinux/Pi/add_Pi_Station.sh + bash ~/source/RMS/Scripts/MultiCamLinux/add_Station.sh else echo "" echo "You have chosen to keep the legacy data structure." echo "If you decide to convert in the future, run this command from a terminal:" - echo " ~/source/RMS/Scripts/MultiCamLinux/Pi/add_Pi_Station.sh" + echo " ~/source/RMS/Scripts/MultiCamLinux/add_Station.sh" fi -fi # if aarch64 +fi # if no ~/source/Stations yet # Enable autorun. This is done only after the optional data structure # conversion so an interrupted conversion does not get skipped over by From 0ec29a70d89fc31a681ec30bf2ea55184c6e9477 Mon Sep 17 00:00:00 2001 From: Luc Busquin <133058544+Cybis320@users.noreply.github.com> Date: Wed, 10 Jun 2026 00:37:48 -0700 Subject: [PATCH 04/17] Prompt for station ID and coordinates directly in RMS_FirstRun Instead of opening the whole .config in a GUI editor (leafpad, mousepad, or whatever the desktop provides) and grep-checking afterwards that the station ID was changed, FirstRun now asks for the four values it needs - station ID, latitude, longitude, elevation - right at the terminal, validates them (GMN code format with an override, numeric ranges for the coordinates), shows a summary to confirm or re-enter, and writes them into .config with anchored seds. EOF on any prompt aborts the script instead of looping. The RMS_config desktop shortcut remains the way to edit any other setting. --- Scripts/RMS_FirstRun.sh | 111 ++++++++++++++++++++++++---------------- 1 file changed, 67 insertions(+), 44 deletions(-) diff --git a/Scripts/RMS_FirstRun.sh b/Scripts/RMS_FirstRun.sh index 11d478d39..7ece49674 100755 --- a/Scripts/RMS_FirstRun.sh +++ b/Scripts/RMS_FirstRun.sh @@ -22,22 +22,10 @@ RMSSTARTCAPTURE=~/Desktop/RMS_StartCapture.sh RMSUPDATESCRIPT=~/Desktop/RMS_Update.sh -# Function for editing the RMS config -editRMSConfig () { - - # Use the first available editor (leafpad on Raspbian Jessie, mousepad on - # Raspbian Buster and later, gedit/gnome-text-editor on GNOME desktops) - local editor - for editor in leafpad mousepad gedit gnome-text-editor nano; do - if [ $( command -v $editor ) ]; then - - # Open the config file - $editor $RMSCONFIG - return - - fi - done - +# Check that a value is a number within the given range +isNumberInRange () { # $1 = value, $2 = min, $3 = max + [[ "$1" =~ ^-?[0-9]+(\.[0-9]+)?$ ]] || return 1 + awk -v v="$1" -v lo="$2" -v hi="$3" 'BEGIN { exit !(v >= lo && v <= hi) }' } @@ -116,7 +104,7 @@ echo " 0. Expand the file system (if you flashed this SD card yourself)." echo " 1. Connect your Pi to the Internet. " echo " 2. Change the default password for security reasons." echo " 3. Generate a new SSH key." -echo " 4. Edit the RMS config file. " +echo " 4. Enter the station ID and the geo coordinates of your camera." echo " 5. Convert to the multi-camera data structure (recommended)." echo "" echo "At the end of these steps the first data capture session will start." @@ -215,46 +203,81 @@ echo "Updating to the latest version of RMS..." bash $RMSUPDATESCRIPT echo "" -echo "4) Editing the configuration file" -echo "---------------------------------" -echo "Finally, edit the RMS configuration file." -echo "Put in the station code/ID and the geo coordinates of your camera, save the" -echo "file and you are good to go!" +echo "4) Station configuration" +echo "------------------------" +echo "Enter the station ID and the geo coordinates of your camera." echo "" echo " -If you need to make changes to the configuration file in the future, you can -find a shortcut to it on desktop (RMS_config), or open it directly in -$RMSCONFIG. +If you need to change any other setting in the future, you can find a +shortcut to the configuration file on desktop (RMS_config), or open it +directly in $RMSCONFIG. " -read -p "The configuration file will open for editing after you press ENTER..." - - -editRMSConfig - - -echo "" +while true; do + # Station ID + while true; do + read -p "Station ID (e.g. US01AB): " statID || exit 1 + statID=$(echo "$statID" | xargs | tr '[:lower:]' '[:upper:]') + if [[ "$statID" =~ ^[A-Z]{2}[A-Z0-9]{4}$ && "$statID" != "XX0001" ]]; then + break + fi + echo " A station code has 2 country letters followed by 4 characters, e.g. US01AB." + if [[ -n "$statID" ]]; then + read -n1 -r -p " Use '$statID' anyway? Press Y to accept it, any other key to retype... " key + echo "" + if [[ "$key" = "y" || "$key" = "Y" ]]; then + break + fi + fi + done + # Latitude + while true; do + read -p "Latitude (+N, in degrees, e.g. 40.689298): " lat || exit 1 + lat=$(echo "$lat" | xargs) + isNumberInRange "$lat" -90 90 && break + echo " The latitude must be a number between -90 and 90." + done -while true; do + # Longitude + while true; do + read -p "Longitude (+E, in degrees, NEGATIVE in the western hemisphere, e.g. -74.044479): " lon || exit 1 + lon=$(echo "$lon" | xargs) + isNumberInRange "$lon" -180 180 && break + echo " The longitude must be a number between -180 and 180." + done -# Check if the config file was changed -statID=$(grep stationID $RMSCONFIG | cut -d ":" -f 2 | xargs) + # Elevation + while true; do + read -p "Elevation (mean sea level, in meters, NOT feet): " elev || exit 1 + elev=$(echo "$elev" | xargs) + isNumberInRange "$elev" -500 9000 && break + echo " The elevation must be a number in meters, e.g. 95.3." + done + echo " +Station ID: $statID +Latitude: $lat +Longitude: $lon +Elevation: $elev m +" + read -n1 -r -p 'Press ENTER to save these values, or R to re-enter them... ' key + echo "" + if [[ "$key" = "" ]]; then + break + fi -if [ "$statID" = "XX0001" ]; then - - echo "The config file was not changed!" - echo "Please change the station ID and the geo coordinates in the config file!" +done - editRMSConfig +# Write the values into the config file +sed -i "s/^stationID:.*$/stationID: $statID/" $RMSCONFIG +sed -i "s/^latitude:.*$/latitude: $lat/" $RMSCONFIG +sed -i "s/^longitude:.*$/longitude: $lon/" $RMSCONFIG +sed -i "s/^elevation:.*$/elevation: $elev/" $RMSCONFIG -else - break -fi -done +echo "Configuration saved to $RMSCONFIG" # Offer the conversion to the multi-camera data structure, unless the # system already uses it From d26f89d2e18a7542b36c5ac8726ae958fce404b8 Mon Sep 17 00:00:00 2001 From: Luc Busquin <133058544+Cybis320@users.noreply.github.com> Date: Wed, 10 Jun 2026 00:51:22 -0700 Subject: [PATCH 05/17] Clean up inherited quirks in the first-run and capture scripts - RMS_FirstRun no longer overwrites an existing SSH key: the public half is already on file with GMN, so regenerating it on a re-run would silently break the data uploads. The Desktop link uses ln -sf so a re-run does not error either - The successful autorun path exits 0 instead of 1 - The updater is invoked directly from Scripts/ instead of through the Desktop symlink (the StartCapture symlink stays, it is retargeted by add_Station.sh on purpose) - The internet check falls back to an HTTP probe of github.com when ping is blocked, since GitHub is what the update needs to reach anyway - The init message no longer promises 10 seconds while sleeping 12 - Prompts that accepted any key to skip no longer claim Q is special - Removed the unused delay_by_station_sequence() from StartCapture.sh --- Scripts/MultiCamLinux/StartCapture.sh | 13 -------- Scripts/MultiCamLinux/add_Station.sh | 2 +- Scripts/RMS_FirstRun.sh | 44 ++++++++++++++++++--------- 3 files changed, 30 insertions(+), 29 deletions(-) diff --git a/Scripts/MultiCamLinux/StartCapture.sh b/Scripts/MultiCamLinux/StartCapture.sh index 066c5730c..2f34bdf34 100755 --- a/Scripts/MultiCamLinux/StartCapture.sh +++ b/Scripts/MultiCamLinux/StartCapture.sh @@ -25,19 +25,6 @@ # second command line arg is optional, can be used to adjust the sleep time in seconds # so that there can be a delay between starting each station -delay_by_station_sequence() { - -# implement a delay based on position of station in directory -# this is not used presently - -delay=$(ls /home/$(whoami)/source/Stations/ | grep -n $1 | cut -d':' -f1) -delay=$((delay*30)) -echo Additional $delay second delay will be added. -sleep $delay - -} - - if [[ -z "$1" ]] # called with no args then echo " No Station directory specified, quitting now" diff --git a/Scripts/MultiCamLinux/add_Station.sh b/Scripts/MultiCamLinux/add_Station.sh index 18917c376..a7ba27763 100755 --- a/Scripts/MultiCamLinux/add_Station.sh +++ b/Scripts/MultiCamLinux/add_Station.sh @@ -165,7 +165,7 @@ to ${RMS_data}/${DefStation} EOF - read -n1 -r -p 'Press ENTER to continue with the relocation, or Q to skip it: ' key + read -n1 -r -p 'Press ENTER to continue with the relocation, or any other key to skip it: ' key echo "" if [[ "$key" != "" ]]; then echo "Skipping the relocation" diff --git a/Scripts/RMS_FirstRun.sh b/Scripts/RMS_FirstRun.sh index 7ece49674..2cca72936 100755 --- a/Scripts/RMS_FirstRun.sh +++ b/Scripts/RMS_FirstRun.sh @@ -1,6 +1,6 @@ #!/bin/bash -echo "Waiting 10 seconds to init the Pi..." +echo "Waiting a few seconds for the system to initialize..." echo "" echo "IMPORTANT: RMS will first update itself." echo "Do not touch any file during the update and do not close this window." @@ -18,8 +18,10 @@ RMSCONFIG=~/source/RMS/.config # Auto run enable flag file RMSAUTORUNFILE=~/.rmsautorunflag +# The StartCapture Desktop entry is a symlink that add_Station.sh retargets +# to the multi-camera launcher after a conversion, so it is used on purpose RMSSTARTCAPTURE=~/Desktop/RMS_StartCapture.sh -RMSUPDATESCRIPT=~/Desktop/RMS_Update.sh +RMSUPDATESCRIPT=~/source/RMS/Scripts/RMS_Update.sh # Check that a value is a number within the given range @@ -45,7 +47,7 @@ else # If the configuration was done, run recording bash $RMSSTARTCAPTURE - exit 1 + exit 0 fi fi @@ -117,7 +119,7 @@ echo " 0) Expanding the file system ---------------------------- If you have bought a system that was already assembled, or the file system -has already been expanded, PRESS Q to skip this step. +has already been expanded, press any key to skip this step. " read -n1 -r -p 'If you have flashed this SD card yourself, press ENTER.' key @@ -152,11 +154,13 @@ echo "" echo "1) Internet connection" echo "----------------------" echo "Checking if connected to the Internet..." -if ping -c 1 $IP &> /dev/null +# Some networks block ping, so fall back to an HTTP check of the server +# the update needs to reach anyway +if ping -c 1 $IP &> /dev/null || wget -q --spider --timeout=10 https://github.com then echo "Success!" else - echo "The device is not connected to the internet! Please connect the device to the Internet to proceed!" + echo "The device is not connected to the internet! Please connect the device to the Internet to proceed!" read -p "Press ENTER to continue..." exit 1 fi @@ -167,7 +171,7 @@ echo "--------------------------------" echo "The default password is either 'raspberry' or 'rmsraspberry'. Please change it so nobody can connect to your Raspberry Pi and hack the computers on your network!" echo "" -read -n1 -r -p 'Press ENTER to change the password (recommended), or Q to skip this step...' key +read -n1 -r -p 'Press ENTER to change the password (recommended), or any other key to skip this step...' key if [[ "$key" = "" ]]; then passwd @@ -178,17 +182,27 @@ echo "" echo "3) Generating a new SSH key" echo "---------------------------" -read -n1 -r -p 'Press ENTER to generate the SSH key (recommended), or Q to skip this step...' key +read -n1 -r -p 'Press ENTER to generate the SSH key (recommended), or any other key to skip this step...' key if [[ "$key" = "" ]]; then - echo "" - echo "Generating a new SSH key..." - # Generate an SSH key without a passphrase - yes y | ssh-keygen -t rsa -m PEM -N "" -f ~/.ssh/id_rsa >/dev/null + if [[ -f ~/.ssh/id_rsa ]]; then + + # Never overwrite an existing key - GMN already has its public half on + # file, and replacing it would break the data uploads + echo "" + echo "An SSH key already exists in ~/.ssh, not overwriting it." + + else + echo "" + echo "Generating a new SSH key..." + + # Generate an SSH key without a passphrase + ssh-keygen -t rsa -m PEM -N "" -f ~/.ssh/id_rsa >/dev/null + fi # Link the public SSH key to desktop - ln -s ~/.ssh/id_rsa.pub ~/Desktop/id_rsa.pub + ln -sf ~/.ssh/id_rsa.pub ~/Desktop/id_rsa.pub echo "" echo "A file called id_rsa.pub appeared on Desktop, please send this file to Denis " @@ -300,12 +314,12 @@ sleep 1 # clear the input buffer while read -t 0.01; do :; done -read -n1 -r -p 'Press ENTER to convert to the new data structure, or Q to stay with the legacy structure... ' key +read -n1 -r -p 'Press ENTER to convert to the new data structure, or any other key to stay with the legacy structure... ' key if [[ "$key" != "" ]]; then echo "" echo "" echo "Are you sure you want to stay with the legacy structure?" - read -n1 -r -p 'Press Q again to confirm, or ENTER to convert to the new data structure... ' key + read -n1 -r -p 'Press any key to confirm, or ENTER to convert to the new data structure... ' key echo "" fi From 78f5893eff15bbfa0ca5b163928674187ad7a0e1 Mon Sep 17 00:00:00 2001 From: Luc Busquin <133058544+Cybis320@users.noreply.github.com> Date: Wed, 10 Jun 2026 00:55:15 -0700 Subject: [PATCH 06/17] Add stations_template.csv as a sample for bulk station creation The template shows the CSV format add_Station.sh reads: station_id, camera_ip, and any .config key as further columns (capture_wait_seconds here, which staggers the capture start across cameras). Copy it to ~/source/RMS/stations.csv (gitignored) and edit. The script's usage and error messages point to it. --- Scripts/MultiCamLinux/add_Station.sh | 4 +++- stations_template.csv | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 stations_template.csv diff --git a/Scripts/MultiCamLinux/add_Station.sh b/Scripts/MultiCamLinux/add_Station.sh index a7ba27763..e9a303fc1 100755 --- a/Scripts/MultiCamLinux/add_Station.sh +++ b/Scripts/MultiCamLinux/add_Station.sh @@ -31,7 +31,8 @@ # (spliced into the rtsp device URL), and any other .config key, which is # applied verbatim to that station's config. With no CSV argument the # script uses ~/source/RMS/stations.csv if present, otherwise it prompts -# for station IDs interactively. +# for station IDs interactively. A sample to copy and edit is provided in +# ~/source/RMS/stations_template.csv. # # On Raspberry Pi platforms a recommended camera limit applies (1 camera, # or 4 on a Pi5 or later). The limit is advisory - it reflects what has @@ -243,6 +244,7 @@ fi if [[ -n "${CSV_FILE}" && ! -f "${CSV_FILE}" ]]; then echo "Error: CSV file not found: ${CSV_FILE}" echo "Usage: $0 [RMS_data_path] [stations.csv]" + echo "A sample to copy and edit is provided in ~/source/RMS/stations_template.csv" exit 1 fi diff --git a/stations_template.csv b/stations_template.csv new file mode 100644 index 000000000..24345f151 --- /dev/null +++ b/stations_template.csv @@ -0,0 +1,7 @@ +station_id,camera_ip,capture_wait_seconds +XX0001,192.168.42.101,0 +XX0002,192.168.42.102,30 +XX0003,192.168.42.103,60 +XX0004,192.168.42.104,90 +XX0005,192.168.42.105,120 +XX0006,192.168.42.106,150 From 919061204c43d87a636bc2fb33a4c4a8d7c86bad Mon Sep 17 00:00:00 2001 From: Luc Busquin <133058544+Cybis320@users.noreply.github.com> Date: Wed, 10 Jun 2026 01:04:18 -0700 Subject: [PATCH 07/17] Fix capture startup for FirstRun conversions on PCs The XDG-autostart-vs-Desktop-entry choice keyed on the autorun flag alone, but a PC that converts through RMS_FirstRun also carries that flag - and nothing launches FirstRun at boot on a PC, so such systems ended up with no automatic capture start at all. The Pi-image behavior (Desktop entry only, MCP starts captures at boot) now additionally requires Pi hardware; everything else gets autostart entries. The Desktop RMS_StartCapture.sh symlink is retargeted to the multi-camera launcher unconditionally - after any conversion the legacy script it pointed at would read the reset template config. Also create ~/.config/autostart and the Desktop dir inside create_station: the migration path runs before the main-flow mkdirs, so the autostart entry write could fail silently. --- Scripts/MultiCamLinux/add_Station.sh | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/Scripts/MultiCamLinux/add_Station.sh b/Scripts/MultiCamLinux/add_Station.sh index e9a303fc1..5f305cb7a 100755 --- a/Scripts/MultiCamLinux/add_Station.sh +++ b/Scripts/MultiCamLinux/add_Station.sh @@ -89,6 +89,7 @@ create_station() { echo "Creating station ${item}..." mkdir -p ~/source/Stations/${item} mkdir -p "${RMS_data}/${item}" + mkdir -p ~/.config/autostart "${Desktop}" # Copy config template cp ~/source/RMS/.config ~/source/Stations/${item} @@ -96,11 +97,13 @@ create_station() { # Copy mask file cp ~/source/RMS/mask.bmp ~/source/Stations/${item} 2>/dev/null - # Launch entry for the station. On FirstRun-managed systems (the Pi - # images) captures are started by RMS_StartCapture_MCP.sh, so only a - # Desktop launcher is created; elsewhere the entry goes into XDG - # autostart with a Desktop symlink, so captures start at login. - if [[ -f ~/.rmsautorunflag ]]; then + # Launch entry for the station. On the Pi images, FirstRun runs at boot + # and starts captures through RMS_StartCapture_MCP.sh, so only a Desktop + # launcher is created there; elsewhere the entry goes into XDG autostart + # with a Desktop symlink, so captures start at login. (A PC may carry the + # autorun flag from running FirstRun, but nothing launches FirstRun at + # boot on a PC, hence the additional Pi check.) + if [[ -f ~/.rmsautorunflag && $RECOMMENDED -gt 0 ]]; then entry="${Desktop}/${item}_StartCapture.desktop" else entry=~/.config/autostart/${item}_StartCapture.desktop @@ -438,12 +441,12 @@ if [[ $total -gt 1 ]]; then fi fi -# On FirstRun-managed systems (the Pi images) captures are started by -# RMS_StartCapture_MCP.sh after the boot-time update -if [[ -f ~/.rmsautorunflag ]]; then - rm -f "${Desktop}/RMS_StartCapture.sh" - ln -s ~/source/RMS/Scripts/MultiCamLinux/Pi/RMS_StartCapture_MCP.sh "${Desktop}/RMS_StartCapture.sh" -fi +# Point the Desktop StartCapture entry at the multi-camera launcher, which +# starts every configured station. The legacy entry it replaces reads +# ~/source/RMS/.config, which after a conversion is just the template. +# On the Pi images FirstRun also runs this launcher at boot. +rm -f "${Desktop}/RMS_StartCapture.sh" +ln -sf ~/source/RMS/Scripts/MultiCamLinux/Pi/RMS_StartCapture_MCP.sh "${Desktop}/RMS_StartCapture.sh" # Generate SSH keys if not present if [[ ! -f ~/.ssh/id_rsa ]]; then From 02f77aa4c9b715c35ae1d33c4f1979b303763c5f Mon Sep 17 00:00:00 2001 From: Luc Busquin <133058544+Cybis320@users.noreply.github.com> Date: Wed, 10 Jun 2026 01:12:59 -0700 Subject: [PATCH 08/17] Move add_Station.sh up to Scripts/ A universal tool does not belong in the MultiCamLinux subdirectory; it now sits next to RMS_Installer.sh and RMS_FirstRun.sh. The runtime launchers (StartCapture.sh, LiveStream.sh, RMS_StartCapture_MCP.sh) stay in MultiCamLinux/ because desktop entries on deployed systems reference those paths. Compatibility wrappers and FirstRun updated. --- Scripts/MultiCamLinux/Pi/add_Pi_Station.sh | 2 +- Scripts/MultiCamLinux/add_GStation.sh | 2 +- Scripts/RMS_FirstRun.sh | 6 +++--- Scripts/{MultiCamLinux => }/add_Station.sh | 0 4 files changed, 5 insertions(+), 5 deletions(-) rename Scripts/{MultiCamLinux => }/add_Station.sh (100%) diff --git a/Scripts/MultiCamLinux/Pi/add_Pi_Station.sh b/Scripts/MultiCamLinux/Pi/add_Pi_Station.sh index 9e2e7180c..3670ebe85 100755 --- a/Scripts/MultiCamLinux/Pi/add_Pi_Station.sh +++ b/Scripts/MultiCamLinux/Pi/add_Pi_Station.sh @@ -1,4 +1,4 @@ #!/bin/bash # Kept for compatibility with existing documentation and older images. # The station tooling is now universal - see add_Station.sh. -exec "$(dirname "$0")/../add_Station.sh" "$@" +exec "$(dirname "$0")/../../add_Station.sh" "$@" diff --git a/Scripts/MultiCamLinux/add_GStation.sh b/Scripts/MultiCamLinux/add_GStation.sh index 1ed202c0a..03cbb7674 100755 --- a/Scripts/MultiCamLinux/add_GStation.sh +++ b/Scripts/MultiCamLinux/add_GStation.sh @@ -1,4 +1,4 @@ #!/bin/bash # Kept for compatibility with existing documentation. # The station tooling is now universal - see add_Station.sh. -exec "$(dirname "$0")/add_Station.sh" "$@" +exec "$(dirname "$0")/../add_Station.sh" "$@" diff --git a/Scripts/RMS_FirstRun.sh b/Scripts/RMS_FirstRun.sh index 2cca72936..2024af498 100755 --- a/Scripts/RMS_FirstRun.sh +++ b/Scripts/RMS_FirstRun.sh @@ -327,17 +327,17 @@ if [[ "$key" = "" ]]; then echo "" echo "Next, this script will run to convert to the recommended data structure" echo "(it will show the details and ask you to confirm):" - echo " ~/source/RMS/Scripts/MultiCamLinux/add_Station.sh" + echo " ~/source/RMS/Scripts/add_Station.sh" echo "" echo "Note: you can run the same script later to add more cameras." echo "" sleep 2 - bash ~/source/RMS/Scripts/MultiCamLinux/add_Station.sh + bash ~/source/RMS/Scripts/add_Station.sh else echo "" echo "You have chosen to keep the legacy data structure." echo "If you decide to convert in the future, run this command from a terminal:" - echo " ~/source/RMS/Scripts/MultiCamLinux/add_Station.sh" + echo " ~/source/RMS/Scripts/add_Station.sh" fi fi # if no ~/source/Stations yet diff --git a/Scripts/MultiCamLinux/add_Station.sh b/Scripts/add_Station.sh similarity index 100% rename from Scripts/MultiCamLinux/add_Station.sh rename to Scripts/add_Station.sh From b679a42d95b770e35c8ede427213703b4a84b670 Mon Sep 17 00:00:00 2001 From: Luc Busquin <133058544+Cybis320@users.noreply.github.com> Date: Wed, 10 Jun 2026 01:16:50 -0700 Subject: [PATCH 09/17] Update Scripts/README.md for the universal station tooling --- Scripts/README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Scripts/README.md b/Scripts/README.md index ecd4c6308..1cda662fb 100644 --- a/Scripts/README.md +++ b/Scripts/README.md @@ -1 +1,6 @@ -These scripts are usually kept on the desktop of the Pi and assume that RMS is installed in ~/source/RMS. +Helper scripts for running a meteor station. All of them assume that RMS is installed in `~/source/RMS`. + +- Most scripts here (`RMS_FirstRun.sh`, `RMS_StartCapture.sh`, `RMS_Update.sh`, ...) are linked on the desktop of a station and run from there. +- `RMS_Installer.sh` installs RMS and everything it needs on a generic Linux machine (Ubuntu LTS or Debian). +- `add_Station.sh` creates camera stations under `~/source/Stations/` — interactively, or in bulk from a CSV file (see `stations_template.csv` in the repository root). It also relocates a legacy single-camera setup into that layout. Works on any Linux platform, Raspberry Pi included. +- `MultiCamLinux/` holds the per-station runtime launchers (`StartCapture.sh`, `LiveStream.sh`, `Pi/RMS_StartCapture_MCP.sh`) plus compatibility wrappers for the older `add_GStation.sh` and `add_Pi_Station.sh` entry points. From 65e1889155bf2ad0fe7b9c39b8d9ed95ead4053c Mon Sep 17 00:00:00 2001 From: Luc Busquin <133058544+Cybis320@users.noreply.github.com> Date: Wed, 10 Jun 2026 01:32:47 -0700 Subject: [PATCH 10/17] Make the conversion copy-only: add_Station never modifies ~/source/RMS The legacy conversion used to mutate the repo working tree: it deleted the root .config and re-downloaded it from GitHub (leaving no config at all when offline), moved mask.bmp and platepar out, and the camera_settings.json move had been proposed too. None of that is the station tool's business - RMS_Update already owns the lifecycle of the root .config/mask.bmp/camera_settings.json (backup, reset, restore), and camera_settings.json is shared by all stations from the root by default (every station's camera_settings_path points there). The conversion now only COPIES .config, mask.bmp and platepar into ~/source/Stations/ and moves the captured data under ~/RMS_data/. Nothing in ~/source/RMS is created, modified or deleted - no git commands, no downloads. The root files keep the operator's values and remain the templates for adding further stations. --- Scripts/README.md | 2 +- Scripts/RMS_FirstRun.sh | 8 +++--- Scripts/add_Station.sh | 58 ++++++++++++----------------------------- 3 files changed, 21 insertions(+), 47 deletions(-) diff --git a/Scripts/README.md b/Scripts/README.md index 1cda662fb..fc7b27a25 100644 --- a/Scripts/README.md +++ b/Scripts/README.md @@ -2,5 +2,5 @@ Helper scripts for running a meteor station. All of them assume that RMS is inst - Most scripts here (`RMS_FirstRun.sh`, `RMS_StartCapture.sh`, `RMS_Update.sh`, ...) are linked on the desktop of a station and run from there. - `RMS_Installer.sh` installs RMS and everything it needs on a generic Linux machine (Ubuntu LTS or Debian). -- `add_Station.sh` creates camera stations under `~/source/Stations/` — interactively, or in bulk from a CSV file (see `stations_template.csv` in the repository root). It also relocates a legacy single-camera setup into that layout. Works on any Linux platform, Raspberry Pi included. +- `add_Station.sh` creates camera stations under `~/source/Stations/` — interactively, or in bulk from a CSV file (see `stations_template.csv` in the repository root). It also converts a legacy single-camera setup to that layout (by copying — nothing in the RMS root is modified). Works on any Linux platform, Raspberry Pi included. - `MultiCamLinux/` holds the per-station runtime launchers (`StartCapture.sh`, `LiveStream.sh`, `Pi/RMS_StartCapture_MCP.sh`) plus compatibility wrappers for the older `add_GStation.sh` and `add_Pi_Station.sh` entry points. diff --git a/Scripts/RMS_FirstRun.sh b/Scripts/RMS_FirstRun.sh index 2024af498..aa991ee87 100755 --- a/Scripts/RMS_FirstRun.sh +++ b/Scripts/RMS_FirstRun.sh @@ -304,10 +304,10 @@ The new recommended data structure keeps important camera files in a safer place. Converting to this multi-camera data structure is recommended for both single and multiple camera systems. -The important files (.config, mask.bmp, platepar_cmn2010.cal and -camera_settings.json) will be moved to ~/source/Stations/, -and captured data will be stored in ~/RMS_data/, where - is the unique station code you were given by GMN. +The important files (.config, mask.bmp and platepar_cmn2010.cal) will be +copied to ~/source/Stations/, and captured data will be stored +in ~/RMS_data/, where is the unique station code +you were given by GMN. Nothing in ~/source/RMS is modified. " sleep 1 diff --git a/Scripts/add_Station.sh b/Scripts/add_Station.sh index 5f305cb7a..fbc9ccfa5 100755 --- a/Scripts/add_Station.sh +++ b/Scripts/add_Station.sh @@ -20,7 +20,7 @@ # compatibility wrappers around this script. # # What it does: -# - relocates a legacy single-camera setup (config files in ~/source/RMS, +# - converts a legacy single-camera setup (config files in ~/source/RMS, # data directly in ~/RMS_data) into the per-station layout under # ~/source/Stations/ and ~/RMS_data/ # - creates new stations, either interactively or in bulk from a CSV file @@ -52,13 +52,6 @@ uppercase() { printf '%s' "$*" | tr '[:lower:]' '[:upper:]' } -# Restore a pristine copy of a file in ~/source/RMS from git, or from -# GitHub when ~/source/RMS is not a git checkout -restore_default() { - ( cd ~/source/RMS && git checkout -- "$1" 2>/dev/null ) || \ - wget -q -O ~/source/RMS/"$1" "https://raw.githubusercontent.com/CroatianMeteorNetwork/RMS/master/$1" -} - count_stations() { find ~/source/Stations -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l } @@ -146,22 +139,22 @@ create_station() { return 0 } -# Relocate a legacy single-camera setup into the per-station layout +# Convert a legacy single-camera setup to the per-station layout by copying migrate_legacy() { cat <. + +Your configured station ${DefStation} will get copies of: - .config - platepar_cmn2010.cal - mask.bmp -- camera_settings.json -from their default location of ~/source/RMS to the folder -~/source/Stations/${DefStation} +in ~/source/Stations/${DefStation} -You have already configured your first station as id ${DefStation} -Its camera specific files will now be relocated to -~/source/Stations/${DefStation} +Nothing in ~/source/RMS is modified, and camera_settings.json is shared +by all stations from there. Any previously captured data will be moved from ${RMS_data} @@ -169,30 +162,20 @@ to ${RMS_data}/${DefStation} EOF - read -n1 -r -p 'Press ENTER to continue with the relocation, or any other key to skip it: ' key + read -n1 -r -p 'Press ENTER to continue with the conversion, or any other key to skip it: ' key echo "" if [[ "$key" != "" ]]; then - echo "Skipping the relocation" + echo "Skipping the conversion" return fi create_station "${DefStation}" || return + # the .config and mask copies are made by create_station if [[ -e ~/source/RMS/platepar_cmn2010.cal ]]; then - mv ~/source/RMS/platepar_cmn2010.cal ~/source/Stations/${DefStation}/ - fi - if [[ -e ~/source/RMS/camera_settings.json ]]; then - mv ~/source/RMS/camera_settings.json ~/source/Stations/${DefStation}/ - # point the station .config at the relocated camera settings file - sed -i "s,^camera_settings_path:.*$,camera_settings_path: ~/source/Stations/${DefStation}/camera_settings.json,g" ~/source/Stations/${DefStation}/.config + cp ~/source/RMS/platepar_cmn2010.cal ~/source/Stations/${DefStation}/ fi - # restore pristine default copies in ~/source/RMS, these are used as - # templates when additional stations are added (the user's .config is - # restored at the end, so stations added in this run inherit it) - restore_default mask.bmp - restore_default camera_settings.json - # move any existing captured data into the station folder find "${RMS_data}" -mindepth 1 -maxdepth 1 ! -name "${DefStation}" -exec mv -t "${RMS_data}/${DefStation}/" {} + @@ -210,9 +193,8 @@ EOF rm -f ~/Desktop/TunnelIPCamera.sh ~/Desktop/DownloadOpenVPNconfig.sh fi - MIGRATED=1 echo "" - echo "Relocation of station ${DefStation} complete" + echo "Conversion of station ${DefStation} complete" } # ------------------------------------------------------------------ main @@ -284,15 +266,14 @@ fi # Get user's desktop directory Desktop=$(xdg-user-dir DESKTOP 2>/dev/null || echo "$HOME/Desktop") -MIGRATED=0 DefStation=$(uppercase "$(awk '/^stationID:/ {print $2}' ~/source/RMS/.config 2>/dev/null)") -# A configured legacy single-camera setup is offered relocation first +# A configured legacy single-camera setup is offered conversion first if [[ ! -d ~/source/Stations && -n "$DefStation" && "$DefStation" != "XX0001" ]]; then migrate_legacy elif [[ -f ~/.rmsautorunflag && "$DefStation" == "XX0001" && ! -d ~/source/Stations ]]; then echo "Please run RMS_FirstRun and configure your 1st station," - echo "then run add_Station again to relocate it or add more cameras." + echo "then run add_Station again to convert it or add more cameras." exit fi @@ -516,11 +497,4 @@ sleep 2 EOF chmod +x "${Desktop}/Edit_configs" -# After a relocation, replace the now station-specific .config in -# ~/source/RMS with a pristine default. This happens last so stations -# added in the same run used the configured one as their template. -if [[ $MIGRATED -eq 1 ]]; then - restore_default .config -fi - echo -e "\nStation configuration complete" From 989372201f58e0c3c69f03320b2796dc53be8191 Mon Sep 17 00:00:00 2001 From: Luc Busquin <133058544+Cybis320@users.noreply.github.com> Date: Tue, 28 Jul 2026 11:02:17 -0700 Subject: [PATCH 11/17] Auto-detect and expand an undersized root file system in RMS_FirstRun Instead of walking the user through the raspi-config menus, measure the root file system against the device it lives on and offer a one-key expansion (raspi-config --expand-rootfs) followed by an automatic reboot. If the device is already fully used but below the 64 GB minimum, warn about it instead of offering an expansion that would do nothing. Based on Peter E.'s revised first-run proposal. --- Scripts/RMS_FirstRun.sh | 68 ++++++++++++++++++++++++++++------------- 1 file changed, 47 insertions(+), 21 deletions(-) diff --git a/Scripts/RMS_FirstRun.sh b/Scripts/RMS_FirstRun.sh index aa991ee87..5af19a261 100755 --- a/Scripts/RMS_FirstRun.sh +++ b/Scripts/RMS_FirstRun.sh @@ -102,7 +102,7 @@ read -p "" echo "" echo "This is a brief overview of this guide:" -echo " 0. Expand the file system (if you flashed this SD card yourself)." +echo " 0. Expand the file system (checked automatically)." echo " 1. Connect your Pi to the Internet. " echo " 2. Change the default password for security reasons." echo " 3. Generate a new SSH key." @@ -118,31 +118,57 @@ if command -v raspi-config >/dev/null 2>&1; then echo " 0) Expanding the file system ---------------------------- -If you have bought a system that was already assembled, or the file system -has already been expanded, press any key to skip this step. -" +Checking if the file system needs to be expanded..." + +# Compare the root file system to the device it lives on to see if there +# is unallocated space left - a freshly flashed image is much smaller +# than the SD card it was written to +fs_gb=$(( $(df --output=size -B1 / | tail -1) / 1000000000 )) +root_part=$(findmnt -n -o SOURCE / 2>/dev/null) +root_dev=$(lsblk -n -o PKNAME "$root_part" 2>/dev/null | head -1) +if [[ -n "$root_dev" ]]; then + dev_gb=$(( $(lsblk -b -d -n -o SIZE "/dev/$root_dev") / 1000000000 )) +else + dev_gb=$fs_gb +fi -read -n1 -r -p 'If you have flashed this SD card yourself, press ENTER.' key +# clear the input buffer +while read -t 0.01; do :; done -if [[ "$key" = "" ]]; then - lxterminal -e "sudo raspi-config" +if (( dev_gb - fs_gb >= 2 )); then echo " - Another window has opened where you can expand the file system. - - Go to: - - 7 ADVANCED OPTIONS --> - A1 EXPAND FILE SYSTEM --> - < OK > - - < FINISH > (at the bottom) --> - < YES > (reboot) - - Your Raspberry Pi will reboot. - " +The file system takes up ${fs_gb} GB of the ${dev_gb} GB storage device. +RMS needs the whole device for data capture: 64 GB at minimum, 128 GB or +more is recommended. The usable size is always a bit smaller than the +rated size, e.g. a 64 GB card gives about 62 GB. +" + read -n1 -r -p 'Press ENTER to expand the file system to the full device (recommended), or any other key to skip this step...' key + + if [[ "$key" = "" ]]; then + echo "" + echo "Expanding the file system, then the system will reboot." + echo "This guide will start again after the reboot." + sudo raspi-config --expand-rootfs + echo "Rebooting in 5 seconds, please wait..." + sleep 5 + sudo reboot + exit 0 + else + echo "" + echo "The file system was not expanded, so part of the storage device" + echo "will stay unused and there may not be enough room for data capture!" + fi - read -p "Press ENTER to continue..." +elif (( fs_gb < 61 )); then + echo "" + echo "The whole ${dev_gb} GB storage device is already in use, but it is smaller" + echo "than the 64 GB minimum RMS needs for data capture (128 GB or more is" + echo "recommended). Please consider moving to a larger storage device." + read -p "Press ENTER to continue anyway..." +else + echo "" + echo "${fs_gb} GB of space is available, no expansion needed." fi fi # raspi-config available From a1d321ef37625863757b7e49f389e3e83c245c2e Mon Sep 17 00:00:00 2001 From: Luc Busquin <133058544+Cybis320@users.noreply.github.com> Date: Tue, 28 Jul 2026 11:03:21 -0700 Subject: [PATCH 12/17] Make RMS_StartCapture.sh handle both data structures RMS_StartCapture.sh now detects the multi-camera structure (station directories under ~/source/Stations) and starts every configured station itself, staggered as before, falling back to the legacy single-camera start otherwise. RMS_StartCapture_MCP.sh becomes a compatibility wrapper for Desktop shortcuts created by older installs, and add_Station.sh no longer needs to retarget the Desktop entry after a conversion. Based on Peter E.'s revised proposal (RMS_StartCapture2.sh). --- .../MultiCamLinux/Pi/RMS_StartCapture_MCP.sh | 44 +--------- Scripts/README.md | 3 +- Scripts/RMS_FirstRun.sh | 6 +- Scripts/RMS_StartCapture.sh | 82 +++++++++++++++---- Scripts/add_Station.sh | 11 ++- 5 files changed, 79 insertions(+), 67 deletions(-) diff --git a/Scripts/MultiCamLinux/Pi/RMS_StartCapture_MCP.sh b/Scripts/MultiCamLinux/Pi/RMS_StartCapture_MCP.sh index d079de974..a105b5dbc 100755 --- a/Scripts/MultiCamLinux/Pi/RMS_StartCapture_MCP.sh +++ b/Scripts/MultiCamLinux/Pi/RMS_StartCapture_MCP.sh @@ -1,42 +1,6 @@ #!/bin/bash -# Start capture on all configured stations, staggered so concurrent -# first-start updates cannot collide. On a single camera system there is -# no such risk, so the long delay is skipped - -dircount=$(find ~/source/Stations -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l) -if [[ $dircount -le 1 ]]; then - seconds=2 -else - seconds=70 -fi - -# Detect the terminal emulator -if [[ "${XDG_CURRENT_DESKTOP:-}" == *GNOME* ]] && command -v gnome-terminal >/dev/null 2>&1; then - TERMINAL=gnome-terminal -elif command -v lxterminal >/dev/null 2>&1; then - TERMINAL=lxterminal -else - TERMINAL=gnome-terminal -fi - -echo " Starting all configured stations post-update..." - -loop=0 -for Dir in ~/source/Stations/*/ - do - Station=$(basename "$Dir") - echo " Starting camera ${Station}" - if [[ "$TERMINAL" == "gnome-terminal" ]]; then - gnome-terminal --profile=StartCapture --title=${Station} -- bash -c "$HOME/source/RMS/Scripts/MultiCamLinux/StartCapture.sh ${Station}" & - else - lxterminal --title=${Station} -e "$HOME/source/RMS/Scripts/MultiCamLinux/StartCapture.sh ${Station}" & - fi - echo " waiting $seconds seconds..." - sleep ${seconds} - if [[ $loop = 0 ]] ; then - seconds=10 - fi - let loop++ - done -echo " All cameras started" +# Kept for compatibility with Desktop shortcuts created by older +# installs - RMS_StartCapture.sh now handles the multi-camera data +# structure directly +exec ~/source/RMS/Scripts/RMS_StartCapture.sh "$@" diff --git a/Scripts/README.md b/Scripts/README.md index fc7b27a25..baa6d028a 100644 --- a/Scripts/README.md +++ b/Scripts/README.md @@ -3,4 +3,5 @@ Helper scripts for running a meteor station. All of them assume that RMS is inst - Most scripts here (`RMS_FirstRun.sh`, `RMS_StartCapture.sh`, `RMS_Update.sh`, ...) are linked on the desktop of a station and run from there. - `RMS_Installer.sh` installs RMS and everything it needs on a generic Linux machine (Ubuntu LTS or Debian). - `add_Station.sh` creates camera stations under `~/source/Stations/` — interactively, or in bulk from a CSV file (see `stations_template.csv` in the repository root). It also converts a legacy single-camera setup to that layout (by copying — nothing in the RMS root is modified). Works on any Linux platform, Raspberry Pi included. -- `MultiCamLinux/` holds the per-station runtime launchers (`StartCapture.sh`, `LiveStream.sh`, `Pi/RMS_StartCapture_MCP.sh`) plus compatibility wrappers for the older `add_GStation.sh` and `add_Pi_Station.sh` entry points. +- `RMS_StartCapture.sh` starts capture on either data structure: every station under `~/source/Stations/` in its own terminal, or the single legacy camera if there are none. +- `MultiCamLinux/` holds the per-station runtime launchers (`StartCapture.sh`, `LiveStream.sh`) plus compatibility wrappers for the older `add_GStation.sh`, `add_Pi_Station.sh` and `Pi/RMS_StartCapture_MCP.sh` entry points. diff --git a/Scripts/RMS_FirstRun.sh b/Scripts/RMS_FirstRun.sh index 5af19a261..593be773f 100755 --- a/Scripts/RMS_FirstRun.sh +++ b/Scripts/RMS_FirstRun.sh @@ -18,9 +18,9 @@ RMSCONFIG=~/source/RMS/.config # Auto run enable flag file RMSAUTORUNFILE=~/.rmsautorunflag -# The StartCapture Desktop entry is a symlink that add_Station.sh retargets -# to the multi-camera launcher after a conversion, so it is used on purpose -RMSSTARTCAPTURE=~/Desktop/RMS_StartCapture.sh +# RMS_StartCapture.sh handles both the legacy and the multi-camera data +# structure, so the same launcher is used before and after a conversion +RMSSTARTCAPTURE=~/source/RMS/Scripts/RMS_StartCapture.sh RMSUPDATESCRIPT=~/source/RMS/Scripts/RMS_Update.sh diff --git a/Scripts/RMS_StartCapture.sh b/Scripts/RMS_StartCapture.sh index 677e6e5b3..b501737a2 100755 --- a/Scripts/RMS_StartCapture.sh +++ b/Scripts/RMS_StartCapture.sh @@ -1,26 +1,74 @@ #!/bin/bash + +# Starts capture on either supported data structure: +# - multi-camera (~/source/Stations has station directories): every +# configured station is started in its own terminal, staggered so +# concurrent first-start updates cannot collide +# - legacy: the single camera is started in this terminal + echo "Starting RMS..." sleep 10 -source ~/vRMS/bin/activate -cd ~/source/RMS -# Init log file -LOGPATH=~/RMS_data/logs/ -LOGDATE=$(date +"%Y%m%d_%H%M%S") -LOGSUFFIX="_log.txt" -LOGFILE=$LOGPATH$LOGDATE$LOGSUFFIX +dircount=$(find ~/source/Stations -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l) + +if [[ $dircount -gt 0 ]]; then + + # On a single camera system concurrent first-start updates are no + # risk, so the long stagger delay is skipped + if [[ $dircount -le 1 ]]; then + seconds=2 + else + seconds=70 + fi + + # Detect the terminal emulator + if [[ "${XDG_CURRENT_DESKTOP:-}" == *GNOME* ]] && command -v gnome-terminal >/dev/null 2>&1; then + TERMINAL=gnome-terminal + elif command -v lxterminal >/dev/null 2>&1; then + TERMINAL=lxterminal + else + TERMINAL=gnome-terminal + fi + + echo " Starting all configured stations..." + + loop=0 + for Dir in ~/source/Stations/*/ + do + Station=$(basename "$Dir") + echo " Starting camera ${Station}" + if [[ "$TERMINAL" == "gnome-terminal" ]]; then + gnome-terminal --profile=StartCapture --title=${Station} -- bash -c "$HOME/source/RMS/Scripts/MultiCamLinux/StartCapture.sh ${Station}" & + else + lxterminal --title=${Station} -e "$HOME/source/RMS/Scripts/MultiCamLinux/StartCapture.sh ${Station}" & + fi + echo " waiting $seconds seconds..." + sleep ${seconds} + if [[ $loop = 0 ]] ; then + seconds=10 + fi + let loop++ + done + echo " All cameras started" + +else + + # Legacy single-camera data structure + source ~/vRMS/bin/activate + cd ~/source/RMS -mkdir -p $LOGPATH + mkdir -p ~/RMS_data/logs/ -echo "" -echo "" -echo "If you need to update the RMS config file, you can do it now." -echo "Any changes to the config file will be read only after this script is started again or the Pi is rebooted." -echo "" -sleep 5 + echo "" + echo "" + echo "If you need to update the RMS config file, you can do it now." + echo "Any changes to the config file will be read only after this script is started again or the Pi is rebooted." + echo "" + sleep 5 -python -m RMS.StartCapture "$@" + python -m RMS.StartCapture "$@" -read -p "Press any key to continue... " + read -p "Press any key to continue... " -$SHELL + $SHELL +fi diff --git a/Scripts/add_Station.sh b/Scripts/add_Station.sh index fbc9ccfa5..674a511d8 100755 --- a/Scripts/add_Station.sh +++ b/Scripts/add_Station.sh @@ -91,7 +91,7 @@ create_station() { cp ~/source/RMS/mask.bmp ~/source/Stations/${item} 2>/dev/null # Launch entry for the station. On the Pi images, FirstRun runs at boot - # and starts captures through RMS_StartCapture_MCP.sh, so only a Desktop + # and starts captures through RMS_StartCapture.sh, so only a Desktop # launcher is created there; elsewhere the entry goes into XDG autostart # with a Desktop symlink, so captures start at login. (A PC may carry the # autorun flag from running FirstRun, but nothing launches FirstRun at @@ -422,12 +422,11 @@ if [[ $total -gt 1 ]]; then fi fi -# Point the Desktop StartCapture entry at the multi-camera launcher, which -# starts every configured station. The legacy entry it replaces reads -# ~/source/RMS/.config, which after a conversion is just the template. +# Make sure the Desktop StartCapture entry points at RMS_StartCapture.sh, +# which handles both the legacy and the multi-camera data structure (the +# entry may be a stale copy rather than a symlink on older installs). # On the Pi images FirstRun also runs this launcher at boot. -rm -f "${Desktop}/RMS_StartCapture.sh" -ln -sf ~/source/RMS/Scripts/MultiCamLinux/Pi/RMS_StartCapture_MCP.sh "${Desktop}/RMS_StartCapture.sh" +ln -sf ~/source/RMS/Scripts/RMS_StartCapture.sh "${Desktop}/RMS_StartCapture.sh" # Generate SSH keys if not present if [[ ! -f ~/.ssh/id_rsa ]]; then From 5d50987ef067d3d32703c5ee6fbb2ea41e9b8bbd Mon Sep 17 00:00:00 2001 From: Luc Busquin <133058544+Cybis320@users.noreply.github.com> Date: Mon, 3 Aug 2026 13:17:15 -0700 Subject: [PATCH 13/17] Fix the expansion check flagging large cards as unexpanded forever --- Scripts/RMS_FirstRun.sh | 86 ++++++++++++++++++++++++++++++++++------- 1 file changed, 72 insertions(+), 14 deletions(-) diff --git a/Scripts/RMS_FirstRun.sh b/Scripts/RMS_FirstRun.sh index 593be773f..8e57783fc 100755 --- a/Scripts/RMS_FirstRun.sh +++ b/Scripts/RMS_FirstRun.sh @@ -30,6 +30,22 @@ isNumberInRange () { # $1 = value, $2 = min, $3 = max awk -v v="$1" -v lo="$2" -v hi="$3" 'BEGIN { exit !(v >= lo && v <= hi) }' } +# Strip leading/trailing whitespace. Used instead of xargs, which aborts +# with an error when the input contains a stray quote or backslash +trimSpaces () { + local s="$*" + s="${s#"${s%%[![:space:]]*}"}" + s="${s%"${s##*[![:space:]]}"}" + printf '%s' "$s" +} + +# Discard buffered keypresses (e.g. the tail of an arrow key escape +# sequence left behind by a single-key read) so they cannot pollute the +# next typed entry +flushInput () { + while read -t 0.01; do :; done +} + # If the autorun file does not exist, create it and run the configuration @@ -120,22 +136,36 @@ echo " ---------------------------- Checking if the file system needs to be expanded..." -# Compare the root file system to the device it lives on to see if there -# is unallocated space left - a freshly flashed image is much smaller -# than the SD card it was written to +# Compare the root file system to the partition and the device it lives +# on. A freshly flashed image is much smaller than the SD card it was +# written to: expanding first grows the partition to fill the device +# (raspi-config + reboot), then a boot-time resize2fs grows the file +# system to fill the partition. On large cards that second step can still +# be running in the background when this guide starts again after the +# reboot, so the two are checked separately. fs_gb=$(( $(df --output=size -B1 / | tail -1) / 1000000000 )) root_part=$(findmnt -n -o SOURCE / 2>/dev/null) root_dev=$(lsblk -n -o PKNAME "$root_part" 2>/dev/null | head -1) +part_gb=$(( $(lsblk -b -d -n -o SIZE "$root_part" 2>/dev/null || echo 0) / 1000000000 )) +if (( part_gb == 0 )); then + part_gb=$fs_gb +fi if [[ -n "$root_dev" ]]; then dev_gb=$(( $(lsblk -b -d -n -o SIZE "/dev/$root_dev") / 1000000000 )) else - dev_gb=$fs_gb + dev_gb=$part_gb fi +# The file system reported by df is always a few percent smaller than its +# partition (ext4 keeps inode tables and a journal), and that overhead +# grows with the card size - so the comparison slack must scale too, or +# large cards get flagged as unexpanded forever +slack_gb=$(( part_gb / 20 + 2 )) + # clear the input buffer while read -t 0.01; do :; done -if (( dev_gb - fs_gb >= 2 )); then +if (( dev_gb - part_gb >= 2 )); then echo " The file system takes up ${fs_gb} GB of the ${dev_gb} GB storage device. @@ -160,6 +190,28 @@ rated size, e.g. a 64 GB card gives about 62 GB. echo "will stay unused and there may not be enough room for data capture!" fi +elif (( part_gb - fs_gb >= slack_gb )); then + + # The partition already fills the device but the file system does not, + # so the resize scheduled by raspi-config is still running (or failed) + echo " +The file system is still being expanded to fill the ${dev_gb} GB storage +device - on large cards this takes a few minutes after the resize reboot. +Waiting for it to finish..." + while pgrep -x resize2fs >/dev/null 2>&1; do + printf '.' + sleep 2 + done + echo "" + fs_gb=$(( $(df --output=size -B1 / | tail -1) / 1000000000 )) + if (( part_gb - fs_gb >= slack_gb )); then + # The scheduled resize never ran or did not finish the job - + # resize directly, which is safe online on a mounted ext4 root + sudo resize2fs "$root_part" + fs_gb=$(( $(df --output=size -B1 / | tail -1) / 1000000000 )) + fi + echo "${fs_gb} GB of space is now available." + elif (( fs_gb < 61 )); then echo "" echo "The whole ${dev_gb} GB storage device is already in use, but it is smaller" @@ -258,13 +310,16 @@ while true; do # Station ID while true; do - read -p "Station ID (e.g. US01AB): " statID || exit 1 - statID=$(echo "$statID" | xargs | tr '[:lower:]' '[:upper:]') + flushInput + read -r -p "Station ID (e.g. US01AB): " statID || exit 1 + statID=$(trimSpaces "$statID" | tr '[:lower:]' '[:upper:]') if [[ "$statID" =~ ^[A-Z]{2}[A-Z0-9]{4}$ && "$statID" != "XX0001" ]]; then break fi echo " A station code has 2 country letters followed by 4 characters, e.g. US01AB." - if [[ -n "$statID" ]]; then + # Only offer to keep a nonstandard entry when it is still safe to use + # as a directory name and a .config value + if [[ "$statID" =~ ^[A-Z0-9_-]+$ ]]; then read -n1 -r -p " Use '$statID' anyway? Press Y to accept it, any other key to retype... " key echo "" if [[ "$key" = "y" || "$key" = "Y" ]]; then @@ -275,24 +330,27 @@ while true; do # Latitude while true; do - read -p "Latitude (+N, in degrees, e.g. 40.689298): " lat || exit 1 - lat=$(echo "$lat" | xargs) + flushInput + read -r -p "Latitude (+N, in degrees, e.g. 40.689298): " lat || exit 1 + lat=$(trimSpaces "$lat") isNumberInRange "$lat" -90 90 && break echo " The latitude must be a number between -90 and 90." done # Longitude while true; do - read -p "Longitude (+E, in degrees, NEGATIVE in the western hemisphere, e.g. -74.044479): " lon || exit 1 - lon=$(echo "$lon" | xargs) + flushInput + read -r -p "Longitude (+E, in degrees, NEGATIVE in the western hemisphere, e.g. -74.044479): " lon || exit 1 + lon=$(trimSpaces "$lon") isNumberInRange "$lon" -180 180 && break echo " The longitude must be a number between -180 and 180." done # Elevation while true; do - read -p "Elevation (mean sea level, in meters, NOT feet): " elev || exit 1 - elev=$(echo "$elev" | xargs) + flushInput + read -r -p "Elevation (mean sea level, in meters, NOT feet): " elev || exit 1 + elev=$(trimSpaces "$elev") isNumberInRange "$elev" -500 9000 && break echo " The elevation must be a number in meters, e.g. 95.3." done From bf655e99b76acf9d95d83dd0f84f81dd847fa6f8 Mon Sep 17 00:00:00 2001 From: Luc Busquin <133058544+Cybis320@users.noreply.github.com> Date: Mon, 3 Aug 2026 13:17:15 -0700 Subject: [PATCH 14/17] Validate station ID entries and harden the prompts against stray input --- Scripts/add_Station.sh | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/Scripts/add_Station.sh b/Scripts/add_Station.sh index 674a511d8..deb37fc58 100755 --- a/Scripts/add_Station.sh +++ b/Scripts/add_Station.sh @@ -337,6 +337,12 @@ if [[ -n "${CSV_FILE}" ]]; then item="${VALUES[$station_id_col]}" [[ -z "$item" ]] && continue + # reject IDs that would break directory names or the sed edits + if [[ ! "$item" =~ ^[A-Za-z0-9_-]+$ ]]; then + echo "Skipping invalid station ID from CSV: ${item}" + continue + fi + create_station "$item" || continue config_file=~/source/Stations/${item}/.config @@ -385,11 +391,28 @@ else break fi fi - read -p "Enter station ID, to end: " this_Station + # drop buffered keypresses (e.g. the tail of an arrow key escape + # sequence left by a single-key read) before taking a typed entry + while read -t 0.01; do :; done + read -r -p "Enter station ID, to end: " this_Station this_Station=$(uppercase "$(trim "$this_Station")") if [[ -z "$this_Station" ]]; then break fi + if [[ ! "$this_Station" =~ ^[A-Z]{2}[A-Z0-9]{4}$ ]]; then + echo " A station code has 2 country letters followed by 4 characters, e.g. US01AB." + # Only offer to keep a nonstandard entry when it is still safe + # to use as a directory name and a .config value + if [[ "$this_Station" =~ ^[A-Z0-9_-]+$ ]]; then + read -n1 -r -p " Use '${this_Station}' anyway? Press Y to accept it, any other key to retype: " key + echo "" + if [[ "$key" != "y" && "$key" != "Y" ]]; then + continue + fi + else + continue + fi + fi Station+=("$this_Station") done From b2aae718aeb5a8c5dc32aca68b8ac00b09718c06 Mon Sep 17 00:00:00 2001 From: Luc Busquin <133058544+Cybis320@users.noreply.github.com> Date: Mon, 3 Aug 2026 17:11:01 -0700 Subject: [PATCH 15/17] Pause for review before the file system expansion reboot --- Scripts/RMS_FirstRun.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Scripts/RMS_FirstRun.sh b/Scripts/RMS_FirstRun.sh index 8e57783fc..b30fa90ae 100755 --- a/Scripts/RMS_FirstRun.sh +++ b/Scripts/RMS_FirstRun.sh @@ -180,8 +180,12 @@ rated size, e.g. a 64 GB card gives about 62 GB. echo "Expanding the file system, then the system will reboot." echo "This guide will start again after the reboot." sudo raspi-config --expand-rootfs - echo "Rebooting in 5 seconds, please wait..." - sleep 5 + echo "" + flushInput + read -n1 -s -r -p 'Take your time to review the output above, then press any key to reboot... ' + echo "" + echo "Rebooting..." + sleep 2 sudo reboot exit 0 else From d7b4e699df8beb7982e73f7468715d22c0bedc68 Mon Sep 17 00:00:00 2001 From: Luc Busquin <133058544+Cybis320@users.noreply.github.com> Date: Mon, 3 Aug 2026 17:11:01 -0700 Subject: [PATCH 16/17] Recompute reboot_after_processing from the station count on every run --- Scripts/add_Station.sh | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/Scripts/add_Station.sh b/Scripts/add_Station.sh index deb37fc58..f22c178e3 100755 --- a/Scripts/add_Station.sh +++ b/Scripts/add_Station.sh @@ -431,18 +431,25 @@ fi total=$(count_stations) -if [[ $total -gt 1 ]]; then - for dir in ~/source/Stations/*/; do +# Recompute the per-station settings from the current station count on +# every run, so cameras added (or removed) since the last run are +# accounted for +for dir in ~/source/Stations/*/; do + [[ -f "${dir}/.config" ]] || continue + if [[ $total -gt 1 ]]; then # scale the reserved free space with the number of stations sed -i "s/^extra_space_gb:.*$/extra_space_gb: $(( total * 20 ))/g" "${dir}/.config" # disable the daily post processing reboot, it would kill the other captures sed -i "s/^\(reboot_after_processing:\).*/\1 false/g" "${dir}/.config" - done - - # remove comment from last line of wayfire.ini to enable window cascade (Pi image) - if [[ -f ~/.config/wayfire.ini ]]; then - sed -i s/#mode/mode/ ~/.config/wayfire.ini + else + # a single camera keeps the standard daily post processing reboot + sed -i "s/^\(reboot_after_processing:\).*/\1 true/g" "${dir}/.config" fi +done + +# remove comment from last line of wayfire.ini to enable window cascade (Pi image) +if [[ $total -gt 1 && -f ~/.config/wayfire.ini ]]; then + sed -i s/#mode/mode/ ~/.config/wayfire.ini fi # Make sure the Desktop StartCapture entry points at RMS_StartCapture.sh, From d0fe849e57a3da52b29237c2e87cf4a75a7f229b Mon Sep 17 00:00:00 2001 From: Luc Busquin <133058544+Cybis320@users.noreply.github.com> Date: Mon, 3 Aug 2026 22:18:29 -0700 Subject: [PATCH 17/17] Clean up legacy launch entry names and match nonstandard IDs in GRMSUpdater --- Scripts/MultiCamLinux/GRMSUpdater.sh | 4 +++- Scripts/add_Station.sh | 10 ++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Scripts/MultiCamLinux/GRMSUpdater.sh b/Scripts/MultiCamLinux/GRMSUpdater.sh index 51246d6ba..c9e148c31 100755 --- a/Scripts/MultiCamLinux/GRMSUpdater.sh +++ b/Scripts/MultiCamLinux/GRMSUpdater.sh @@ -527,7 +527,9 @@ fi mapfile -t RunList < <( pgrep -f "Scripts/MultiCamLinux/StartCapture.sh" | while read -r pid; do cmdline=$(ps -p "$pid" -o args= 2>/dev/null || continue) - if [[ "$cmdline" =~ Scripts/MultiCamLinux/StartCapture\.sh[[:space:]]+([[:alnum:]]{6}) ]]; then + # Station IDs are normally 6 alphanumerics, but add_Station.sh + # also accepts underscores/hyphens and other lengths on request + if [[ "$cmdline" =~ Scripts/MultiCamLinux/StartCapture\.sh[[:space:]]+([[:alnum:]_-]+) ]]; then echo "${BASH_REMATCH[1]}" fi done | sort -u diff --git a/Scripts/add_Station.sh b/Scripts/add_Station.sh index f22c178e3..2fad718e8 100755 --- a/Scripts/add_Station.sh +++ b/Scripts/add_Station.sh @@ -436,6 +436,16 @@ total=$(count_stations) # accounted for for dir in ~/source/Stations/*/; do [[ -f "${dir}/.config" ]] || continue + station=$(basename "$dir") + + # Remove launch entries left over from the pre-merge per-platform + # scripts - their different file names would otherwise double-launch + # the station at login + rm -f ~/.config/autostart/"${station}_StartCap.desktop" \ + "${Desktop}/${station}_StartCap.desktop" \ + "${Desktop}/Show_LiveStream-${station}.desktop" \ + "${Desktop}/${station}-Show_LiveStream.desktop" + if [[ $total -gt 1 ]]; then # scale the reserved free space with the number of stations sed -i "s/^extra_space_gb:.*$/extra_space_gb: $(( total * 20 ))/g" "${dir}/.config"