diff --git a/allsky.sh b/allsky.sh index 605380d40..03ccaa9b3 100755 --- a/allsky.sh +++ b/allsky.sh @@ -16,7 +16,7 @@ STOPPED_MSG="Allsky Stopped!" ERROR_MSG_PREFIX="*** ERROR ***\n${STOPPED_MSG}\n" #shellcheck disable=SC2086 source-path=. -source "${ALLSKY_HOME}/variables.sh" || exit ${ALLSKY_ERROR_STOP} +source "${ALLSKY_HOME}/variables.sh" || exit "${ALLSKY_ERROR_STOP}" if [[ -z ${ALLSKY_CONFIG} ]]; then MSG="FATAL ERROR: 'source variables.sh' did not work properly." echo -e "${RED}*** ${MSG}${NC}" @@ -25,19 +25,17 @@ if [[ -z ${ALLSKY_CONFIG} ]]; then "${NOT_STARTED_MSG}
${MSG}" fi -#shellcheck disable=SC2086 source-path=scripts -source "${ALLSKY_SCRIPTS}/functions.sh" || exit ${ALLSKY_ERROR_STOP} -#shellcheck disable=SC2086,SC1091 # file doesn't exist in GitHub -source "${ALLSKY_CONFIG}/config.sh" || exit ${ALLSKY_ERROR_STOP} -#shellcheck disable=SC2086 source-path=scripts -source "${ALLSKY_SCRIPTS}/installUpgradeFunctions.sh" || exit ${ALLSKY_ERROR_STOP} +#shellcheck source-path=scripts +source "${ALLSKY_SCRIPTS}/functions.sh" || exit "${ALLSKY_ERROR_STOP}" +#shellcheck source-path=scripts +source "${ALLSKY_SCRIPTS}/installUpgradeFunctions.sh" || exit "${ALLSKY_ERROR_STOP}" # Make sure they rebooted if they were supposed to. NEEDS_REBOOT="false" reboot_needed && NEEDS_REBOOT="true" # Make sure the settings have been configured after an installation or upgrade. -LAST_CHANGED="$( settings ".lastChanged" )" +LAST_CHANGED="$( settings ".lastchanged" )" if [[ ${LAST_CHANGED} == "" ]]; then echo "*** ===== Allsky needs to be configured before it can be used. See the WebUI." if [[ ${NEEDS_REBOOT} == "true" ]]; then @@ -46,12 +44,12 @@ if [[ ${LAST_CHANGED} == "" ]]; then "Allsky needs\nconfiguration\nand the Pi needs\na reboot" \ "Allsky needs to be configured then the Pi rebooted." else - doExit "${EXIT_ERROR_STOP}" "ConfigurationNeeded" "" "" "${ALLSKY_SCRIPTS}/addMessage.sh" "Error" "Allsky needs to be configured." + doExit "${EXIT_ERROR_STOP}" "ConfigurationNeeded" "" "" fi elif [[ ${NEEDS_REBOOT} == "true" ]]; then - doExit "${EXIT_ERROR_STOP}" "RebootNeeded" "" "" "${ALLSKY_SCRIPTS}/addMessage.sh" "Error" "The Pi needs to be rebooted." + doExit "${EXIT_ERROR_STOP}" "RebootNeeded" "" "" fi SEE_LOG_MSG="See ${ALLSKY_LOG}" @@ -83,7 +81,10 @@ if [[ -f ${POST_INSTALLATION_ACTIONS} ]]; then fi fi -USE_NOTIFICATION_IMAGES=$(settings ".notificationimages") +USE_NOTIFICATION_IMAGES="$( settings ".notificationimages" )" || exit "${ALLSKY_ERROR_STOP}" +UHUBCTL_PATH="$( settings ".uhubctlpath" )" || exit "${ALLSKY_ERROR_STOP}" +UHUBCTL_PORT="$( settings ".uhubctlport" )" || exit "${ALLSKY_ERROR_STOP}" +LOCALE="$( settings ".locale" )" || exit "${ALLSKY_ERROR_STOP}" if [[ -z ${CAMERA_TYPE} ]]; then MSG="FATAL ERROR: 'Camera Type' not set in WebUI." @@ -98,7 +99,7 @@ pgrep "${ME}" | grep -v $$ | xargs "sudo kill -9" 2>/dev/null if [[ ${CAMERA_TYPE} == "RPi" ]]; then # "true" means use doExit() on error - RPi_COMMAND_TO_USE="$(determineCommandToUse "true" "${ERROR_MSG_PREFIX}" )" + RPi_COMMAND_TO_USE="$( determineCommandToUse "true" "${ERROR_MSG_PREFIX}" )" elif [[ ${CAMERA_TYPE} == "ZWO" ]]; then RPi_COMMAND_TO_USE="" @@ -122,7 +123,7 @@ elif [[ ${CAMERA_TYPE} == "ZWO" ]]; then fi MSG="${YELLOW}WARNING: Resetting USB ports ${REASON/\\n/ }" - if [[ ${ON_TTY} -eq 1 ]]; then + if [[ ${ON_TTY} == "true" ]]; then echo "${MSG}; restart ${ME} when done.${NC}" >&2 else echo "${MSG}, then restarting.${NC}" >&2 @@ -136,7 +137,7 @@ elif [[ ${CAMERA_TYPE} == "ZWO" ]]; then "${ALLSKY_SCRIPTS}/generate_notification_images.sh" --directory "${ALLSKY_TMP}" "${FILENAME}" \ "yellow" "" "85" "" "" \ "" "5" "yellow" "${EXTENSION}" "" "WARNING:\n\nResetting USB bus\n${REASON}.\nAttempt ${NUM_USB_RESETS}." - sudo "$UHUBCTL_PATH" -a cycle -l "$UHUBCTL_PORT" + sudo "${UHUBCTL_PATH}" -a cycle -l "${UHUBCTL_PORT}" sleep 3 # give it a few seconds, plus, allow the notification images to be seen } @@ -190,7 +191,7 @@ fi # Make sure the settings file is linked to the camera-specific file. if ! MSG="$( check_settings_link "${SETTINGS_FILE}" )" ; then "${ALLSKY_SCRIPTS}/addMessage.sh" "error" "${MSG}" - echo "ERROR: Settings file (${SETTINGS_FILE}) not linked correctly." >&2 + echo "ERROR: ${MSG}" >&2 fi # Make directories that need to exist. @@ -216,8 +217,10 @@ mkdir "${ALLSKY_ABORTS_DIR}" sudo chgrp "${WEBSERVER_GROUP}" "${ALLSKY_ABORTS_DIR}" sudo chmod 775 "${ALLSKY_ABORTS_DIR}" +rm -f "${ALLSKY_NOTIFICATION_LOG}" # clear out any notificatons from prior runs. + # Optionally display a notification image. -if [[ $USE_NOTIFICATION_IMAGES -eq 1 ]]; then +if [[ ${USE_NOTIFICATION_IMAGES} == "true" ]]; then # Can do this in the background to speed up startup. "${ALLSKY_SCRIPTS}/copy_notification_image.sh" "StartingUp" 2>&1 & fi @@ -225,30 +228,28 @@ fi : > "${ARGS_FILE}" # If the locale isn't in the settings file, try to determine it. -LOCALE="$(settings .locale)" if [[ -z ${LOCALE} ]]; then if [[ -n ${LC_ALL} ]]; then - echo "-Locale=${LC_ALL}" >> "${ARGS_FILE}" + echo "Locale=${LC_ALL}" >> "${ARGS_FILE}" elif [[ -n ${LANG} ]]; then - echo "-lOcale=${LANG}" >> "${ARGS_FILE}" + echo "lOcale=${LANG}" >> "${ARGS_FILE}" elif [[ -n ${LANGUAGE} ]]; then - echo "-loCale=${LANGUAGE}" >> "${ARGS_FILE}" + echo "loCale=${LANGUAGE}" >> "${ARGS_FILE}" fi fi # We must pass "-config ${ARGS_FILE}" on the command line, # and debuglevel we did above, so don't do them again. -TAB="$( echo -e "\t" )" -convert_json_to_tabs "${SETTINGS_FILE}" | - grep -E -i -v "^config${TAB}|^debuglevel${TAB}" | - sed -e 's/^/-/' -e "s/${TAB}/=/" >> "${ARGS_FILE}" +# Only pass settings that are used by the capture program. +"${ALLSKY_WEBUI}/includes/outputJSONwithEqual.php" --capture-only "${OPTIONS_FILE}" | + grep -E -i -v "^config=|^debuglevel=" >> "${ARGS_FILE}" # When using a desktop environment a preview of the capture can be displayed in a separate window. # The preview mode does not work if we are started as a service or if the debian distribution has no desktop environment. -[[ $1 == "preview" ]] && echo "-preview=1" >> "${ARGS_FILE}" +[[ $1 == "preview" ]] && echo "preview=true" >> "${ARGS_FILE}" -echo "-version=$( get_version )" >> "${ARGS_FILE}" -echo "-save_dir=${CAPTURE_SAVE_DIR}" >> "${ARGS_FILE}" +echo "version=$( get_version )" >> "${ARGS_FILE}" +echo "save_dir=${CAPTURE_SAVE_DIR}" >> "${ARGS_FILE}" FREQUENCY_FILE="${ALLSKY_TMP}/IMG_UPLOAD_FREQUENCY.txt" # If the user wants images uploaded only every n times, save that number to a file. @@ -262,8 +263,6 @@ fi CAPTURE="capture_${CAMERA_TYPE}" -rm -f "${ALLSKY_NOTIFICATION_LOG}" # clear out any notificatons from prior runs. - # Clear up any flow timings "${ALLSKY_SCRIPTS}/flow-runner.py" --cleartimings @@ -277,7 +276,7 @@ RETCODE=$? [[ ${RETCODE} -eq ${EXIT_OK} ]] && doExit "${EXIT_OK}" "" if [[ ${RETCODE} -eq ${EXIT_RESTARTING} ]]; then - if [[ ${ON_TTY} -eq 1 ]]; then + if [[ ${ON_TTY} == "true" ]]; then echo "*** Can restart allsky now. ***" NOTIFICATION_TYPE="NotRunning" else @@ -290,20 +289,20 @@ if [[ ${RETCODE} -eq ${EXIT_RESET_USB} ]]; then # Reset the USB bus if possible if [[ ${UHUBCTL_PATH} != "" ]]; then reset_usb " (ASI_ERROR_TIMEOUTs)" - if [[ ${ON_TTY} -eq 1 ]]; then + if [[ ${ON_TTY} == "true" ]]; then echo "*** USB bus was reset; You can restart allsky now. ***" NOTIFICATION_TYPE="NotRunning" else NOTIFICATION_TYPE="Restarting" fi - if [[ ${USE_NOTIFICATION_IMAGES} -eq 1 ]]; then + if [[ ${USE_NOTIFICATION_IMAGES} == "true" ]]; then "${ALLSKY_SCRIPTS}/copy_notification_image.sh" "${NOTIFICATION_TYPE}" fi doExit 0 "" # use 0 so the service is restarted else # TODO: use ASI_ERROR_TIMEOUT message MSG="Non-recoverable ERROR found" - [[ ${ON_TTY} -eq 1 ]] && echo "*** ${MSG} - ${SEE_LOG_MSG}. ***" + [[ ${ON_TTY} == "true" ]] && echo "*** ${MSG} - ${SEE_LOG_MSG}. ***" doExit "${EXIT_ERROR_STOP}" "Error" \ "${ERROR_MSG_PREFIX}Too many\nASI_ERROR_TIMEOUT\nerrors received!\n${SEE_LOG_MSG}" \ "${STOPPED_MSG}
${MSG}
${SEE_LOG_MSG}." @@ -313,7 +312,7 @@ fi # RETCODE -ge ${EXIT_ERROR_STOP} means we should not restart until the user fixes the error. if [[ ${RETCODE} -ge ${EXIT_ERROR_STOP} ]]; then echo "***" - if [[ ${ON_TTY} -eq 1 ]]; then + if [[ ${ON_TTY} == "true" ]]; then echo "*** After fixing, restart ${ME}.sh. ***" else echo "*** After fixing, restart the allsky service. ***" @@ -323,7 +322,7 @@ if [[ ${RETCODE} -ge ${EXIT_ERROR_STOP} ]]; then fi # Some other error -if [[ ${USE_NOTIFICATION_IMAGES} -eq 1 ]]; then +if [[ ${USE_NOTIFICATION_IMAGES} == "true" ]]; then # If started by the service, it will restart us once we exit. doExit "${RETCODE}" "NotRunning" else diff --git a/config_repo/Makefile b/config_repo/Makefile index 498b208b1..aaa5fac38 100644 --- a/config_repo/Makefile +++ b/config_repo/Makefile @@ -24,7 +24,8 @@ ifeq ($(PKGBUILD),) endif -CONFIGFILES := config.sh ftp-settings.sh +CONFIGFILES := config.sh +ENVFILE := env.json UNINSTALLFILES := $(DESTDIR)$(sysconfdir)/logrotate.d/allsky $(DESTDIR)$(sysconfdir)/rsyslog.d/allsky.conf $(DESTDIR)$(sysconfdir)/systemd/system/allsky.service $(DESTDIR)$(sysconfdir)/systemd/system/allskyperiodic.service %: @@ -42,7 +43,6 @@ uninstall: @echo `date +%F\ %R:%S` Complete. @echo `date +%F\ %R:%S` NOTE: Config files were \-NOT\- removed. @echo `date +%F\ %R:%S` To remove config files, please run \'sudo make remove_configs\' - .PHONY : uninstall ifeq ($(PKGBUILD),1) @@ -57,7 +57,6 @@ createDirs: @if [ ! -e $(DESTDIR)$(sysconfdir)/profile.d ]; then mkdir -p $(DESTDIR)$(sysconfdir)/profile.d; fi @if [ ! -e $(DESTDIR)$(sysconfdir)/systemd/system ]; then mkdir -p $(DESTDIR)$(sysconfdir)/systemd/system; fi @if [ ! -e $(DESTDIR)$(sysconfdir)/udev/rules.d ]; then mkdir -p $(DESTDIR)$(sysconfdir)/udev/rules.d; fi - .PHONY : createDirs $(CONFIGFILES): @@ -67,7 +66,14 @@ $(CONFIGFILES): fi .PHONY : $(CONFIGFILES) -install: createDirs $(CONFIGFILES) +$(ENVFILE): + @if [ ! -e $(DESTDIR)$(sysconfdir)/allsky/$@ ]; then \ + echo `date +%F\ %R:%S` Copying default $@; \ + install -m 0644 $@.repo $(DESTDIR)$(sysconfdir)/allsky/$@; \ + fi +.PHONY : $(ENVFILE) + +install: createDirs $(CONFIGFILES) $(ENVFILE) @echo `date +%F\ %R:%S` Setting up udev rules... @install -D -m 0644 asi.rules $(DESTDIR)$(sysconfdir)/udev/rules.d/ @echo `date +%F\ %R:%S` Setting up logging... @@ -80,18 +86,18 @@ install: createDirs $(CONFIGFILES) @echo `date +%F\ %R:%S` Setting up home environment variable... @echo -e "export ALLSKY_TMP=/tmp\nexport ALLSKY_CONFIG=$(DESTDIR)$(sysconfdir)/allsky\nexport ALLSKY_SCRIPTS=$(DESTDIR)$(libexecdir)\nexport ALLSKY_NOTIFICATION_IMAGES=$(DESTDIR)$(sharedir)\nexport ALLSKY_IMAGES=/home/allsky/images/" > $(DESTDIR)$(sysconfdir)/profile.d/allsky.sh -else # Not in package build mode + +else ############################# Not in package build mode remove_configs: @echo `date +%F\ %R:%S` Removing config path and files ../config @rm -rf ../config - .PHONY : remove_configs createDirs: @echo `date +%F\ %R:%S` Creating directory structures... @if [ ! -e ../config ]; then mkdir -p ../config; chown $(SUDO_USER):$(SUDO_USER) ../config; fi - .PHONY : createDirs + $(CONFIGFILES): @if [ ! -e ../config/$@ ]; then \ echo `date +%F\ %R:%S` Copying default $@; \ @@ -99,7 +105,14 @@ $(CONFIGFILES): fi .PHONY : $(CONFIGFILES) -install: createDirs $(CONFIGFILES) +$(ENVFILE): + @if [ ! -e ../$@ ]; then \ + echo `date +%F\ %R:%S` Copying default .$@; \ + install -m 0644 -o $(SUDO_USER) -g $(SUDO_USER) $@.repo ../$@; \ + fi +.PHONY : $(ENVFILE) + +install: createDirs $(CONFIGFILES) $(ENVFILE) @echo `date +%F\ %R:%S` Setting up udev rules... @install -D -m 0644 asi.rules $(DESTDIR)$(sysconfdir)/udev/rules.d/ @echo `date +%F\ %R:%S` Setting up logging... diff --git a/config_repo/allskyDefines.inc.repo b/config_repo/allskyDefines.inc.repo index 5e26dfb48..9f3f5b331 100644 --- a/config_repo/allskyDefines.inc.repo +++ b/config_repo/allskyDefines.inc.repo @@ -1,6 +1,7 @@ Allsky" link, -# then, in the "Editor WebUI Page" section, open the "config.sh" sub-section. - -# X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*XX*X*X*X*X*X*X - - -########## Images -# Set to "true" to upload the current image to your website. -IMG_UPLOAD="false" - -# Upload the image file as "image-YYYYMMDDHHMMSS.jpg" (true) or "image.jpg" (false). -IMG_UPLOAD_ORIGINAL_NAME="false" - -# If IMG_UPLOAD is "true", upload images every IMG_UPLOAD_FREQUENCY frames, e.g., every 5 frames. -# 1 uploades every frame. -IMG_UPLOAD_FREQUENCY=1 - -# Resize images before cropping, stretching, and saving. -IMG_RESIZE="false" -IMG_WIDTH=2028 -IMG_HEIGHT=1520 - -# Crop images before stretching and saving. -CROP_IMAGE="false" -CROP_WIDTH=640 -CROP_HEIGHT=480 -CROP_OFFSET_X=0 -CROP_OFFSET_Y=0 - -# Auto stretch images saved at night. The numbers below are good defaults. -AUTO_STRETCH="false" -AUTO_STRETCH_AMOUNT=10 -AUTO_STRETCH_MID_POINT="10%" - -# Resize uploaded images. Change the size to fit your sensor. -RESIZE_UPLOADS="false" -RESIZE_UPLOADS_WIDTH=962 -RESIZE_UPLOADS_HEIGHT=720 - -# Create thumbnails of images. If you never look at them, consider changing this to "false". -IMG_CREATE_THUMBNAILS="true" - -# Remove corrupt or too dim/bright images. -REMOVE_BAD_IMAGES="true" -REMOVE_BAD_IMAGES_THRESHOLD_LOW=1 -REMOVE_BAD_IMAGES_THRESHOLD_HIGH=90 - - -########## Timelapse -# Set to "true" to generate a timelapse video at the end of each night. -TIMELAPSE="true" - -# Set the resolution in pixels of the timelapse video. -TIMELAPSEWIDTH=0 -TIMELAPSEHEIGHT=0 - -# Bitrate of the timelapse video. -TIMELAPSE_BITRATE="2000k" - -# Timelapse video Frames Per Second. -FPS=25 - -# Encoder for timelapse video. -VCODEC="libx264" - -# Pixel format. -PIX_FMT="yuv420p" - -# Amount of information displayed while creating a timelapse video. -FFLOG="warning" - -# Set to "true" to keep the list of files used in creating the timelapse video. -KEEP_SEQUENCE="false" - -# Any additional timelapse parameters. Run "ffmpeg -?" to see the options. -TIMELAPSE_EXTRA_PARAMETERS="" - -# Set to "true" to upload the timelapse video to your website at the end of each night. -UPLOAD_VIDEO="false" - -# Set to "true" to upload the timelapse video's thumbnail to your website at the end of each night. -TIMELAPSE_UPLOAD_THUMBNAIL="true" - -###### Mini-timelapse -# The number of images you want in the mini-timelapse. 0 disables mini-timelapse creation. -TIMELAPSE_MINI_IMAGES=0 - -# Should a mini-timelapse be created even if ${TIMELAPSE_MINI_IMAGES} haven't been captured yet? -TIMELAPSE_MINI_FORCE_CREATION="false" - -# After how many images should the mini-timelapse be made? -# If you have a slow Pi or short delays between images, -# set this to a higher number (i.e., not as often). -TIMELAPSE_MINI_FREQUENCY=5 - -# The remaining TIMELAPSE_MINI_* variables serve the same function as the daily timelapse. -TIMELAPSE_MINI_UPLOAD_VIDEO="true" -TIMELAPSE_MINI_UPLOAD_THUMBNAIL="true" -TIMELAPSE_MINI_FPS=5 -TIMELAPSE_MINI_BITRATE="1000k" -TIMELAPSE_MINI_WIDTH=1014 -TIMELAPSE_MINI_HEIGHT=760 - - -########## Keogram -# Set to "true" to generate a keogram at the end of each night. -KEOGRAM="true" - -# Additional Keogram parameters. -KEOGRAM_EXTRA_PARAMETERS="--font-size 1.0 --font-line 1 --font-color '255 255 255'" - -# Set to "true" to upload the keogram image to your website at the end of each night. -UPLOAD_KEOGRAM="false" - - -########## Startrails -# Set to "true" to generate a startrails image of each night. -STARTRAILS="true" - -# Images with a brightness higher than this threshold will be skipped for -# startrails image generation. -BRIGHTNESS_THRESHOLD=0.1 - -# Any additional startrails parameters. -STARTRAILS_EXTRA_PARAMETERS="" - -# Set to "true" to upload the startrails image to your website at the end of each night. -UPLOAD_STARTRAILS="false" - - -########## Other -# Size of thumbnails. -THUMBNAIL_SIZE_X=100 -THUMBNAIL_SIZE_Y=75 - -# Set this value to the number of days images plus videos you want to keep. -# Set to 0 to keep ALL days. -DAYS_TO_KEEP=14 - -# Same as DAYS_TO_KEEP, but for the Allsky Website, if installed. -WEB_DAYS_TO_KEEP=0 - -# See the documentation for a description of this setting. -WEBUI_DATA_FILES="" - -# See the documentation for a description of these settings. -UHUBCTL_PATH="" -UHUBCTL_PORT=2 - - -# ================ DO NOT CHANGE ANYTHING BELOW THIS LINE ================ -ME2="$(basename "${BASH_SOURCE[0]}")" - -# CAMERA_TYPE is updated during installation -CAMERA_TYPE="" -if [ "${CAMERA_TYPE}" = "" ]; then - echo -e "${RED}${ME2}: ERROR: Please set 'Camera Type' in the WebUI.${NC}" - sudo systemctl stop allsky > /dev/null 2>&1 - exit ${EXIT_ERROR_STOP} -fi - -IMG_DIR="current/tmp" -CAPTURE_SAVE_DIR="${ALLSKY_TMP}" - -# Don't try to upload a mini-timelapse if they aren't using them. -if [[ ${TIMELAPSE_MINI_IMAGES} -eq 0 ]]; then - TIMELAPSE_MINI_UPLOAD_VIDEO="false" - TIMELAPSE_MINI_UPLOAD_THUMBNAIL="false" -fi - -if [[ -z ${SETTINGS_FILE} ]]; then # SETTINGS_FILE is defined in variables.sh - echo -e "${RED}${ME2}: ERROR: SETTINGS_FILE variable not defined!${NC}" - echo -e "${RED}Make sure 'variables.sh' is source'd in!${NC}" - return 1 -fi -if [[ ! -f ${SETTINGS_FILE} ]]; then - echo -e "${RED}${ME2}: ERROR: Settings file '${SETTINGS_FILE}' not found!${NC}" - sudo systemctl stop allsky > /dev/null 2>&1 - exit ${EXIT_ERROR_STOP} -fi - -# Get the name of the file the websites will look for, and split into name and extension. -FULL_FILENAME="$(settings ".filename")" -EXTENSION="${FULL_FILENAME##*.}" -FILENAME="${FULL_FILENAME%.*}" - -CAMERA_MODEL="$(settings '.cameraModel')" - -# So scripts can conditionally output messages. -ALLSKY_DEBUG_LEVEL="$(settings '.debuglevel')" -# ALLSKY_VERSION is updated during installation -ALLSKY_VERSION="XX_ALLSKY_VERSION_XX" - -CONFIG_SH_VERSION=1 diff --git a/config_repo/env.json.repo b/config_repo/env.json.repo new file mode 100644 index 000000000..e389355ff --- /dev/null +++ b/config_repo/env.json.repo @@ -0,0 +1,26 @@ +{ + "WARNING" : "Do NOT edit this file manually. Use the WebUI.", + "GITHUB_TOKEN" : "", + "REMOTEWEBSITE_HOST" : "", + "REMOTEWEBSITE_PORT" : "", + "REMOTEWEBSITE_USER" : "", + "REMOTEWEBSITE_PASSWORD" : "", + "REMOTEWEBSITE_LFTP_COMMANDS" : "", + "REMOTEWEBSITE_SSH_KEY_FILE" : "", + "REMOTEWEBSITE_AWS_CLI_DIR" : "/home/pi/.local/bin", + "REMOTEWEBSITE_S3_BUCKET" : "allskybucket", + "REMOTEWEBSITE_S3_ACL" : "private", + "REMOTEWEBSITE_GCS_BUCKET" : "allskybucket", + "REMOTEWEBSITE_GCS_ACL" : "private", + "REMOTESERVER_HOST" : "", + "REMOTESERVER_PORT" : "", + "REMOTESERVER_USER" : "", + "REMOTESERVER_PASSWORD" : "", + "REMOTESERVER_LFTP_COMMANDS" : "", + "REMOTESERVER_SSH_KEY_FILE" : "", + "REMOTESERVER_AWS_CLI_DIR" : "/home/pi/.local/bin", + "REMOTESERVER_S3_BUCKET" : "allskybucket", + "REMOTESERVER_S3_ACL" : "private", + "REMOTESERVER_GCS_BUCKET" : "allskybucket", + "REMOTESERVER_GCS_ACL" : "private" +} diff --git a/config_repo/ftp-settings.sh.repo b/config_repo/ftp-settings.sh.repo deleted file mode 100644 index 89c91ebe3..000000000 --- a/config_repo/ftp-settings.sh.repo +++ /dev/null @@ -1,80 +0,0 @@ -#!/bin/bash - -# X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*XX*X*X*X*X*X*X - -# For details on these settings, click on the "Allsky Documentation" link in the WebUI, -# then click on the "Settings -> Allsky" link, -# then, in the "Editor WebUI Page" section, open the "ftp-settings.sh" sub-section. - -# X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*XX*X*X*X*X*X*X - - - - # How will files be uploaded? -PROTOCOL="" - - # The directory where the current image should be copied to. -IMAGE_DIR="" -WEB_IMAGE_DIR="" - - # The directory where the timelapse video should be uploaded to. -VIDEOS_DIR="" -VIDEOS_DESTINATION_NAME="" -WEB_VIDEOS_DIR="" - - # The directory where the keogram image should be copied to. -KEOGRAM_DIR="" -KEOGRAM_DESTINATION_NAME="" -WEB_KEOGRAM_DIR="" - - # The directory where the startrails image should be copied to. -STARTRAILS_DIR="" -STARTRAILS_DESTINATION_NAME="" -WEB_STARTRAILS_DIR="" - - -############### ftp, ftps, sftp, and scp PROTOCOLS only: - # Name of the remote server. -REMOTE_HOST="" - - # Optionally enter the port required by your server. -REMOTE_PORT="" - - -############### ftp, ftps, and sftp PROTOCOLS only. REMOTE_USER is also used by the scp PROTOCOL: - # The username of the login on the remote server. -REMOTE_USER="" - - # The password of the login on your FTP server. Does not apply to PROTOCOL=scp. -REMOTE_PASSWORD="" - - # If you need special commands executed when connecting to the FTP server enter them here. -LFTP_COMMANDS="" - - -############### scp PROTOCOL only: - # The path to the SSH key. -SSH_KEY_FILE="" - - -############### S3 PROTOCOL only: - # AWS CLI directory where the AWS CLI tools are installed. -AWS_CLI_DIR="/home/pi/.local/bin" - - # Name of S3 Bucket where the files will be uploaded. -S3_BUCKET="allskybucket" - - # S3 Access Control List (ACL). -S3_ACL="private" - - -############### GCS PROTOCOL only: - # Name of the GCS bucket where the files are uploaded. -GCS_BUCKET="allskybucket" - - # GCS Access Control List (ACL). -GCS_ACL="private" - - -#### DO NOT CHANGE THE NEXT LINE -FTP_SH_VERSION=1 diff --git a/config_repo/options.json.repo b/config_repo/options.json.repo index a8fb44207..5e10ba2db 100644 --- a/config_repo/options.json.repo +++ b/config_repo/options.json.repo @@ -1,50 +1,63 @@ [ { -"name" : "daytimeSettingsHeader", -"description" : "Daytime settings", -"type" : "header", -"display" : 1, -"advanced" : 0 +"name" : "daynighttab========================================", +"label" : "Day/Night Settings", +"type" : "header-tab" }, { -"name" : "takeDaytimeImages", -"default" : 1, -"description" : "Activate to take daytime images.", +"name" : "daytimesettingsheader", +"label" : "Daytime settings", +"type" : "header" +}, +{ +"name" : "daynightcolumns", +"label" : "Setting, Day Value, Description", +"candrop" : "Description", +"type" : "header-column" +}, +{ +"name" : "takedaytimeimages", +"default" : true, +"description" : "Enable to take daytime images.", "label" : "Daytime Capture", "type" : "boolean", -"display" : 1, -"checkchanges" : 1, -"advanced" : 1 +"usage" : "capture", +"checkchanges" : true, +"action" : "reload", +"advanced" : true }, { -"name" : "saveDaytimeImages", -"default" : 0, -"description" : "Activate to save daytime images.
Only applies if Daytime Capture is enabled.", +"name" : "savedaytimeimages", +"default" : false, +"description" : "Enable to save daytime images.", "label" : "Daytime Save", "type" : "boolean", -"display" : 1, -"advanced" : 1 +"booldependson" : "takedaytimeimages", +"advanced" : true + }, { "name" : "dayautoexposure", -"default" : 1, -"description" : "Activate to enable daytime auto-exposure.", +"default" : true, +"description" : "Enable to use daytime auto-exposure.", "label" : "Auto-Exposure", "type" : "boolean", -"display" : 1, -"generic" : 1, -"advanced" : 1 +"usage" : "capture", +"booldependson" : "takedaytimeimages", +"action" : "reload", +"advanced" : true }, { "name" : "daymaxautoexposure", "minimum" : "_min", "maximum" : "_max", "default" : "_default", -"description" : "Maximum daytime Auto-Exposure time in milliseconds (1000ms = 1 sec).
Only applies if daytime Auto-Exposure is enabled.", +"description" : "Maximum daytime Auto-Exposure time in milliseconds (1000ms = 1 sec).", "label" : "Max Auto-Exposure", "type" : "float", -"display" : 1, -"advanced" : 0 +"usage" : "capture", +"booldependson" : "takedaytimeimages AND daymaxautoexposure", +"action" : "reload" }, { "name" : "dayexposure", @@ -54,8 +67,10 @@ "description" : "Manual exposure time in milliseconds (1000ms = 1 sec). Can be a fraction.
If using daytime Auto-Exposure, this number is used as a starting point.", "label" : "Manual Exposure", "type" : "float", -"display" : 1, -"advanced" : 1 +"usage" : "capture", +"booldependson" : "takedaytimeimages", +"action" : "reload", +"advanced" : true }, { "name" : "daymean", @@ -65,30 +80,23 @@ "description" : "The target mean brightness level when Auto-Exposure is on. 1.0 is pure white.
Best used when Auto-Exposure and Auto-Gain are on.
0 disables this auto-exposure mode.", "label" : "Mean Target", "type" : "float", -"display" : 1, -"advanced" : 1 +"usage" : "capture", +"booldependson" : "takedaytimeimages", +"action" : "reload", +"advanced" : true }, { "name" : "daymeanthreshold", "minimum" : "day_min", "maximum" : "day_max", "default" : "day_default", -"description" : "When using Mean Target this specifies how close to the target the brightness should be.", +"description" : "When using Mean Target this specifies how close to the target the brightness should be.", "label" : "Mean Threshold", "type" : "float", -"display" : 1, -"advanced" : 1 -}, -{ -"name" : "daybrightness", -"minimum" : "_min", -"maximum" : "_max", -"default" : "_default", -"description" : "Deprecated. Use Mean Target instead.
Changes the amount of light in the image. Higher numbers are brighter.", -"label" : "Brightness", -"type" : "float", -"display" : "_display", -"advanced" : 0 +"usage" : "capture", +"booldependson" : "takedaytimeimages AND daymean", +"action" : "reload", +"advanced" : true }, { "name" : "daydelay", @@ -98,19 +106,20 @@ "description" : "Delay between daytime images in milliseconds (1000ms = 1 sec).", "label" : "Delay", "type" : "integer", -"display" : 1, -"generic" : 1, -"advanced" : 0 +"usage" : "capture", +"booldependson" : "takedaytimeimages", +"action" : "reload" }, { "name" : "dayautogain", -"default" : 0, -"description" : "Activate to enable dayime Auto-Gain.", +"default" : false, +"description" : "Enable to use dayime Auto-Gain.", "label" : "Auto-Gain", "type" : "boolean", -"display" : 1, -"generic" : 1, -"advanced" : 1 +"usage" : "capture", +"booldependson" : "takedaytimeimages", +"action" : "reload", +"advanced" : true }, { "name" : "daymaxautogain", @@ -120,19 +129,23 @@ "description" : "Maximum gain when using Auto-Gain.", "label" : "Max Auto-Gain", "type" : "float", -"display" : 1, -"advanced" : 1 +"usage" : "capture", +"booldependson" : "takedaytimeimages AND dayautogain", +"action" : "reload", +"advanced" : true }, { "name" : "daygain", "minimum" : "_min", "maximum" : "_max", "default" : "day_default", -"description" : "Manually sets the light sensitivity of the camera.
A high number returns a brighter image but more noise too. Can normally leave at the minimum value for daytime.", +"description" : "Manually sets the light sensitivity of the camera.
A high number returns a brighter image but more noise. Can normally leave at the minimum value for daytime.", "label" : "Gain", "type" : "float", -"display" : 1, -"advanced" : 1 +"usage" : "capture", +"booldependson" : "takedaytimeimages", +"action" : "reload", +"advanced" : true }, { "name" : "daybin", @@ -140,22 +153,24 @@ "description" : "Binning level. 1x1 = OFF.", "label" : "Binning", "type" : "select", +"usage" : "capture", "options" : [ "bin_values" ], -"display" : 1, -"generic" : 1, -"advanced" : 1 +"booldependson" : "takedaytimeimages", +"action" : "reload", +"advanced" : true }, { "name" : "dayawb", -"default" : 0, -"description" : "Sets Auto White Balance.", +"default" : true, +"description" : "Sets Auto White Balance (camera adjusts color).", "label" : "Auto White Balance", "type" : "boolean", "display" : "_display", -"generic" : 1, -"advanced" : 0 +"usage" : "capture", +"booldependson" : "takedaytimeimages", +"action" : "reload" }, { "name" : "daywbr", @@ -166,7 +181,9 @@ "label" : "Red Balance", "type" : "float", "display" : "_display", -"advanced" : 0 +"usage" : "capture", +"booldependson" : "takedaytimeimages AND dayawb", +"action" : "reload" }, { "name" : "daywbb", @@ -177,32 +194,37 @@ "label" : "Blue Balance", "type" : "float", "display" : "_display", -"advanced" : 0 +"usage" : "capture", +"booldependson" : "takedaytimeimages AND dayawb", +"action" : "reload" }, { "name" : "dayskipframes", "minimum" : 0, "maximum" : "none", "default" : 5, -"description" : "When starting Allsky during the day, skip up to this many frames while the software gets to the correct exposure.
Only applies if daytime Auto-Exposure is on.", +"description" : "When starting Allsky during the day, skip up to this many frames while the software gets to the correct exposure.", "label" : "Frames To Skip", "type" : "integer", -"display" : 1, -"generic" : 1, -"advanced" : 1 +"usage" : "capture", +"booldependson" : "takedaytimeimages AND dayautoexposure", +"action" : "reload", +"advanced" : true }, { -"name" : "dayEnableCooler", -"default" : 0, -"description" : "Activate to use cooling (works only on cooled cameras).", +"name" : "dayenablecooler", +"default" : true, +"description" : "Enable to use cooling (on cooled cameras only).", "label" : "Cooling", "type" : "boolean", "display" : "_display", -"generic" : 1, -"advanced" : 1 +"usage" : "capture", +"booldependson" : "takedaytimeimages", +"action" : "reload", +"advanced" : true }, { -"name" : "dayTargetTemp", +"name" : "daytargettemp", "minimum" : "_min", "maximum" : "_max", "default" : "_default", @@ -210,47 +232,71 @@ "label" : "Target Temp.", "type" : "integer", "display" : "_display", -"generic" : 1, -"advanced" : 1 +"usage" : "capture", +"booldependson" : "takedaytimeimages AND dayenablecooler", +"action" : "reload", +"advanced" : true }, { -"name" : "dayTuningFile", +"name" : "daytuningfile", "default" : "", -"description" : "Name of the day camera tuning file to use. Omit this option to preserve the default behavior of libcamera.
Please read this documentation or this document if using a Raspberry camera with libcamera. For other Raspberry cameras read the camera manual for more information.", +"description" : "Name of the optional day camera tuning file to use.
See documentation , document , or the camera manual for more information.", "label" : "Tuning File", "type" : "widetext", +"usage" : "capture", "display" : "_display", -"checkchanges" : 1, -"optional" : 1, -"advanced" : 1 +"checkchanges" : true, +"optional" : true, +"booldependson" : "takedaytimeimages", +"action" : "restart", +"advanced" : true }, + { -"name" : "nighttimeSettingsHeader", -"description" : "Nighttime settings", -"type" : "header", -"display" : 1, -"advanced" : 0 +"name" : "nighttimesettingsheader", +"label" : "Nighttime settings", +"type" : "header" +}, +{ +"name" : "takenighttimeimages", +"default" : true, +"description" : "Enable to take nighttime images.", +"label" : "Nighttime Capture", +"type" : "boolean", +"usage" : "capture", +"checkchanges" : true, +"action" : "reload", +"advanced" : true +}, +{ +"name" : "savenighttimeimages", +"default" : false, +"description" : "Enable to save nighttime images.", +"label" : "Nighttime Save", +"type" : "boolean", +"booldependson" : "takenighttimeimages", +"advanced" : true }, { "name" : "nightautoexposure", -"default" : 1, -"description" : "Activate to enable nighttime auto-exposure.", +"default" : true, +"description" : "Enable to use nighttime auto-exposure.", "label" : "Auto-Exposure", "type" : "boolean", -"display" : 1, -"generic" : 1, -"advanced" : 0 +"usage" : "capture", +"action" : "reload" }, { "name" : "nightmaxautoexposure", "minimum" : "_min", "maximum" : "_max", "default" : "_default", -"description" : "Maximum nighttime Auto-Exposure time in milliseconds (1000ms = 1 sec).
Only applies if nighttime Auto-Exposure is enabled.", +"description" : "Maximum nighttime Auto-Exposure time in milliseconds (1000ms = 1 sec).", "label" : "Max Auto-Exposure", "type" : "float", -"display" : 1, -"advanced" : 0 +"usage" : "capture", +"booldependson" : "nightautoexposure", +"action" : "reload" }, { "name" : "nightexposure", @@ -260,8 +306,8 @@ "description" : "Manual exposure time in milliseconds (1000ms = 1 sec). Can be a fraction.
If using nighttime Auto-Exposure, this number is used as a starting point.", "label" : "Manual Exposure", "type" : "float", -"display" : 1, -"advanced" : 0 +"usage" : "capture", +"action" : "reload" }, { "name" : "nightmean", @@ -271,30 +317,22 @@ "description" : "The target mean brightness level when Auto-Exposure is on. 1.0 is pure white.
Best used when Auto-Exposure and Auto-Gain are on.
0 disables this auto-exposure mode.", "label" : "Mean Target", "type" : "float", -"display" : 1, -"advanced" : 1 +"usage" : "capture", +"action" : "reload", +"advanced" : true }, { "name" : "nightmeanthreshold", "minimum" : "night_min", "maximum" : "night_max", "default" : "day_default", -"description" : "When using Mean Target this specifies how close to the target the brightness should be.", +"description" : "When using Mean Target this specifies how close to the target the brightness should be.", "label" : "Mean Threshold", "type" : "float", -"display" : 1, -"advanced" : 1 -}, -{ -"name" : "nightbrightness", -"minimum" : "_min", -"maximum" : "_max", -"default" : "_default", -"description" : "Deprecated. Use Mean Target instead.
Changes the amount of light in the image. Higher numbers are brighter.", -"label" : "Brightness", -"type" : "float", -"display" : "_display", -"advanced" : 0 +"usage" : "capture", +"booldependson" : "nightmean", +"action" : "reload", +"advanced" : true }, { "name" : "nightdelay", @@ -304,19 +342,17 @@ "description" : "Delay between nighttime images in milliseconds (1000ms = 1 sec).", "label" : "Delay", "type" : "integer", -"display" : 1, -"generic" : 1, -"advanced" : 0 +"usage" : "capture", +"action" : "reload" }, { "name" : "nightautogain", -"default" : 0, -"description" : "Activate to enable nighttime Auto-Gain.", +"default" : false, +"description" : "Enable to use nighttime Auto-Gain.", "label" : "Auto-Gain", "type" : "boolean", -"display" : 1, -"generic" : 1, -"advanced" : 0 +"usage" : "capture", +"action" : "reload" }, { "name" : "nightmaxautogain", @@ -326,19 +362,21 @@ "description" : "Maximum gain when using Auto-Gain.", "label" : "Max Auto-Gain", "type" : "float", -"display" : 1, -"advanced" : 1 +"usage" : "capture", +"booldependson" : "nightautogain", +"action" : "reload", +"advanced" : true }, { "name" : "nightgain", "minimum" : "_min", "maximum" : "_max", "default" : "night_default", -"description" : "Manually sets the light sensitivity of the camera.
A high number returns a brighter image but more noise too.", +"description" : "Manually sets the light sensitivity of the camera.
A high number returns a brighter image but more noise.", "label" : "Gain", "type" : "float", -"display" : 1, -"advanced" : 0 +"usage" : "capture", +"action" : "reload" }, { "name" : "nightbin", @@ -346,22 +384,21 @@ "description" : "Binning level. 1x1 = OFF.", "label" : "Binning", "type" : "select", +"usage" : "capture", "options" : [ "bin_values" ], -"display" : 1, -"generic" : 1, -"advanced" : 0 +"action" : "reload" }, { "name" : "nightawb", -"default" : 0, -"description" : "Activate to enable Auto White Balance (camera adjusts color).", +"default" : false, +"description" : "Enable to use Auto White Balance (camera adjusts color).", "label" : "Auto White Balance", "type" : "boolean", +"usage" : "capture", "display" : "_display", -"generic" : 1, -"advanced" : 0 +"action" : "reload" }, { "name" : "nightwbr", @@ -372,7 +409,9 @@ "label" : "Red Balance", "type" : "float", "display" : "_display", -"advanced" : 0 +"usage" : "capture", +"booldependsoff" : "nightawb", +"action" : "reload" }, { "name" : "nightwbb", @@ -382,60 +421,81 @@ "description" : "Blue component for the white balance.
When using Auto White Balance, this number is a starting point.", "label" : "Blue Balance", "type" : "float", +"usage" : "capture", "display" : "_display", -"advanced" : 0 +"booldependsoff" : "nightawb", +"action" : "reload" }, { "name" : "nightskipframes", "minimum" : 0, "maximum" : "none", "default" : 1, -"description" : "When starting Allsky at night, skip up to this many frames while the software gets to the correct exposure.
Only applies if nighttime Auto-Exposure is on.", +"description" : "When starting Allsky at night, skip up to this many frames while the software gets to the correct exposure.", "label" : "Frames To Skip", "type" : "integer", -"display" : 1, -"generic" : 1, -"advanced" : 1 +"usage" : "capture", +"booldependson" : "nightautoexposure", +"advanced" : true }, { -"name" : "nightEnableCooler", -"default" : 0, -"description" : "Activate to use cooling (works only on cooled cameras).", +"name" : "nightenablecooler", +"default" : false, +"description" : "Enable to use cooling (on cooled cameras only).", "label" : "Cooling", "type" : "boolean", +"usage" : "capture", "display" : "_display", -"generic" : 1, -"advanced" : 1 +"action" : "reload", +"advanced" : true }, { -"name" : "nightTargetTemp", +"name" : "nighttargettemp", "minimum" : "_min", "maximum" : "_max", "default" : "_default", "description" : "Sensor's target temperature when cooler is enabled (degrees Celsius).", "label" : "Target Temp.", "type" : "integer", +"usage" : "capture", "display" : "_display", -"generic" : 1, -"advanced" : 1 +"booldependson" : "nightenablecooler", +"action" : "reload", +"advanced" : true }, { -"name" : "nightTuningFile", +"name" : "nighttuningfile", "default" : "", "description" : "Name of the night camera tuning file to use.", "label" : "Tuning File", "type" : "widetext", +"usage" : "capture", "display" : "_display", -"checkchanges" : 1, -"optional" : 1, -"advanced" : 1 +"checkchanges" : true, +"optional" : true, +"action" : "restart", +"advanced" : true }, + { -"name" : "DayAndNightHeader", -"description" : "Both daytime and nighttime settings", -"type" : "header", -"display" : 1, -"advanced" : 0 +"name" : "bothtab========================================", +"label" : "24 Hours", +"type" : "header-tab" +}, +{ +"name" : "dayandnightheader", +"label" : "Both day and night settings", +"type" : "header" +}, +{ +"name" : "daystokeep", +"minimum" : 0, +"maximum" : "none", +"default" : 14, +"description" : "Number of days of images and videos to keep on the Pi.", +"label" : "Days To Keep", +"type" : "integer", +"booldependson" : "savedaytimeimages OR savenighttimeimages" }, { "name" : "config", @@ -443,20 +503,23 @@ "description" : "Configuration file to use for settings.", "label" : "Configuration File", "type" : "widetext", -"display" : 1, -"checkchanges" : 1, -"optional" : 1, -"advanced" : 1 +"usage" : "capture", +"checkchanges" : true, +"optional" : true, +"action" : "reload", +"advanced" : true }, { -"name" : "extraArgs", +"name" : "extraargs", "default" : "", "description" : "Extra arguments to pass to the capture program that Allsky doesn't support.", "label" : "Extra Arguments", "type" : "widetext", +"usage" : "capture", "display" : "_display", -"optional" : 1, -"advanced" : 1 +"optional" : true, +"action" : "reload", +"advanced" : true }, { "name" : "saturation", @@ -466,8 +529,10 @@ "description" : "Changes the saturation level of the image.
The lowest level produces a black and white image.", "label" : "Saturation", "type" : "float", +"usage" : "capture", "display" : "_display", -"advanced" : 1 +"action" : "reload", +"advanced" : true }, { "name" : "contrast", @@ -477,8 +542,10 @@ "description" : "Changes the contrast of an image.", "label" : "Contrast", "type" : "float", +"usage" : "capture", "display" : "_display", -"advanced" : 1 +"action" : "reload", +"advanced" : true }, { "name" : "sharpness", @@ -488,8 +555,10 @@ "description" : "Changes the sharpness of an image.
Too sharp and images look unnatural.", "label" : "Sharpness", "type" : "float", +"usage" : "capture", "display" : "_display", -"advanced" : 1 +"action" : "reload", +"advanced" : true }, { "name" : "gamma", @@ -499,8 +568,10 @@ "description" : "Changes the difference between light and dark areas.
higher numbers increase contrast.", "label" : "Gamma", "type" : "integer", +"usage" : "capture", "display" : "_display", -"advanced" : 1 +"action" : "reload", +"advanced" : true }, { "name" : "offset", @@ -510,8 +581,10 @@ "description" : "Adds about 1/10 the specified number to every pixel to brighten everything.", "label" : "Offset", "type" : "integer", +"usage" : "capture", "display" : "_display", -"advanced" : 1 +"action" : "reload", +"advanced" : true }, { "name" : "aggression", @@ -521,19 +594,25 @@ "description" : "How much of a calculated exposure change should be made during Auto-Exposure?
Lower numbers smooth out changes but take longer to react to brightness changes.", "label" : "Aggression", "type" : "percent", +"usage" : "capture", "display" : "_display", -"advanced" : 1 +"booldependson" : "dayautoexposure OR nightautoexposure", +"action" : "reload", +"advanced" : true }, { "name" : "gaintransitiontime", "minimum" : 0, "maximum" : "none", "default" : "_default", -"description" : "Number of minutes over which to increase or decrease the gain when switching between day and night.
This helps smooth brightness differences. Only works if nighttime Auto-Gain is off. 0 disables transitions.", +"description" : "Number of minutes over which to increase or decrease the gain when switching between day and night.
This helps smooth brightness differences. 0 disables transitions.", "label" : "Gain Transition Time", "type" : "integer", +"usage" : "capture", "display" : "_display", -"advanced" : 1 +"booldependsoff" : "nightautogain", +"action" : "reload", +"advanced" : true }, { "name" : "width", @@ -543,8 +622,8 @@ "description" : "Image width in pixels. 0 = max sensor width.", "label" : "Image Width", "type" : "integer", -"display" : 1, -"advanced" : 0 +"usage" : "capture", +"action" : "reload" }, { "name" : "height", @@ -554,8 +633,8 @@ "description" : "Image height in pixels. 0 = max sensor height.", "label" : "Image Height", "type" : "integer", -"display" : 1, -"advanced" : 0 +"usage" : "capture", +"action" : "reload" }, { "name" : "type", @@ -563,11 +642,12 @@ "description" : "'auto' uses color if possible, otherwise the best mono mode supported.
RAW16 only works with PNG extension.", "label" : "Image Type", "type" : "select", -"display" : 1, +"usage" : "capture", "options" : [ "type_values" ], -"advanced" : 1 +"action" : "reload", +"advanced" : true }, { "name" : "quality", @@ -577,17 +657,19 @@ "description" : "JPG quality: 0-100. Higher numbers produce larger, clearer files.
PNG compression: 0-9. Higher numbers produce smaller files but take longer to save.", "label" : "Quality / Compression", "type" : "integer", -"display" : 1, -"advanced" : 0 +"usage" : "capture", +"action" : "reload" }, { "name" : "autousb", -"default" : 1, +"default" : true, "description" : "Automatically set the USB bandwidth.", "label" : "Auto USB Bandwidth", "type" : "boolean", +"usage" : "capture", "display" : "_display", -"advanced" : 1 +"action" : "reload", +"advanced" : true }, { "name" : "usb", @@ -597,8 +679,11 @@ "description" : "USB bandwidth.
If you get ASI_ERROR_TIMEOUT errors in the log file, try changing this number.", "label" : "USB Bandwidth", "type" : "integer", +"usage" : "capture", "display" : "_display", -"advanced" : 1 +"booldependsoff" : "autousb", +"action" : "reload", +"advanced" : true }, { "name" : "filename", @@ -606,9 +691,11 @@ "description" : "Extensions allowed: .jpg or .png.
WARNING: saving a .png file can take 10 or more seconds so be sure the time between successive images is long enough. To measure how long it takes your Pi to save a .png file, set the Debug Level to 4 and look in the log file.", "label" : "Filename", "type" : "text", -"display" : 1, -"checkchanges" : 1, -"advanced" : 1 +<"usage" : "capture", +"checkchanges" : true, +"action" : "reload", +"advanced" : true + }, { "name" : "rotation", @@ -616,11 +703,12 @@ "description" : "Set image rotation to enable proper orientation.", "label" : "Rotation", "type" : "select", +"usage" : "capture", "options" : [ "rotation_values" ], "display" : "_display", -"advanced" : 0 +"action" : "reload" }, { "name" : "flip", @@ -628,32 +716,33 @@ "description" : "Flips the image along an axis.", "label" : "Flip", "type" : "select", +"usage" : "capture", "options" : [ {"value" : 0, "label" : "None"}, {"value" : 1, "label" : "Horizontal"}, {"value" : 2, "label" : "Vertical"}, {"value" : 3, "label" : "Both"} ], -"display" : 1, -"advanced" : 0 +"action" : "reload" }, { "name" : "notificationimages", -"default" : 1, -"description" : "Activate to display notification images, e.g., 'Camera is off during the day'.
While these messages appear in the WebUI and Allsky Website, they are not saved so don't appear in timelapse, startrails, or keograms.", +"default" : true, +"description" : "Enable to display notification images, e.g., 'Camera is off during the day'.
While these messages appear in the WebUI and Allsky Website, they are not saved so don't appear in timelapse, startrails, or keograms.", "label" : "Notification Images", "type" : "boolean", -"display" : 1, -"advanced" : 0 +"usage" : "capture", +"action" : "reload" }, { -"name" : "consistentDelays", -"default" : 1, -"description" : "Activate to have the delays between images be a consistent length.
The time between the start of exposures will always be (max exposure + delay).", +"name" : "consistentdelays", +"default" : true, +"description" : "Enable to have the delays between images be a consistent length.
The time between the start of exposures will always be (max exposure + delay).", "label" : "Consistent Delays Between Images", "type" : "boolean", -"display" : 1, -"advanced" : 1 +"usage" : "capture", +"action" : "reload", +"advanced" : true }, { "name" : "timeformat", @@ -661,28 +750,43 @@ "description" : "Determines the format of the displayed time. Run 'man 3 strftime' to see the options.", "label" : "Time Format", "type" : "text", -"display" : 1, -"advanced" : 0 +"usage" : "capture", +"action" : "reload" +}, +{ +"name" : "temptype", +"default" : "C", +"description" : "Unit(s) to display temperature in.", +"label" : "Temperature Units", +"type" : "select", +"usage" : "capture", +"options" : [ + {"value" : "C", "label" : "Celcius"}, + {"value" : "F", "label" : "Fahrenheit"}, + {"value" : "B", "label" : "Both"} +], +"action" : "reload", +"advanced" : true }, { "name" : "latitude", "default" : "", -"description" : "Camera latitude - a number with a sign (-90 to +90) or a positive number with direction (N or S), for example: 60.7N.", +"description" : "Camera latitude: a signed number from -90 to +90, or an unsigned number with direction (N or S), for example: 60.7N.", "label" : "Latitude", "type" : "text", -"display" : 1, -"checkchanges" : 1, -"advanced" : 0 +"usage" : "capture", +"checkchanges" : true, +"action" : "reload" }, { "name" : "longitude", "default" : "", -"description" : "Camera longitude - a number with a sign (-180 to +180) or a positive number with a direction (E or W), for example: 135.05W.", +"description" : "Camera longitude: a signed number from -180 to +180, or an unsigned number with a direction (E or W), for example: 135.05W.", "label" : "Longitude", "type" : "text", -"display" : 1, -"checkchanges" : 1, -"advanced" : 0 +"usage" : "capture", +"checkchanges" : true, +"action" : "reload" }, { "name" : "angle", @@ -690,27 +794,25 @@ "description" : "Altitude of the Sun in degrees relative to the horizon at which to switch between day and night.", "label" : "Angle", "type" : "float", -"display" : 1, -"checkchanges" : 1, -"advanced" : 0 +"usage" : "capture", +"checkchanges" : true, +"action" : "reload" }, { -"name" : "takeDarkFrames", -"default" : 0, -"description" : "Activate to capture dark frames, which are used to decrease noise.
Continues taking dark frames until Allsky is restarted.", +"name" : "takedarkframes", +"default" : false, +"description" : "Enable to capture dark frames, which are used to decrease noise.
Continues taking dark frames until Allsky is restarted.", "label" : "Take Dark Frames", "type" : "boolean", -"display" : 1, -"advanced" : 0 +"usage" : "capture", +"action" : "stop" }, { -"name" : "useDarkFrames", -"default" : 0, +"name" : "usedarkframes", +"default" : false, "description" : "Enables dark frame subtraction if you have dark frames.", "label" : "Use Dark Frames", -"type" : "boolean", -"display" : 1, -"advanced" : 0 +"type" : "boolean" }, { "name" : "locale", @@ -718,17 +820,19 @@ "description" : "Your locale, used to determine what the thousands and decimal separators are.
Type locale at a command prompt to see your choices.", "label" : "Locale", "type" : "text", -"display" : 1, -"advanced" : 0 +"usage" : "capture", +"action" : "reload" }, { -"name" : "experimentalExposure", -"default" : 0, -"description" : "Activate to use a newer auto-exposure algorithm at night. Initial testing indictes the images taken during the day-to-night transition as well as at night have better exposures.
If you use this, please add a Discussion item describing your results - good or bad. We need the feedback.", +"name" : "experimentalexposure", +"default" : false, +"description" : "Enable to use an improved auto-exposure algorithm at night. Initial testing indictes the images taken during the day-to-night transition as well as at night have better exposures.
If you use this, please add a Discussion item describing your results - good or bad. We need the feedback.", "label" : "New Exposure Algorithm", "type" : "boolean", +"usage" : "capture", "display" : "_display", -"advanced" : 1 +"action" : "reload", +"advanced" : true }, { "name" : "histogrambox", @@ -736,8 +840,31 @@ "description" : "Size of the histogram box (X and Y in pixels) and offset from left and top (0-100 percent). Sizes must be even numbers.", "label" : "Histogram Box", "type" : "text", +"usage" : "capture", "display" : "_display", -"advanced" : 1 +"booldependson" : "dayautoexposure OR nightautoexposure", +"action" : "reload", +"advanced" : true +}, +{ +"name" : "thumbnailsizex", +"minimum" : 25, +"maximum" : "500", +"default" : 100, +"description" : "Width of image and video thumbnails.", +"label" : "Thumbnail Width", +"type" : "integer", +"booldependson" : "savedaytimeimages OR savenighttimeimages" +}, +{ +"name" : "thumbnailsizey", +"minimum" : 20, +"maximum" : "400", +"default" : 75, +"description" : "Height of image and video thumbnails.", +"label" : "Thumbnail Height", +"type" : "integer", +"booldependson" : "savedaytimeimages OR savenighttimeimages" }, { "name" : "debuglevel", @@ -745,6 +872,7 @@ "description" : "Debug level. 0 is errors only. 4 is for Allsky developers use.", "label" : "Debug Level", "type" : "select", +"usage" : "capture", "options" : [ {"value" : 0, "label" : 0}, {"value" : 1, "label" : 1}, @@ -752,161 +880,715 @@ {"value" : 3, "label" : 3}, {"value" : 4, "label" : 4} ], -"display" : 1, -"advanced" : 0 +"action" : "reload" +}, +{ +"name" : "uhubctlpath", +"default" : "", +"description" : "See documentation for a description of this setting", +"label" : "Path to uhubctl", +"type" : "text", +"display" : "_display" +}, +{ +"name" : "uhubctlport", +"minimum" : 0, +"maximum" : 10, +"default" : 2, +"description" : "See documentation for a description of this setting", +"label" : "USB Port of Camera", +"type" : "integer", +"display" : "_display", +"valuedependson" : "uhubctlpath!=''" }, { "name" : "newexposure", -"default" : 1, -"description" : "Activate to use the Allsky version 0.8 exposure method which decreases sensor temperature. If you see ASI_ERROR_TIMEOUTs in the log file, deactivate this.", +"default" : true, +"description" : "Enable to use the Allsky version 0.8 exposure method which decreases sensor temperature. If you see ASI_ERROR_TIMEOUTs in the log file, deactivate this.", "label" : "Version 0.8 Exposure", "type" : "boolean", +"usage" : "capture", "display" : "_display", -"advanced" : 1 +"action" : "reload", +"advanced" : true }, { -"name" : "useLogin", -"default" : 1, +"name" : "uselogin", +"default" : true, "description" : "Determines if you need to login to the WebUI or not.
If your Pi is accessible on the Internet, do NOT turn this off!!.", "label" : "Require WebUI Login", "type" : "boolean", -"display" : 1, -"advanced" : 1 +"advanced" : true }, { "name" : "alwaysshowadvanced", -"default" : 0, -"description" : "Activate to always show the advanced options.", +"default" : false, +"description" : "Enable to always show the advanced options.", "label" : "Always Show Advanced", "type" : "boolean", -"display" : 1, -"advanced" : 1 +"advanced" : true }, + { -"name" : "overlayHeader", -"description" : "Image overlay settings", -"type" : "header", -"display" : 1, -"advanced" : 0 +"name" : "postprocessingtab========================================", +"label" : "Post-processing", +"type" : "header-tab" +}, +{ +"name" : "postprocessingheader", +"label" : "Post-processing", +"type" : "header" }, { -"name" : "overlayMethod", +"name" : "imageuploadfrequency", +"minimum" : 0, +"maximum" : "none", +"default" : 1, +"description" : "How often should images be uploaded to an Allsky Website or remote server?
0 never uploads images, 1 uploads every frame, 2 every other frame, etc.", +"label" : "Upload Every X Images", +"type" : "integer", +"booldependson" : "uselocalwebsite OR useremotewebsite OR useremoteserver" +}, +{ +"name" : "imagecreatethumbnails", +"default" : true, +"description" : "Enable to create thumbnails of images. If you never look at them, unset this.", +"label" : "Create Image Thumbnails", +"type" : "boolean", +"booldependson" : "savedaytimeimages OR savenighttimeimages" +}, +{ +"name" : "imageresizewidth", +"minimum" : 2, +"maximum" : "none", "default" : 0, -"description" : "Determines how image overlays are done.
module supports a visual editor.
When set to module, the overlay settings below do NOT apply.", -"label" : "Overlay Method", +"description" : "Width of resized image before cropping. 0 means don't resize.", +"label" : "Image Resize Width", +"type" : "integer", +"checkchanges" : true, +"booldependson" : "takedaytimeimages OR takenighttimeimages", +"action" : "reload" +}, +{ +"name" : "imageresizeheight", +"minimum" : 2, +"maximum" : "none", +"default" : 0, +"description" : "Height of resized image before cropping. 0 means don't resize.", +"label" : "Image Resize Height", +"type" : "integer", +"checkchanges" : true, +"booldependson" : "takedaytimeimages OR takenighttimeimages", +"action" : "reload" +}, +{ +"name": "imagestretchamountdaytime", +"minimum": 0, +"maximum": 100, +"default": 0, +"description": "Amount to stretch daytime images.", +"label": "Stretch Amount", +"type": "integer", +"booldependson": "takedaytimeimages" +}, +{ +"name": "imagestretchmidpointdaytime", +"minimum": 0, +"maximum": 100, +"default": 0, +"description": "Mid point of image stretch.
Lower numbers brighten darker parts of image; higher numbers brighten lighter parts.", +"label": "Stretch mid point", +"type": "percent", +"booldependson": "takedaytimeimages" +}, +{ +"name": "imagestretchamountnighttime", +"minimum": 0, +"maximum": 100, +"default": 0, +"description": "Amount to stretch nighttime images.", +"label": "Stretch Amount", +"type": "integer", +"booldependson": "takenighttimeimages" +}, +{ +"name": "imagestretchmidpointnighttime", +"minimum": 0, +"maximum": 100, +"default": 0, +"description": "Mid point of image stretch.
Lower numbers brighten darker parts of image; higher numbers brighten lighter parts.", +"label": "Stretch mid point", +"type": "percent", +"booldependson": "takenighttimeimages" +}, +{ +"name" : "imagecroptop", +"minimum" : 0, +"maximum" : "_max", +"default" : 0, +"description" : "Number of pixels to crop off the top of image.", +"label" : "Image Crop Top", +"type" : "integer", +"checkchanges": true, +"booldependson": "takedaytimeimages OR takenighttimeimages" +}, +{ +"name" : "imagecropright", +"minimum" : 0, +"maximum" : "_max", +"default" : 0, +"description" : "Number of pixels to crop off the right side of image.", +"label" : "Image Crop Right", +"type" : "integer", +"checkchanges": true, +"booldependson": "takedaytimeimages OR takenighttimeimages" +}, +{ +"name" : "imagecropbottom", +"minimum" : 0, +"maximum" : "_max", +"default" : 0, +"description" : "Number of pixels to crop off the bottom of image.", +"label" : "Image Crop Bottom", +"type" : "integer", +"checkchanges": true, +"booldependson": "takedaytimeimages OR takenighttimeimages" +}, +{ +"name" : "imagecropleft", +"minimum" : 0, +"maximum" : "_max", +"default" : 0, +"description" : "Number of pixels to crop off the left side of image.", +"label" : "Image Crop Left", +"type" : "integer", +"checkchanges": true, +"booldependson": "takedaytimeimages OR takenighttimeimages" +}, +{ +"name" : "imageresizeuploadswidth", +"minimum" : 0, +"maximum" : "_max", +"default" : 0, +"description" : "Resized width of uploaded images.", +"label" : "Resize Uploaded Images Width", +"type" : "integer", +"booldependson" : "imageupload" +}, +{ +"name" : "imageresizeuploadsheight", +"minimum" : 0, +"maximum" : "_max", +"default" : 0, +"description" : "Resized height of uploaded images.", +"label" : "Resize Uploaded Images Height", +"type" : "integer", +"booldependson" : "imageupload" +}, +{ +"name": "imageremovebadlow", +"minimum": 0, +"maximum": 0.5, +"default": 0.1, +"description": "Delete images whose mean is below this value.
0 disables this check.", +"label": "Remove Bad Images Threshold Low", +"type": "float", +"booldependson" : "savedaytimeimages OR savenighttimeimages" +}, +{ +"name": "imageremovebadhigh", +"minimum": 0, +"maximum": 0.5, +"default": 0.9, +"description": "Delete images whose mean is above this value.
0 disables this check.", +"label": "Remove Bad Images Threshold High", +"type": "float", +"booldependson" : "savedaytimeimages OR savenighttimeimages" +}, + + +{ +"name" : "timelapsetab========================================", +"label" : "Timelapses", +"type" : "header-tab" +}, +{ +"name" : "timelapseheader", +"label" : "Timelapse settings", +"type" : "header" +}, +{ +"name" : "timelapsesubheader", +"label" : "Daily Timelapse Settings", +"type" : "header-sub" +}, +{ +"name" : "timelapsegenerate", +"default" : true, +"description" : "Enable to generate a timelapse video at the end of night.", +"label" : "Generate Timelapse", +"type" : "boolean", +"booldependson" : "savedaytimeimages OR savenighttimeimages" +}, +{ +"name": "timelapseupload", +"default": true, +"description": "Enable to upload the timelapse video to an Allsky Website or remote server.", +"label": "Upload Timelapse", +"type": "boolean", +"booldependson" : "timelapsegenerate" +}, +{ +"name": "timelapseuploadthumbnail", +"default": true, +"description": "Enable to upload the timelapse video thumbnail.", +"label": "Upload Timelapse Thumbnail", +"type": "boolean", +"booldependson" : "timelapsegenerate AND timelapseupload AND (uselocalwebsite OR useremotewebsite OR useremoteserver)" +}, +{ +"name": "timelapsewidth", +"minimum": 0, +"maximum": "_max", +"default": 0, +"description": "Width of resized timelapse video. 0 disables resize.", +"label": "Timelapse Width", +"type": "integer", +"booldependson" : "timelapsegenerate" +}, +{ +"name": "timelapseheight", +"minimum": 0, +"maximum": "_max", +"default": 0, +"description": "Height of resized timelapse video. 0 disables resize.", +"label": "Timelapse Height", +"type": "integer", +"booldependson" : "timelapsegenerate" +}, +{ +"name": "timelapsebitrate", +"minimum": 100, +"maximum": 10000, +"default": 2000, +"description": "Bitrate (in K) of timelapse video. Higher values produce higher quality but larger files.", +"label": "Timelapse Bitrate", +"type" : "integer", +"booldependson" : "timelapsegenerate" +}, +{ +"name": "timelapsefps", +"minimum": 1, +"maximum": 200, +"default": 25, +"description": "Frames Per Second (FPS) of timelapse video.", +"label": "Timelapse FPS", +"type": "integer", +"booldependson" : "timelapsegenerate" +}, +{ +"name" : "timelapsevcodec", +"default" : "libx264", +"description" : "Video encoder for timelapse. Rarely changed.", +"label" : "Timelapse VCODEC", +"type" : "text", +"comment": "check using ffmpeg -encoder looking at argument 2 for ' libxs264 '", +"booldependson" : "timelapsegenerate", +"checkchanges": true +}, +{ +"name" : "timelapsepixfmt", +"default" : "yuv420p", +"description" : "Pixel format for timelapse. Rarely changed.", +"label" : "Timelapse Pixel format", +"type" : "text", +"booldependson" : "timelapsegenerate", +"comment": "check using ffmpeg -pix_fmts looking at argument 2 for ' yuv420p '", +"checkchanges" : true +}, +{ +"name" : "timelapsefflog", +"default" : "Warning", +"description" : "Amount of information to display while creating a timelapse. Rarely changed.", +"label" : "Timelapse Log Level", "type" : "select", -"options" : [ - {"value" : 0, "label" : "legacy"}, - {"value" : 1, "label" : "module"} +"options": [ + { "value": "quiet", "label": "No output" }, + { "value": "fatal", "label": "Fatal Errors" }, + { "value": "error", "label": "Errors" }, + { "value": "warning", "label": "Warnings + Errors" }, + { "value": "info", "label": "Info + Warnings + Errors" } ], -"display" : 1, -"checkchanges" : 1, -"advanced" : 0 +"booldependson" : "timelapsegenerate" }, { -"name" : "showTime", -"default" : 1, -"description" : "Activate to display the time an image was taken on the overlay.", -"label" : "Show Time", +"name": "timelapsekeepsequence", +"default": false, +"description": "Enable to keep the list of files used in creating the timelapse video. Rarely changed.", +"label": "Keep Timelapse Sequence", +"type": "boolean", +"booldependson" : "timelapsegenerate" +}, +{ +"name" : "timelapseextraparameters", +"default" : "", +"description" : "Optional additional timelapse creation parameters. Run ffmpeg -? to see the options.", +"label" : "Timelapse Extra Parameters", +"type" : "widetext", +"optional" : true, +"booldependson" : "timelapsegenerate" +}, + +{ +"name" : "minitimelapseheader", +"label" : "Mini Timelapse Settings", +"type" : "header-sub" +}, +{ +"name" : "minitimelapsenumimages", +"minimum" : 0, +"maximum" : "none", +"default" : 0, +"description" : "Number of images in a mini-timelapse. 0 disables mini-timelapse creation.", +"label" : "Number Of Images", +"type" : "integer", +"booldependson" : "savedaytimeimages OR savenighttimeimages" +}, +{ +"name" : "minitimelapseforcecreation", +"default" : false, +"description" : "Create a mini-timelapse even if 'Number Of Images' isn't reached?", +"label" : "Force Creation", "type" : "boolean", -"display" : 1, -"advanced" : 0 +"valuedependson" : "minitimelapsenumimages=[1-9]*" }, { -"name" : "showTemp", -"default" : 1, -"description" : "Activate to display the camera sensor temperature on the overlay.", -"label" : "Show Temperature", +"name": "minitimelapsefrequency", +"minimum": 1, +"maximum": "none", +"default": 5, +"description": "Make a mini-timelapse every X images.
If you have a slow Pi or short delays between images, set this to a higher number (i.e., not as often).", +"label": "Mini-Timelapse Frequency", +"type": "integer", +"valuedependson" : "minitimelapsenumimages=[1-9]*" +}, +{ +"name": "minitimelapseupload", +"default": true, +"description": "Enable to upload the mini-timelapse video to an Allsky Website or remote server.", +"label": "Upload Mini-Timelapse", +"type": "boolean", +"valuedependson" : "minitimelapsenumimages=[1-9]*" +}, +{ +"name": "minitimelapseuploadthumbnail", +"default": true, +"description": "Enable to upload the mini-timelapse video thumbnail.", +"label": "Upload Mini-Timelapse Thumbnail", +"type": "boolean", +"booldependson" : "minitimelapseupload AND (uselocalwebsite OR useremotewebsite OR useremoteserver)", +"valuedependson" : "minitimelapsenumimages=[1-9]*" +}, +{ +"name": "minitimelapsewidth", +"minimum": 0, +"maximum": "_max", +"default": 0, +"description": "Width of resized mini-timelapse video. 0 disables resize.", +"label": "Mini-Timelapse Width", +"type": "integer", +"valuedependson" : "minitimelapsenumimages=[1-9]*" +}, +{ +"name": "minitimelapseheight", +"minimum": 0, +"maximum": "_max", +"default": 0, +"description": "Height of resized mini-timelapse video. 0 disables resize.", +"label": "Mini-Timelapse Height", +"type": "integer", +"valuedependson" : "minitimelapsenumimages=[1-9]*" +}, +{ +"name": "minitimelapsebitrate", +"minimum": 100, +"maximum": 10000, +"default": 1000, +"description": "Bitrate (in K) of mini-timelapse video. Higher values produce higher quality but larger files.", +"label": "Mini-Timelapse Bitrate", +"type": "integer", +"valuedependson" : "minitimelapsenumimages=[1-9]*" +}, +{ +"name": "minitimelapsefps", +"minimum": 1, +"maximum": 200, +"default": 5, +"description": "Frames Per Second (FPS) of mini-timelapse video.", +"label": "Mini-Timelapse FPS", +"type": "integer", +"valuedependson" : "minitimelapsenumimages=[1-9]*" +}, + +{ +"name" : "keogramstartrailstab========================================", +"label" : "Keogram and Startrails", +"type" : "header-tab" +}, +{ +"name" : "timelapseheader", +"label" : "Keogram and Startrails Settings", +"type" : "header" +}, +{ +"name" : "keogramsubheader", +"label" : "Keogram Settings", +"type" : "header-sub" +}, +{ +"name" : "keogramgenerate", +"default" : true, +"description" : "Enable to generate a keogram at the end of night.", +"label" : "Generate Keogram", "type" : "boolean", -"display" : "_display", -"advanced" : 0 +"booldependson" : "savedaytimeimages OR savenighttimeimages" }, { -"name" : "temptype", -"default" : "C", -"description" : "Unit(s) to display temperature in.", -"label" : "Temperature Units", +"name": "keogramupload", +"default": true, +"description": "Enable to upload the keogram to an Allsky Website or remote server.", +"label": "Upload Keogram", +"type": "boolean", +"booldependson" : "keogramgenerate AND (uselocalwebsite OR useremotewebsite OR useremoteserver)" +}, + +{ +"name" : "keogramexpand", +"default" : true, +"description" : "Enable to expand keograms to the image width.", +"label" : "Expand Keograms", +"type" : "boolean", +"booldependsoff" : "keogramgenerate" +}, +{ +"name" : "keogramfontname", +"default" : "Simplex", +"description" : "Font name.", +"label" : "Font Name", "type" : "select", "options" : [ - {"value" : "C", "label" : "Celcius"}, - {"value" : "F", "label" : "Fahrenheit"}, - {"value" : "B", "label" : "Both"} + {"value" : "simplex", "label" : "Simplex"}, + {"value" : "plain", "label" : "Plain"}, + {"value" : "duplex", "label" : "Duplex"}, + {"value" : "complex", "label" : "Complex"}, + {"value" : "complexsmall", "label" : "Complex Small"}, + {"value" : "triplex", "label" : "Triplex"}, + {"value" : "scriptsimplex", "label" : "Script Simplex"}, + {"value" : "scriptcomplex", "label" : "Script Complex"} ], -"display" : 1, -"advanced" : 1 +"booldependson" : "keogramgenerate" +}, +{ +"name" : "keogramfontcolor", +"default" : "#fff", +"description" : "Font color.", +"label" : "Font Color", +"type" : "color", +"booldependson" : "keogramgenerate" +}, +{ +"name" : "keogramfontsize", +"minimum" : 1.0, +"maximum" : 10.0, +"default" : 2.0, +"description" : "Font size.", +"label" : "Font Size", +"type" : "integer", +"booldependson" : "keogramgenerate" +}, +{ +"name" : "keogramlinethickness", +"minimum" : 1, +"maximum" : 5, +"default" : 3, +"description" : "Font line thickness.", +"label" : "Font Line Thickness", +"type" : "integer", +"booldependson" : "keogramgenerate" +}, +{ +"name" : "keogramextraparameters", +"default" : "", +"description" : "Optional additional keogram creation parameters.
Run ~/allsky/bin/keogram --help for a list of options.", +"label" : "Keogram Extra Parameters", +"type" : "widetext", +"optional" : true, +"booldependson" : "keogramgenerate" +}, + +{ +"name" : "startrailssubheader", +"label" : "Startrails Settings", +"type" : "header-sub" +}, +{ +"name" : "startrailsgenerate", +"default" : true, +"description" : "Enable to generate a startrails at the end of night.", +"label" : "Generate Startrails", +"type" : "boolean", +"booldependson" : "savedaytimeimages OR savenighttimeimages" +}, +{ +"name" : "startrailsbrightnessthreshold", +"minimum" : 0.0, +"maximum" : 1.0, +"default" : 0.1, +"description" : "Images with a brightness higher than this threshold will not be included in the startrails.", +"label" : "Startrails Brightness Threshold", +"type" : "float", +"booldependson" : "startrailsgenerate" +}, +{ +"name": "startrailsupload", +"default": true, +"description": "Enable to upload the startrails to an Allsky Website or remote server.", +"label": "Upload Startrails", +"type": "boolean", +"booldependson" : "startrailsgenerate AND (uselocalwebsite OR useremotewebsite OR useremoteserver)" +}, +{ +"name" : "startrailsextraparameters", +"default" : "", +"description" : "Optional additional startrails creation parameters.
Run ~/allsky/bin/startrails --help for a list of options.", +"label" : "Startrails Extra Parameters", +"type" : "widetext", +"optional" : true, +"booldependson" : "startrailsgenerate" }, + + { -"name" : "showExposure", +"name" : "overlaytab========================================", +"label" : "Overlay", +"type" : "header-tab" +}, +{ +"name" : "overlayheader", +"label" : "Image overlay settings", +"type" : "header" +}, +{ +"name" : "overlaymethod", "default" : 1, -"description" : "Activate to display the exposure length on the overlay.
If Auto-Exposure is set, '(auto)' will appear after the exposure time.", -"label" : "Show Exposure", +"description" : "Determines how image overlays are done.
module supports a visual editor and will be the only method in the next version of Allsky.", +"label" : "Overlay Method", +"type" : "select", +"usage" : "capture", +"options" : [ + {"value" : 0, "label" : "legacy"}, + {"value" : 1, "label" : "module"} +], +"checkchanges" : true, +"action" : "reload" +}, +{ +"name" : "showtime", +"default" : true, +"description" : "Enable to display the time an image was taken on the overlay.", +"label" : "Show Time", "type" : "boolean", -"display" : 1, -"advanced" : 0 +"usage" : "capture", +"booldependsoff" : "overlaymethod", +"action" : "reload" }, { -"name" : "showGain", -"default" : 0, -"description" : "Activate to display the camera gain on the overlay.
If Auto-Gain is set, '(auto)' will appear after the gain value.", -"label" : "Show Gain", +"name" : "showtemp", +"default" : true, +"description" : "Enable to display the camera sensor temperature on the overlay.", +"label" : "Show Temperature", "type" : "boolean", -"display" : 1, -"advanced" : 0 +"usage" : "capture", +"display" : "_display", +"booldependsoff" : "overlaymethod", +"action" : "reload" }, { -"name" : "showBrightness", -"default" : 0, -"description" : "Activate to display the Brightness number you set on the overlay.", -"label" : "Show Brightness", +"name" : "showexposure", +"default" : true, +"description" : "Enable to display the exposure length on the overlay.
If Auto-Exposure is set, '(auto)' will appear after the exposure time.", +"label" : "Show Exposure", "type" : "boolean", -"display" : 1, -"advanced" : 0 +"usage" : "capture", +"booldependsoff" : "overlaymethod", +"action" : "reload" }, { -"name" : "showUSB", -"default" : 0, -"description" : "Activate to display the USB Bandwidth number on the overlay.
This is primarily for troubleshooting.", +"name" : "showgain", +"default" : false, +"description" : "Enable to display the camera gain on the overlay.
If Auto-Gain is set, '(auto)' will appear after the gain value.", +"label" : "Show Gain", +"type" : "boolean", +"usage" : "capture", +"booldependsoff" : "overlaymethod", +"action" : "reload" +}, +{ +"name" : "showusb", +"default" : false, +"description" : "Enable to display the USB Bandwidth number on the overlay.
This is primarily for troubleshooting.", "label" : "Show USB", "type" : "boolean", +"usage" : "capture", "display" : "_display", -"advanced" : 1 +"booldependsoff" : "overlaymethod", +"action" : "reload", +"advanced" : true }, { -"name" : "showMean", -"default" : 0, -"description" : "Activate to display the calculated mean image brightness on the overlay.", +"name" : "showmean", +"default" : false, +"description" : "Enable to display the calculated mean image brightness on the overlay.", "label" : "Show Mean Brightness", "type" : "boolean", -"display" : 1, -"advanced" : 1 +"usage" : "capture", +"booldependsoff" : "overlaymethod", +"action" : "reload", +"advanced" : true }, { "name" : "showhistogrambox", -"default" : 0, -"description" : "Activate to show an outline of the histogram box, useful for determining what the 'Histogram Box' numbers should be.", +"default" : false, +"description" : "Enable to show an outline of the histogram box, useful for determining what the 'Histogram Box' numbers should be.", "label" : "Show Histogram Box", "type" : "boolean", +"usage" : "capture", "display" : "_display", -"advanced" : 1 +"booldependsoff" : "overlaymethod", +"action" : "reload", +"advanced" : true }, { -"name" : "showFocus", -"default" : 0, -"description" : "Activate to display a focus metric to help you focus the camera.
This is only usefull while focusing.", +"name" : "showfocus", +"default" : false, +"description" : "Enable to display a focus metric to help you focus the camera.
This is only usefull while focusing.", "label" : "Show Focus Metric", "type" : "boolean", -"display" : 1, -"advanced" : 1 -}, +"usage" : "capture", +"booldependsoff" : "overlaymethod", +"action" : "reload", +"advanced" : true +, { "name" : "text", "default" : "", "description" : "Text that appears below the optional time with the same color and size.", "label" : "Text Overlay", "type" : "widetext", -"display" : 1, -"optional" : 1, -"advanced" : 0 +"usage" : "capture", +"optional" : true, +"booldependsoff" : "overlaymethod", +"action" : "reload" }, { "name" : "extratext", @@ -914,10 +1596,11 @@ "description" : "Extra Text File (enter full path to the file).
Leave blank if NOT using an extra text file.", "label" : "Extra Text File", "type" : "widetext", -"display" : 1, -"checkchanges" : 1, -"optional" : 1, -"advanced" : 0 +"usage" : "capture", +"checkchanges" : true, +"optional" : true, +"booldependsoff" : "overlaymethod", +"action" : "reload" }, { "name" : "extratextage", @@ -927,8 +1610,10 @@ "description" : "The maximum age of the Extra Text File in seconds.
After this time the contents of the file will no longer be displayed. Set to 0 to always display.", "label" : "Max Age Of Extra", "type" : "integer", -"display" : 1, -"advanced" : 0 +"usage" : "capture", +"valuedependson" : "extratext=*", +"booldependsoff" : "overlaymethod", +"action" : "reload" }, { "name" : "textlineheight", @@ -938,8 +1623,9 @@ "description" : "The line height of the text in pixels. If increasing the font size causes the text to overlap then increase this number.", "label" : "Line Height", "type" : "integer", -"display" : 1, -"advanced" : 0 +"usage" : "capture", +"booldependsoff" : "overlaymethod", +"action" : "reload" }, { "name" : "textx", @@ -949,8 +1635,9 @@ "description" : "Text will begin this many pixels from the left.", "label" : "Text X", "type" : "integer", -"display" : 1, -"advanced" : 0 +"usage" : "capture", +"booldependsoff" : "overlaymethod", +"action" : "reload" }, { "name" : "texty", @@ -960,8 +1647,9 @@ "description" : "Text will begin this many pixels from the top.", "label" : "Text Y", "type" : "integer", -"display" : 1, -"advanced" : 0 +"usage" : "capture", +"booldependsoff" : "overlaymethod", +"action" : "reload" }, { "name" : "fontname", @@ -969,6 +1657,7 @@ "description" : "Font name for the text overlay. Used for both large and small text.", "label" : "Font Name", "type" : "select", +"usage" : "capture", "options" : [ {"value" : 0, "label" : "Simplex"}, {"value" : 1, "label" : "Plain"}, @@ -979,8 +1668,8 @@ {"value" : 6, "label" : "Script Simplex"}, {"value" : 7, "label" : "Script Complex"} ], -"display" : 1, -"advanced" : 0 +"booldependsoff" : "overlaymethod", +"action" : "reload" }, { "name" : "fontcolor", @@ -989,8 +1678,9 @@ "description" : "Blue, Green, and Red (BGR) values of text color.
NOTE: When using RAW 16 only the first two numbers are used i.e., 255 128 0.", "label" : "Font Color", "type" : "text", -"display" : 1, -"advanced" : 0 +"usage" : "capture", +"booldependsoff" : "overlaymethod", +"action" : "reload" }, { "name" : "smallfontcolor", @@ -998,8 +1688,9 @@ "description" : "BGR value of text color.
NOTE: When using RAW 16 only the first two numbers are used i.e., 255 128 0.", "label" : "Small Font Color", "type" : "text", -"display" : 1, -"advanced" : 0 +"usage" : "capture", +"booldependsoff" : "overlaymethod", +"action" : "reload" }, { "name" : "fonttype", @@ -1007,13 +1698,14 @@ "description" : "Choose between smooth font lines (antialiased), 8-pixel, or 4 pixel connectivity (harder edges).", "label" : "Font Smoothness", "type" : "select", +"usage" : "capture", "options" : [ {"value" : 0, "label" : "Antialiased"}, {"value" : 1, "label" : "8 connected"}, {"value" : 2, "label" : "4 connected"} ], -"display" : 1, -"advanced" : 0 +"booldependsoff" : "overlaymethod", +"action" : "reload" }, { "name" : "fontsize", @@ -1023,8 +1715,9 @@ "description" : "Text Font Size. The time, if displayed, will use this size; other text displayed will be about 20% smaller.", "label" : "Font Size", "type" : "float", -"display" : 1, -"advanced" : 0 +"usage" : "capture", +"booldependsoff" : "overlaymethod", +"action" : "reload" }, { "name" : "fontline", @@ -1034,77 +1727,528 @@ "description" : "How thick the text line should be.", "label" : "Font Weight", "type" : "integer", -"display" : 1, -"advanced" : 0 +"usage" : "capture", +"booldependsoff" : "overlaymethod", +"action" : "reload" }, { "name" : "outlinefont", -"default" : 0, +"default" : false, "description" : "Adds a black outline to the text overlay to improve contrast.", "label" : "Use Outline Font", "type" : "boolean", -"display" : 1, -"advanced" : 0 +"usage" : "capture", +"booldependsoff" : "overlaymethod", +"action" : "reload" }, + { -"name" : "AllskyMapHeader", -"description" : "Allsky Map and Website Settings", -"type" : "header", -"display" : 1, -"advanced" : 0 +"name" : "websitetab========================================", +"label" : "Website", +"type" : "header-tab" }, { -"name" : "displaySettings", -"default" : 0, -"description" : "Activate to add a link to the Allsky Website to display the settings on this page.
Only applies if you have a local or remote Allsky Website.", +"name" : "websiteheader", +"label" : "Website and Remote Server Settings", +"type" : "header" +}, +{ +"name" : "displaysettings", +"default" : false, +"description" : "Enable to add a link on the Allsky Website to display the settings on this page.", "label" : "Display Settings", "type" : "boolean", -"display" : 1, -"checkchanges" : 1, -"advanced" : 0 +"checkchanges" : true, +"booldependson" : "uselocalwebsite OR useremotewebsite OR useremoteserver" }, { -"name" : "showonmap", +"name" : "uselocalwebsite", +"description" : "Enable to use the local Allsky Website. No other settings are needed.", +"default" : false, +"label" : "Use Local Website", +"type" : "boolean", +"popup-yesno" : "Do you want to remove all saved images, keograms, startrails, and videos in the local Website?", +"popup-yesno-value" : 0 +}, +{ +"name" : "daystokeeplocalwebsite", +"minimum" : 0, +"maximum" : "none", "default" : 0, -"description" : "Activate to have your camera appear on the Allsky Map .", -"label" : "Show on Map", +"description" : "Number of days of images and videos to keep on the Pi's Website.", +"label" : "Days To Keep on Pi Website", +"type" : "integer", +"booldependson" : "uselocalwebsite" +}, +{ +"name" : "remotewebsiteheader", +"label" : "Remote Website Settings", +"type" : "header-sub" +}, +{ +"name" : "useremotewebsite", +"default" : false, +"description" : "Enable to use a remote Allsky Website.
See the Allsky Documentation for how to install the Website.", +"label" : "Use Remote Website", +"type" : "boolean", +"checkchanges" : true +}, +{ +"name" : "daystokeepremotewebsite", +"minimum" : 0, +"maximum" : "none", +"default" : 0, +"description" : "Number of days of images and videos to keep on the remote Website.", +"label" : "Days To Keep on Remote Website", +"type" : "integer", +"booldependson" : "useremotewebsite" +}, +{ +"name" : "remotewebsiteprotocol", +"default" : "ftps", +"description" : "Protocol for remote Website", +"label" : "Protocol", +"type" : "select", +"options" : [ + {"value" : "ftps", "label" : "ftps"}, + {"value" : "ftp", "label" : "ftp"}, + {"value" : "sftp", "label" : "sftp"}, + {"value" : "scp", "label" : "scp"}, + {"value" : "S3", "label" : "s3"}, + {"value" : "GCS", "label" : "gcs"} +], +"checkchanges" : true, +"booldependson" : "useremotewebsite" +}, +{ +"name" : "REMOTEWEBSITE_HOST", +"default" : "", +"description" : "Name of server hosting the remote Allsky Website.", +"label" : "Server Name", +"type" : "widetext", +"checkchanges" : true, +"source" : "ALLSKY_ENV", +"booldependson" : "useremotewebsite", +"optional" : true +}, +{ +"name" : "REMOTEWEBSITE_PORT", +"default" : "", +"description" : "Optional port required by server. Rarely required.", +"label" : "Port", +"type" : "integer", +"checkchanges" : true, +"source" : "ALLSKY_ENV", +"booldependson" : "useremotewebsite", +"optional" : true +}, +{ +"name" : "REMOTEWEBSITE_USER", +"default" : "", +"description" : "Username of the login on the remote server.", +"label" : "User Name", +"type" : "text", +"checkchanges" : true, +"source" : "ALLSKY_ENV", +"booldependson" : "useremotewebsite", +"optional" : true +}, +{ +"name" : "REMOTEWEBSITE_PASSWORD", +"default" : "", +"description" : "Password of the login on the remote server.", +"label" : "Password", +"type" : "password", +"checkchanges" : true, +"source" : "ALLSKY_ENV", +"booldependson" : "useremotewebsite", +"optional" : true +}, +{ +"name" : "REMOTEWEBSITE_LFTP_COMMANDS", +"default" : "", +"description" : "Special commands needed when connecting to an FTP server.", +"label" : "FTP Commands", +"type" : "text", +"checkchanges" : true, +"source" : "ALLSKY_ENV", +"booldependson" : "useremotewebsite", +"valuedependson" : "remotewebsiteprotocol=*ftp*", +"optional" : true +}, +{ +"name" : "remotewebsiteimagedir", +"default" : "allsky", +"description" : "Name of the top-level remote directory where images go.", +"label" : "Image Directory", +"type" : "text", +"checkchanges" : true, +"booldependson" : "useremotewebsite", +"valuedependson" : "remotewebsiteprotocol=*ftp*", +"optional" : true +}, +{ +"name" : "REMOTEWEBSITE_SSH_KEY_FILE", +"default" : "", +"description" : "Path on the Pi to the SSH key file.", +"label" : "SSH Key File", +"type" : "widetext", +"checkchanges" : true, +"source" : "ALLSKY_ENV", +"booldependson" : "useremotewebsite", +"valuedependson" : "remotewebsiteprotocol=scp", +"optional" : true +}, +{ +"name" : "REMOTEWEBSITE_AWS_CLI_DIR", +"default" : "${HOME}/.local/bin", +"description" : "AWS CLI directory where the AWS CLI tools are installed.", +"label" : "AWS CLI Directory", +"type" : "widetext", +"checkchanges" : true, +"source" : "ALLSKY_ENV", +"booldependson" : "useremotewebsite", +"valuedependson" : "remotewebsiteprotocol=s3", +"optional" : true +}, +{ +"name" : "REMOTEWEBSITE_S3_BUCKET", +"default" : "allskybucket", +"description" : "Name of S3 bucket where files will be upload.", +"label" : "S3 Bucket", +"type" : "text", +"checkchanges" : true, +"source" : "ALLSKY_ENV", +"booldependson" : "useremotewebsite", +"valuedependson" : "remotewebsiteprotocol=s3", +"optional" : true +}, +{ +"name" : "REMOTEWEBSITE_S3_ACL", +"default" : "private", +"description" : "S3 Access Control List (ACL).", +"label" : "S3 ACL", +"type" : "text", +"checkchanges" : true, +"source" : "ALLSKY_ENV", +"booldependson" : "useremotewebsite", +"valuedependson" : "remotewebsiteprotocol=s3", +"optional" : true +}, +{ +"name" : "REMOTEWEBSITE_GCS_BUCKET", +"default" : "allskybucket", +"description" : "Name of GCS bucket where files will be upload.", +"label" : "GCS Bucket", +"type" : "text", +"checkchanges" : true, +"source" : "ALLSKY_ENV", +"booldependson" : "useremotewebsite", +"valuedependson" : "remotewebsiteprotocol=gcs", +"optional" : true +}, +{ +"name" : "REMOTEWEBSITE_GCS_ACL", +"default" : "private", +"description" : "GCS Access Control List (ACL).", +"label" : "GCS ACL", +"type" : "text", +"checkchanges" : true, +"source" : "ALLSKY_ENV", +"booldependson" : "useremotewebsite", +"valuedependson" : "remotewebsiteprotocol=gcs", +"optional" : true +}, +{ +"name" : "remotewebsiteimageuploadoriginalname", +"default" : false, +"description" : "Enable to upload images using YYYYMMDDHHMMSS naming.", +"label" : "Upload With Original Name", "type" : "boolean", -"display" : 1, -"checkchanges" : 1, -"advanced" : 0 +"valuedependson" : "imageuploadfrequency=[1-9]*" +}, +{ +"name" : "remotewebsitevideodestinationname", +"default" : "", +"description" : "Optional name of the remote video file.", +"label" : "Remote Video File Name", +"type" : "text", +"booldependson" : "useremotewebsite", +"optional" : true }, { -"name" : "websiteurl", +"name" : "remotewebsitekeogramdestinationname", "default" : "", -"description" : "The URL of your Allsky Website, for example: https://www.thomasjacquin.com/allsky.
If your camera is not accessible on the Internet, leave this field empty.
Must begin with http or https.", +"description" : "Optional name of the remote keogram file.", +"label" : "Remote Keogram File Name", +"type" : "text", +"booldependson" : "useremotewebsite", +"optional" : true +}, +{ +"name" : "remotewebsitestartrailsdestinationname", +"default" : "", +"description" : "Optional name of the remote startrails file.", +"label" : "Remote Startrails Name", +"type" : "text", +"booldependson" : "useremotewebsite", +"optional" : true +}, +{ +"name" : "remotewebsiteurl", +"default" : "", +"description" : "The URL of your Allsky Website, for example: https://mywebsite.com/allsky.
Must begin with http or https.", "label" : "Website URL", "type" : "widetext", -"display" : 1, -"checkchanges" : 1, -"optional" : 1, -"advanced" : 0 +"checkchanges" : true, +"booldependson" : "showonmap OR useremotewebsite OR remotewebsiteimageurl", +"optional" : true }, { -"name" : "imageurl", +"name" : "remotewebsiteimageurl", "default" : "", -"description" : "The URL of the image on your Allsky Website, for example: https://wwww.thomasjacquin.com/allsky/image.jpg.
Right-click on the image and select Copy Image Address.
If your camera is not accessible on the Internet, leave this field empty.
Must begin with http or https.", +"description" : "The URL of the image on your Allsky Website, for example: https://mywebsite.com/allsky/image.jpg.
Must begin with http or https.", "label" : "Image URL", "type" : "widetext", -"display" : 1, -"checkchanges" : 1, -"optional" : 1, -"advanced" : 0 +"checkchanges" : true, +"booldependson" : "showonmap OR useremotewebsite OR remotewebsiteurl", +"optional" : true +}, + + +{ +"name" : "remoteserverheader", +"label" : "Remote Server Settings", +"type" : "header-sub" +}, +{ +"name" : "useremoteserver", +"default" : false, +"description" : "This is a remote server NOT running the Allsky Website.
See the documentation for the necessary directory layout.", +"label" : "Use Remote Server", +"type" : "boolean", +"checkchanges" : true +}, +{ +"name" : "remoteserverprotocol", +"default" : "ftps", +"description" : "Protocol for remote server", +"label" : "Protocol", +"type" : "select", +"options" : [ + {"value" : "ftps", "label" : "ftps"}, + {"value" : "ftp", "label" : "ftp"}, + {"value" : "sftp", "label" : "sftp"}, + {"value" : "scp", "label" : "scp"}, + {"value" : "S3", "label" : "s3"}, + {"value" : "GCS", "label" : "gcs"} +], +"checkchanges" : true, +"booldependson" : "useremoteserver" +}, +{ +"name" : "REMOTESERVER_HOST", +"default" : "", +"description" : "Name of remote server NOT hosting an Allsky Website.", +"label" : "Server Name", +"type" : "widetext", +"checkchanges" : true, +"source" : "ALLSKY_ENV", +"booldependson" : "useremoteserver", +"optional" : true +}, +{ +"name" : "REMOTESERVER_PORT", +"default" : "", +"description" : "Optional port required by server. Rarely required.", +"label" : "Port", +"type" : "integer", +"checkchanges" : true, +"source" : "ALLSKY_ENV", +"booldependson" : "useremoteserver", +"optional" : true +}, +{ +"name" : "REMOTESERVER_USER", +"default" : "", +"description" : "Username of the login on the remote server.", +"label" : "User Name", +"type" : "text", +"checkchanges" : true, +"source" : "ALLSKY_ENV", +"booldependson" : "useremoteserver", +"optional" : true +}, +{ +"name" : "REMOTESERVER_PASSWORD", +"default" : "", +"description" : "Password of the remote server login.", +"label" : "Password", +"type" : "password", +"checkchanges" : true, +"source" : "ALLSKY_ENV", +"booldependson" : "useremoteserver", +"optional" : true +}, +{ +"name" : "REMOTESERVER_LFTP_COMMANDS", +"default" : "", +"description" : "Special commands needed when connecting to an FTP server.", +"label" : "FTP Commands", +"type" : "text", +"checkchanges" : true, +"source" : "ALLSKY_ENV", +"booldependson" : "useremoteserver", +"valuedependson" : "remoteserverprotocol=*ftp*", +"optional" : true +}, +{ +"name" : "REMOTESERVER_SSH_KEY_FILE", +"default" : "", +"description" : "Path on the Pi to the SSH key file.", +"label" : "SSH Key File", +"type" : "widetext", +"checkchanges" : true, +"source" : "ALLSKY_ENV", +"booldependson" : "useremoteserver", +"valuedependson" : "remoteserverprotocol=scp", +"optional" : true +}, +{ +"name" : "REMOTESERVER_AWS_CLI_DIR", +"default" : "${HOME}/.local/bin", +"description" : "AWS CLI directory where the AWS CLI tools are installed.", +"label" : "AWS CLI Directory", +"type" : "widetext", +"checkchanges" : true, +"source" : "ALLSKY_ENV", +"booldependson" : "useremoteserver", +"valuedependson" : "remoteserverprotocol=s3", +"optional" : true +}, +{ +"name" : "REMOTESERVER_S3_BUCKET", +"default" : "allskybucket", +"description" : "Name of S3 bucket where files will be upload.", +"label" : "S3 Bucket", +"type" : "text", +"checkchanges" : true, +"source" : "ALLSKY_ENV", +"booldependson" : "useremoteserver", +"valuedependson" : "remoteserverprotocol=s3", +"optional" : true +}, +{ +"name" : "REMOTESERVER_S3_ACL", +"default" : "private", +"description" : "S3 Access Control List (ACL).", +"label" : "S3 ACL", +"type" : "text", +"checkchanges" : true, +"source" : "ALLSKY_ENV", +"booldependson" : "useremoteserver", +"valuedependson" : "remoteserverprotocol=s3", +"optional" : true +}, +{ +"name" : "REMOTESERVER_GCS_BUCKET", +"default" : "allskybucket", +"description" : "Name of GCS bucket where files will be upload.", +"label" : "GCS Bucket", +"type" : "text", +"checkchanges" : true, +"source" : "ALLSKY_ENV", +"booldependson" : "useremoteserver", +"valuedependson" : "remoteserverprotocol=gcs", +"optional" : true +}, +{ +"name" : "REMOTESERVER_GCS_ACL", +"default" : "private", +"description" : "S3 Access Control List (ACL).", +"label" : "S3 ACL", +"type" : "text", +"checkchanges" : true, +"source" : "ALLSKY_ENV", +"booldependson" : "useremoteserver", +"valuedependson" : "remoteserverprotocol=gcs", +"optional" : true +}, +{ +"name" : "remoteserverimagedir", +"default" : "", +"description" : "Name of the top-level remote directory where images go.", +"label" : "Image Directory", +"type" : "text", +"checkchanges" : true, +"booldependson" : "useremoteserver", +"optional" : true +}, +{ +"name" : "remoteserverimageuploadoriginalname", +"default" : false, +"description" : "Enable to upload images using YYYYMMDDHHMMSS naming.", +"label" : "Upload With Original Name", +"type" : "boolean", +"valuedependson" : "imageuploadfrequency=[1-9]*" +}, +{ +"name" : "remoteservervideodestinationname", +"default" : "", +"description" : "Optional name of the remote video file.", +"label" : "Remote Video File Name", +"type" : "text", +"booldependson" : "useremoteserver", +"optional" : true +}, +{ +"name" : "remoteserverkeogramdestinationname", +"default" : "", +"description" : "Optional name of the remote keogram file.", +"label" : "Remote Keogram File Name", +"type" : "text", +"booldependson" : "useremoteserver", +"optional" : true +}, +{ +"name" : "remoteserverstartrailsdestinationname", +"default" : "", +"description" : "Optional name of the remote startrails file.", +"label" : "Remote Startrails File Name", +"type" : "text", +"booldependson" : "useremoteserver", +"optional" : true +}, + +{ +"name" : "allskymaptab========================================", +"label" : "Map", +"type" : "header-tab" +}, +{ +"name" : "allskymapheader", +"label" : "Allsky Map Settings", +"type" : "header" +}, +{ +"name" : "showonmap", +"default" : false, +"description" : "Enable to have your camera appear on the Allsky Map .", +"label" : "Show on Map", +"type" : "boolean", +"checkchanges" : true, +"booldependson" : "uselocalwebsite OR useremotewebsite" }, + { "name" : "location", "default" : "", -"description" : "The location of your camera, for example: Whitehorse, YT.
No need to enter country since it'll be obvious looking at the map.
This setting and the remaining ones also appear on the Allsky Website, if installed.", +"description" : "The location of your camera, for example: Whitehorse, YT.
This setting and the remaining ones also appear on the Allsky Website, if installed.", "label" : "Location", "type" : "widetext", -"display" : 1, -"checkchanges" : 1, -"optional" : 1, -"advanced" : 0 +"checkchanges" : true, +"optional" : true, +"booldependson" : "showonmap OR useremotewebsite OR useremoteserver" }, { "name" : "owner", @@ -1112,10 +2256,9 @@ "description" : "The owner of the camera. It can be your name or an association or observatory, etc.", "label" : "Owner", "type" : "text", -"display" : 1, -"checkchanges" : 1, -"optional" : 1, -"advanced" : 0 +"checkchanges" : true, +"optional" : true, +"booldependson" : "showonmap OR useremotewebsite OR useremoteserver" }, { "name" : "camera", @@ -1123,10 +2266,9 @@ "description" : "The type and model of your camera, for example: ZWO 224MC or RPi HQ.", "label" : "Camera", "type" : "widetext", -"display" : 1, -"checkchanges" : 1, -"optional" : 1, -"advanced" : 0 +"checkchanges" : true, +"optional" : true, +"booldependson" : "showonmap OR useremotewebsite OR useremoteserver" }, { "name" : "lens", @@ -1134,10 +2276,9 @@ "description" : "The lens you're using on your camera, for example: Arecont 1.55.", "label" : "Lens", "type" : "text", -"display" : 1, -"checkchanges" : 1, -"optional" : 1, -"advanced" : 0 +"checkchanges" : true, +"optional" : true, +"booldependson" : "showonmap OR useremotewebsite OR useremoteserver" }, { "name" : "computer", @@ -1145,20 +2286,25 @@ "description" : "The computer running your allsky camera, for example: Raspberry Pi 3, 4 GB.", "label" : "Computer", "type" : "widetext", -"display" : 1, -"checkchanges" : 1, -"optional" : 1, -"advanced" : 0 +"checkchanges" : true, +"optional" : true, +"booldependson" : "showonmap OR useremotewebsite OR useremoteserver" }, + { -"name" : "CameraHeading", -"description" : "Camera Type", +"name" : "cameratab========================================", +"label" : "Camera Type", +"type" : "header-tab", +"advanced" : true +}, +{ +"name" : "cameraheading", +"label" : "Camera Type", "type" : "header", -"display" : 1, -"advanced" : 1 +"advanced" : true }, { -"name" : "cameraType", +"name" : "cameratype", "default" : "", "description" : "The type of camera you are using. Refresh re-sets the Camera Model and is useful if you change cameras of the same Camera Type.", "label" : "Camera Type", @@ -1168,32 +2314,57 @@ {"value" : "ZWO", "label" : "ZWO"}, {"value" : "Refresh", "label" : "Refresh"} ], -"display" : 1, -"checkchanges" : 1, -"advanced" : 1 +"checkchanges" : true, +"action" : "restart", +"advanced" : true }, { -"name" : "cameraModel", +"name" : "cameramodel", "default" : "", "description" : "The model of camera you are using. Cannot be changed here.", "label" : "Camera Model", "type" : "readonly", -"display" : 1, -"advanced" : 1 +"action" : "restart", +"advanced" : true }, { -"name" : "cameraNumber", +"name" : "cameranumber", "minimum" : 0, "maximum" : "none", "default" : 0, -"description" : "If multiple cameras are connected to the Pi, this is the camera number (starts at 0).", +"description" : "IF multiple cameras are connected to the Pi, this is the camera number (starts at 0).", "label" : "Camera Number", "type" : "integer", -"display" : "_display", -"display" : 0, -"checkchanges" : 1, -"advanced" : 1 +"usage" : "capture", +"xxxdisplay" : "_display", +"display" : false, +"optional" : true, +"checkchanges" : true, +"action" : "restart", +"advanced" : true +}, + + +{ +"name" : "webuitab========================================", +"label" : "WebUI", +"type" : "header-tab" +}, +{ +"name" : "webuiheading", +"label" : "WebUI Configuration", +"type" : "header", +"advanced" : true +}, +{ +"name" : "webuidatafiles", +"default" : "", +"description" : "See documentation for a description of this setting", +"label" : "Items to add to WebUI", +"type" : "widetext", +"optional" : true }, + { "name" : "XX_END_XX" } diff --git a/html/documentation/basics/Pi.html b/html/documentation/basics/Pi.html index a1b813525..9a4073f1b 100644 --- a/html/documentation/basics/Pi.html +++ b/html/documentation/basics/Pi.html @@ -40,7 +40,7 @@

Operating Systems

The two most common versions of Linux for the Pi are Buster, which was released several years ago, and Bullseye which was released late 2021. -Allsky runs on both operating systems but use Bullseye if possible, +Allsky runs on both operating systems but use Bullseye if possible, especially if you have an RPi camera. Allsky works on both the 32-bit and 64-bit versions of Bullseye; there was no official 64-bit version of Buster and Allsky is not supported on it. @@ -48,11 +48,13 @@

Operating Systems

There are different distributions of Linux (called "distros") from various companies. While they are basically the same Linux, -there can be subtle differences and the administrative commands can vary. +there are subtle differences and the commands that Allsky uses can vary.

-The recommended Linux distro is Debian. +The recommended operating system for Allsky is Pi OS which is based +on the Debian distro. We can't guarantee Allsky will run on other distros. +
To see which operating system you have, enter lsb_release -a.
diff --git a/html/documentation/css/custom.css b/html/documentation/css/custom.css index 3de630ad1..4fc1f2c38 100644 --- a/html/documentation/css/custom.css +++ b/html/documentation/css/custom.css @@ -80,6 +80,21 @@ .settingInput { /* Input field in Allsky Settings page */ width: 120px; } +.settingInputSelect { + padding: 0px 3px 0px 3px; + text-align: right; +} +.settingInputTextNumber { + padding: 0px 3px 0px 0px; + text-align: right; +} +.settingInputWidetext { + padding: 6px 5px; +} +.settingInputBoolean { + margin-bottom: -3px !important; + border-radius: 4px; +} .info-item { width: 140px; @@ -422,11 +437,34 @@ table .alert-dismissable { text-align: center; font-weight: bold; font-size: 1.25em; - padding: 3px 0 3px 0; + padding: 5px 0px; } .dark .settingsHeader { background-color: #555; } +.subSettingsHeader { + text-align: center; + font-weight: bold; + font-size: 1.05em; + padding: 0; +} +.subSettingsHeader div { + margin: 0 3em 0 3em; + background-color: #aac; +} +.dark .subSettingsHeader { + background-color: #339 +} +.columnHeader { + background-color: #aac; + text-align: center; + font-weight: bold; + font-size: 1.05em; + padding: 0; +} +.dark .columnHeader { + background-color: #339 +} .boxShadow { -webkit-box-shadow: 2px 3px rgb(0,0,0,15%); diff --git a/html/documentation/css/documentation.css b/html/documentation/css/documentation.css index 7c488ad04..f8923b4ab 100644 --- a/html/documentation/css/documentation.css +++ b/html/documentation/css/documentation.css @@ -342,6 +342,12 @@ body { margin-top: 5px; margin-bottom: 5px; } +.markdown-body table td.subnote { + background-color: var(--color-accent-muted); + color: var(--color-accent-emphasis); + text-align: center; + margin: 0 3em 0 3em; +} .markdown-body table img { background-color: transparent; } diff --git a/html/documentation/installations/RemoteServer.html b/html/documentation/installations/RemoteServer.html new file mode 100644 index 000000000..05606f46b --- /dev/null +++ b/html/documentation/installations/RemoteServer.html @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + Preparing a Remote Server + + + +
+ +
+ +

+This page describes how to prepare a remote server +to accept uploads of the current Allsky image +as well as startrails, keograms, and timelapse videos. +

+

+Since this server normally is NOT running the Allsky Website software, +setting it up is easy: +

    +
  1. Create the necessary directories on the server. + The web server software needs to be able to write to these directories: +
      +
    • videos +
    • videos/thumbnails +
    • startrails +
    • startrails/thumbnails +
    • keograms +
    • keograms/thumbnails +
    +
  2. Go to the WebUI's Allsky Settings page + and fill in the settings in the + Remote Server Settings section. +
+

+

+After enabling the remote server a test will will be uploaded to +ensure it works. +If there are any problems, visit the +Troubleshooting -> Uploads +page for details on how to fix the problem. + + +

+
+ + + diff --git a/html/documentation/settings/allsky.html b/html/documentation/settings/allsky.html index 74a7bb6d0..dfa625d09 100644 --- a/html/documentation/settings/allsky.html +++ b/html/documentation/settings/allsky.html @@ -56,7 +56,7 @@

Allsky Setting that several settings have different ranges on Buster versus Bullseye, so you'll need to update them when upgrading to Bullseye. For example, Contrast in Buster ranged from --100 to 100, whereas in Bullseye it starts at 0. +-100 to 100, whereas in Bullseye it's 0 to 15.99.

@@ -395,7 +395,7 @@

Allsky Setting RAW8: 8-bit mono. RGB24: color (red, green, blue), 8 bits per channel. RAW16: 16-bit mono. -Quality +Quality / Compression 95 For JPG images, this specifies the quality - 0 (low quality) to 100 (high quality). Larger numbers produce higher-quality, but larger, files. @@ -438,18 +438,36 @@

Allsky Setting for example, every 90 seconds, regardless of how long an individual frame's exposure is. +Time Format + %Y%m%d %H:%M:%S + Determines the format of the displayed time. + Run man 3 strftime to see the options. + +Temperature Units + Celsius + Determines what unit(s) the temperature will be displayed in (Celsius, Fahrenheit, or Both). + Latitude AW Latitude of the camera. - Formats include: 123.4N, 123.4S, 123.4, or -123.4. + Formats include: 123.4N, 123.4S, +123.4, or -123.4. Southern hemisphere is negative. + The "+" is needed for positive numbers. Longitude AW Longitude of the camera. - Formats include: 123.4E, 123.4W, 123.4, or -123.4. + Formats include: 123.4E, 123.4W, +123.4, or -123.4. West is negative. + The "+" is needed for positive numbers. +
+ The actual values for latitude and longitude are not displayed on the map, + but users can zoom in to see exactly where your camera is. + If that bothers you, change the latitude and/or longitude values slightly. + Allsky itself only uses them to determine when daytime and nighttime begin so + you can change the values a fair amount and not impact anything. +
Angle AW @@ -572,9 +590,8 @@

Allsky Setting overlay program.
In this mode, the overlay settings below do NOT apply.
- The default will be module in the next major Allsky release, - and in the release after that the legacy mode as well as this "Image overlay settings" - section will be removed. + This setting, as well as the legacy mode and this "Image overlay settings" + section will be removed in the next Allsky release.

When setting to module, don't forget to enable the @@ -587,19 +604,10 @@

Allsky Setting Yes Display the time the picture was taken in the overlay? -Time Format - %Y%m%d %H:%M:%S - Determines the format of the displayed time. - Run man 3 strftime to see the options. - Show Temperature Yes (ZWO only) Display the camera sensor temperature in the overlay? -Temperature Units - Celsius - Determines what unit(s) the temperature will be displayed in (Celsius, Fahrenheit, or Both). - Show Exposure Yes Display the exposure time in the overlay? If Auto-Exposure is enabled, @@ -694,7 +702,272 @@

Allsky Setting Should an outline to the text overlay be added to improve contrast? -Allsky Map and Website Setting +Allsky Website and Remote Server Settings +

Allsky supports uploading files to a local (i.e., on your Pi) Website, + a remote (i.e., not on your Pi) Website, + and a remote server that is NOT an Allsky Website. +

+ +Display Settings + No + People sometimes ask others what settings they are using. + Enable this setting to add a link to your Allsky Website's right-side + popout that displays your settings. +
This only works if you are running the Allsky Website. + +Use Local Website + No + Enable to use a local Allsky Website. + No other settings are needed for a local Website. +
+ If you were using a local Website and disable it you will be asked if you + want to keep or remove the saved images, keograms, startrails, and timelapse videos. + Either way, the Website itself is not not removed nor are its settings so you can + easily re-enable it and pick up where you left off. + +Remote Website Settings +Use Remote Website + No + Enable to use a remote Allsky Website. + Enable the Website BEFORE you + install + it since the installation needs the settings below. + +Protocol + ftps + Specifies how files should be uploaded to the remote Website. +
    +
  • ftps - + uses secure File Transfer Protocol (FTPs) to upload to a remote server. +
  • sftp - + uses SSH file transfer to upload to a remote server. +
  • ftp - + uses (insecure) File Transfer Protocol (FTP) to upload to a remote server. +
    + ftp is unsecure; + please use + ftps or + sftp instead. +
    +
  • scp - + uses secure cp (copy) to copy the file to a remote server. +
  • s3 - + copies the file to an Amazon Web Services (AWS) server. +
  • gcs - + copies the file to a Google Cloud Storage (GCS) server. +
+

Some of the settings below only apply to certain protocols.

+ +Server Name + + Name of the remote server. + Note that this is normally NOT the same as the URL used to access the server. + If you don't know the name of the server, + ask the person or company supporting the server. + +Port + + (ftp protocols only) Optional port required by the server. + This is rarely needed as the software can usually determine what port to use. + +User Name + + The username of the login on the remote Website. + +Password + + The password of the login on the remote Website. + +FTP Commands + + (ftp protocols only) Optional colon-separated list of commands to send to the ftp server + prior to transfering files. + If you have problems uploading to an ftp server you'll often need to add + something to this field - see the + Troubleshooting -> Uploads + page for more information. + +Image Directory + + (ftp protocols only) Name of the directory the current image should be uploaded to. +
+ This setting is a major cause of confusion for many users. + If you don't know what to enter, + ask the person or company supporting the server or look in the + Troubleshooting -> Uploads + page for more information. +
+ +SSH Key File + + (scp protocol only) Path to the SSH key. +
+ You need to set up SSH key authentication on your server. + First, generate an SSH key on your Pi: +
ssh-keygen -t rsa
+ When prompted, leave the default filename, and use an empty passphrase. + Then, copy the generated key to your server: +
ssh-copy-id remote_username@server_ip_address
+ The private SSH key will be stored in + ~/.ssh (default filename is id_rsa). +

+ If you are using SSH Key with Amazon's Lightsail, + copy the ssh-key.pem file to your Pi, + for example, in ~, + then execute chmod 400 ~/ssh-key.pem and set: +

    +
  • Protocol to sftp +
  • Server Name to remote host name +
  • User Name to remote user name +
  • Password to n/a +
  • FTP Commands to + set sftp:connect-program 'ssh -a -x -i /home/pi/ssh-key.pem' +
+

+ +AWS CLI Directory + ${HOME}/.local/bin + (s3 protocol only) Directory on the Pi where the AWS Command-Line Interface (CLI) + tools are installed. + You need to install tools: +
+pip3 install awscli --upgrade --user
+export PATH="${HOME}/.local/bin:${PATH}"
+aws configure
+
+ When prompted, enter a valid access key ID, Secret Access Key, and Default region name, + for example, (e.g. "us-west-2"). + Set the Default output format to "json" when prompted. + +S3 Bucket + allskybucket + (s3 protocol only) Name of the S3 bucket where files will be uploaded to + (must be in Default region specified above). + You may want to turn off or limit bucket versioning to avoid consuming lots of + space with multiple versions of the "image.jpg" files. + +S3 ACL + private + (s3 protocol only) S3 Access Control List (ACL). +
+ If you want to serve your uploaded files vis http(s), + change this to public-read. + You will need to ensure the S3 bucket policy is configured to allow public access to + objects with a public-read ACL. +
You may need to set a CORS policy in S3 if the files are to be accessed by + Javascript from a different domain. + +GCS Bucket + allskybucket + (gcs protocol only) Name of the GCS bucket where files will be uploaded to. +
+ You need to install the gsutil command which is part of the Google Cloud SDK. + See installation instructions + here. +
+ NOTE: The gsutil command must be installed somewhere in the standard ${PATH}, + usually in /usr/bin. + Make sure you authenticate the cli tool with the correct user as well. + +GCS ACL + private + (gcs protocol only) GCS Access Control List (ACL). + You can use any one of the predefined ACL rules found + here. + To access files over https, set this to publicRead. + +Remote Video File Name + + The name to give to the remote timelapse video file. + If not specified it's the same as the file on your Pi. + +Remote Keogram File Name + + The name to give to the remote keogram file. + If not specified it's the same as the file on your Pi. + +Remote Startrails File Name + + The name to give to the remote startrails file. + If not specified it's the same as the file on your Pi. + +Website URL + + Your website's URL, for example: https://www.mywebsite.com/allsky. +
+ If a Website URL is specified, + the Image URL must also be specified, and vice versa. +
+ +Image URL + + The URL to your allsky image, for example: https://wwww.mywebsite.com/allsky/image.jpg. + Normally this will be the Website URL followed by + image.jpg. +
Use the more secure "https" over "http" if possible. + +Remote Server Settings +

These settings are the same as for the Remote Website so are not described.

+ +Use Remote Website + No + Enable to use a remote server which is NOT running the Allsky Website software. + Files will be copied to this server which normally will be a private website + where you want to display the most recent images along with other, + non-Allsky information. + For example, if you have an observatory website and want to show the latest image, + you would use the settings below. + +Protocol + ftps + +Server Name + + +Port + + +User Name + + +Password + + +FTP Commands + + +Image Directory + + +SSH Key File + + +AWS CLI Directory + ${HOME}/.local/bin + +S3 Bucket + allskybucket + +S3 ACL + private + +GCS Bucket + allskybucket + +GCS ACL + private + +Remote Video File Name + + +Remote Keogram File Name + + +Remote Startrails File Name + + + +Allsky Map Settings

If you want your allsky camera's location to display on the Allsky map, @@ -705,45 +978,12 @@

Allsky Setting

- -Display Settings - No - People sometimes ask others what settings they are using. - Enable this setting to add a link to your Allsky Website's popout that displays your - settings in the WebUI's Allsky Settings page. -
Only works if you are running the Allsky Website. Show on Map No Enable to have your camera appear on the Allsky map.
If off, the following settings are ignored. -Website URL - - Your website's URL, for example: https://www.thomasjacquin.com/allsky. - If your camera is not accessible on the Internet or you do not want the - map page to link to your website, leave this field blank. -
- If a Website URL is specified, - the Image URL must also be specified, and vice versa. -
- -Image URL - - The URL to your allsky image, for example: https://wwww.thomasjacquin.com/allsky/image.jpg. - Right-click on the image and select Copy Image Address to determine what to - put in this field. -
- If you have the Allsky Website installed on your Pi and are using its image, - you may need to set this field to - <Pi name>/current/tmp/image.jpg or - whatever's in the imageName field of your website's - configuration.json file. - Be careful of using "http" versus "https", and after enabling - Show On Map, look at the map to ensure you can see your image. - If your camera is not accessible on the Internet or you do not want the image - to appear on the map page, leave this field empty. - However, one of the main purposes of the map is to show pictures from cameras around the world, - so adding your image URL is strongly encouraged. + Location AW The location of your camera. @@ -762,16 +1002,6 @@

Allsky Setting The computer runni g your allsky camera, for example: Raspberry Pi 3. This field is required and a default value is set during Allsky installation. -Latitude & longitude AW - - These are described above and are - required for your camera to appear on the Allsky Map. -
- The actual values are not displayed on the map, - but users can zoom in to see exactly where the camera is. - If that bothers you, change their values slightly. -
- Camera Type @@ -805,29 +1035,14 @@

Editor We

This page allows you to edit the config.sh file, and if you have a local and/or remote Allsky Website installed, the -ftp-settings.sh, configuration.json, and remote_configuration.json files can also be edited. -Further, if you have a endOfNight_additionalSteps.sh file, -it can also be edited. - -

-The endOfNight_additionalSteps.sh file will no longer -be supported in the next version of Allsky. -If you use this file, please move your code to the Script module -in the Night to Day Transition Flow, -then remove this file. -See the Module -documentation for more details. -

+

The table below describes the settings in the config.sh file.
-Settings in the ftp-settings.sh file are described in the -ftp-settings.sh settings page. -
Settings in the configuration.json files are described in the Allsky Website Settings page.

@@ -843,7 +1058,6 @@

config.sh settings

Editor page, then selecting config.sh in the drop-down list at the bottom of the page.

-
@@ -1264,7 +1478,6 @@

config.sh settings

Do not change anything lower in the file
-
+--> diff --git a/html/documentation/settings/allskyWebsite.html b/html/documentation/settings/allskyWebsite.html index b14d53eec..c3d907371 100644 --- a/html/documentation/settings/allskyWebsite.html +++ b/html/documentation/settings/allskyWebsite.html @@ -33,24 +33,19 @@ and aurora activity can be listed.

-Make sure you follow the -Allsky Website Installation Instructions -exactly to install the Allsky Website on your Pi and/or a remote server. +For a local Allsky Website you only need to enable it in +the WebUI's Allsky Settings page. +There are no other settings on this page and no installation needed.

-To configure the Website: +For a remote Website follow the +Allsky Website Installation Instructions. +

+ +

+To configure a local or remote Website, do the following:

  1. In the WebUI, click on the Editor link in the menu. - -
  2. If you have not already done so while installing the Website, - in the drop-down at the bottom of the page, select - ftp-settings.sh - to specify the settings to upload image, - keogram, startrails, and timelapse - files to your local and/or remote Allsky Website. -
    See the ftp-settings.sh file section below - for the various settings and their meanings. -
  3. In the drop-down at the bottom of the page, select one of the following, depending on which Website you want to configure:
      @@ -58,314 +53,17 @@
    • remote_configuration.json (remote Allsky Website) (only if you have a remote Website)
    - See the configuration.json files section below - for a description of the settings in those files.

-

-The sections below list all the settings, their default values, and a description. -Information on the color scheme used by the Editor is -here. -

- - -

ftp-settings.sh file

-

-In order to upload files to your Allsky Website (on the Pi and/or a remote server), -connection details must be specified by clicking on the WebUI's -Editor page, then selecting -ftp-settings.sh in the drop-down list at the bottom of the page. -

-
-

-Notes: -

    -
  • - In the text below, "local" refers to an Allsky Website that's on the Pi - and "remote" refers to an Allsky Website not on the Pi. -
    -
    - Directories on the Pi are created during Allsky Website installation, but - YOU must create the remote directories. -
      -
    -
  • - The WEB_*_DIR - settings below (e.g., WEB_VIDEOS_DIR) - are only used if you have an Allsky Website on the Pi - AND a remote Allsky Website, and you want files copied to both locations. - In this case, see the - example at the end of the table below - for what settings to use. -
  • - By default, the destination file name is the same as the file being uploaded. - The *_DESTINATION_NAME settings below are used - to specify a DIFFERENT destination name. - For example, if the file being uploaded is - allsky-20210710.mp4 you may want it - called allsky.mp4 on the remote web server - so the name is always the same. - In that case, set - VIDEOS_DESTINATION_NAME="allsky.mp4" - (don't forget the file name extension like .mp4, .jpg, etc.). - If you want the destination file name to be the same as what's being uploaded, - leave the *_DESTINATION_NAME blank. -
-

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SettingDefaultDescription
PROTOCOL How the file should be uploaded: -
    -
  • local - - copies the file to a local Allsky Website. -
  • ftps - - uses secure File Transfer Protocol (FTPs) to upload to a remote server. -
  • sftp - - uses SSH file transfer to upload to a remote server. -
  • ftp - - uses (insecure) File Transfer Protocol (FTP) to upload to a remote server. -
    - ftp is unsecure; - please use - ftps or - sftp instead. -
    -
  • scp - - uses secure cp (copy) to copy the file to a remote server. -
  • s3 - - copies the file to an Amazon Web Services (AWS) server. -
  • gcs - - copies the file to a Google Cloud Storage (GCS) server. -
-
image.jpg Settings
IMAGE_DIR The remote directory where the current image should go.
WEB_IMAGE_DIR The local directory where the current image should go.
Timelapse Settings
VIDEOS_DIR The remote directory where the timelapse video should go.
VIDEOS_DESTINATION_NAME Remote name of the timelapse video file. - If not specified it's the same name as the file being uploaded.
WEB_VIDEOS_DIR Location on the Pi to copy the timelapse video to.
Keogram Settings
KEOGRAM_DIR The remote directory where the keogram image should go.
KEOGRAM_DESTINATION_NAME Remote name of the keogram image file. - If not specified it's the same name as the file being uploaded.
WEB_KEOGRAM_DIR Location on the Pi to copy the keogram file to.
Startrails Settings
STARTRAILS_DIR The remote directory where the startrails image should go.
STARTRAILS_DESTINATION_NAME Remote name of the startrails file. - If not specified it's the same name as the file being uploaded.
WEB_STARTRAILS_DIR Location on the Pi to copy the startrails file to.
ftp, ftps, sftp, and scp Settings
REMOTE_HOST Remote server DNS name or IP address. - If you don't know it, ask your service provider. -
ftp, ftps, and sftp Settings
REMOTE_USER Your remote user name.
REMOTE_PASSWORD Your ftp / ftps / sftp password.
REMOTE_PORT An optional port number for ftp and ftps. Is rarely needed.
LFTP_COMMANDS An optional colon-separated (;) list of commands needed by the lftp command. - See this page - for example commands to enter into LFTP_COMMANDS. -
scp Settings
REMOTE_USER Your remote user name. - This is the same setting as above. -
SSH_KEY_FILE Path to the SSH key file. -
- You need to set up SSH key authentication on your server. - First, generate an SSH key on your Pi: -
ssh-keygen -t rsa
- When prompted, leave the default filename, and use an empty passphrase. - Then, copy the generated key to your server: -
ssh-copy-id remote_username@server_ip_address
- The private SSH key will be stored in - ~/.ssh (default filename is id_rsa) -
s3 Settings
- You need to install the AWS Command-Line Interface (CLI): -
-sudo apt-get install python3-pip
-pip3 install awscli --upgrade --user
-export PATH=/home/pi/.local/bin:$PATH
-aws configure
-
- When prompted, enter a valid access key ID, Secret Access Key, and Default region name, - for example, (e.g. "us-west-2"). - Set the Default output format to "json" when prompted. -
AWS_CLI_DIR/home/pi/.local/binDirectory on the Pi where the AWS tools are installed. -
If you used a different PATH setting above, change this setting to match it. -
S3_BUCKETallskybucketName of S3 Bucket where the files will be uploaded - (must be in Default region specified above). - You may want to turn off or limit bucket versioning to avoid consuming lots of - space with multiple versions of the "image.jpg" files. -
S3_ACLprivateS3 Access Control List (ACL). - If you want to serve your uploaded files vis http(s), - change this to public-read. - You will need to ensure the S3 bucket policy is configured to allow public access to - objects with a public-read ACL. -
You may need to set a CORS policy in S3 if the files are to be accessed by - Javascript from a different domain. -
GCS Settings
- You need to install the gsutil command which is part of the Google Cloud SDK. - See installation instructions - here. -
- NOTE: The gsutil command must be installed somewhere in the standard $PATH, - usually in /usr/bin. - Make sure you authenticate the cli tool with the correct user as well. -
GCS_BUCKETallskybucketName of S3 Bucket where the files will be uploaded.
GCS_ACLprivateGCS Access Control List. - You can use any one of the predefined ACL rules found - here. - To access files over https, set this to publicRead. -
- -
-

Settings for various combinations

-
    -
  1. If you have the Allsky Website only on your Pi, use these settings: -
    -PROTOCOL="local"
    -IMAGE_DIR=""
    -VIDEOS_DIR="${ALLSKY_WEBSITE}/videos"
    -KEOGRAM_DIR="${ALLSKY_WEBSITE}/keograms"
    -STARTRAILS_DIR="${ALLSKY_WEBSITE}/startrails"
    -
    - -
  2. - If you have an Allsky Website only on a remote server, use these settings. - For the sake of this example, assume your top-level directory on the server is - /allsky: -
    -PROTOCOL="sftp"		# or another PROTOCOL
    -IMAGE_DIR="/allsky"
    -VIDEOS_DIR="/allsky/videos"
    -KEOGRAM_DIR="/allsky/keogram"
    -STARTRAILS_DIR="/allsky/startrails"
    -
    - -
  3. - If you have an Allsky Website on your Pi AND on a remote server, use these settings. - For the sake of this example, assume your top-level directory on the server is - /allsky: -
    -PROTOCOL="sftp"		# or another PROTOCOL
    -IMAGE_DIR="/allsky"
    -WEB_IMAGE_DIR=""
    -VIDEOS_DIR="/allsky/videos"
    -WEB_VIDEOS_DIR="${ALLSKY_WEBSITE}/videos"
    -KEOGRAM_DIR="/allsky/keogram"
    -WEB_KEOGRAM_DIR="${ALLSKY_WEBSITE}/keograms"
    -STARTRAILS_DIR="/allsky/startrails"
    -WEB_STARTRAILS_DIR="${ALLSKY_WEBSITE}/startrails"
    -
    -
-
- - -

Amazon Lightsail

-If you are using SSH Key with Amazon's Lightsail, -copy the ssh-key.pem file to your Pi, -for example, in ~, -then execute -
chmod 400 ~/ssh-key.pem
-and set: -
-PROTOCOL="sftp"
-REMOTE_HOST="remote host name"
-REMOTE_USER="remote user name"
-REMOTE_PASSWORD="n/a"
-LFTP_COMMANDS="set sftp:connect-program 'ssh -a -x -i /home/pi/ssh-key.pem'"
-
-
-

configuration.json files

-This section describes the settings in the -configuration.json (local Allsky Website) and -remote_configuration.json (remote Allsky Website) files. +This section describes the settings in the .json files, +their default values, and a description. +Information on the color scheme used by the Editor is +here.

-

The settings in both files are identical (although their values may differ) and they are split into two sections: @@ -731,11 +429,6 @@

config settings

Do not change. - - AllskyWebsiteVersion - - Do not change. -
@@ -977,8 +670,6 @@

homePage settings

- - diff --git a/html/documentation/sidebar.html b/html/documentation/sidebar.html index 336d74838..805cc9d72 100644 --- a/html/documentation/sidebar.html +++ b/html/documentation/sidebar.html @@ -13,6 +13,7 @@
  1. Allsky
  2. Allsky Website +
  3. Remote server
  • Settings:
      diff --git a/html/includes/allskySettings.php b/html/includes/allskySettings.php index cb9083c73..b1fae5058 100644 --- a/html/includes/allskySettings.php +++ b/html/includes/allskySettings.php @@ -1,11 +1,35 @@ addMessage($msg, 'danger', false); + return (""); + } + if (! isset($filesContents[$fileName])) { + $errorMsg = "Unable to read source file '$fileName'"; + $filesContents[$fileName] = get_decoded_json_file($fileName, true, $errorMsg); + if ($filesContents[$fileName] === null) { + $msg = "Unable to get json contents of '$fileName' ($f)."; + $status->addMessage($msg, 'danger', false); + } + } + return $filesContents[$fileName]; +} + + function DisplayAllskyConfig(){ - global $formReadonly; + global $formReadonly, $settings_array; - $cameraTypeName = "cameraType"; // json setting name - $cameraModelName = "cameraModel"; // json setting name - $cameraNumberName = "cameraNumber"; // json setting name + $debug = false; + $cameraTypeName = "cameratype"; // json setting name + $cameraModelName = "cameramodel"; // json setting name + $cameraNumberName = "cameranumber"; // json setting name $debugLevelName = "debuglevel"; // json setting name $debugArg = ""; @@ -30,10 +54,12 @@ function DisplayAllskyConfig(){ // If we went ahead and made the changes, we would be making them to the NEW // camera's settings file, but using values from the OLD file. if (CSRFValidate()) { - $settings = array(); $optional_array = array(); + $type_array = array(); + $sourceFiles = array(); // list of files in the "source" field + $sourceFilesContent = array(); // contents of each sourceFiles file $changes = ""; - $somethingChanged = false; + $nonCameraChanges = ""; $refreshingCameraType = false; $newCameraType = ""; @@ -42,13 +68,22 @@ function DisplayAllskyConfig(){ $ok = true; - // Keep track of optional settings - foreach ($options_array as $option){ - $n = $option['name']; - $optional_array[$n] = getVariableOrDefault($option, 'optional', false); + // Keep track of optional settings and which settings are from a different source. + foreach ($options_array as $option) { + $key = $option['name']; + $optional_array[$key] = getVariableOrDefault($option, 'optional', false); + $type_array[$key] = getVariableOrDefault($option, 'type', ""); + $s = getVariableOrDefault($option, 'source', null); + if ($s !== null) { + $fileName = getFileName($s); + $sourceFiles[$key] = $fileName; + $sourceFilesContents[$key] = &getSourceArray($fileName); + } } - foreach ($_POST as $key => $value){ + $numSettingsChanges = 0; + $numSourceChanges = 0; + foreach ($_POST as $key => $newValue) { // Anything that's sent "hidden" in a form that isn't a settings needs to go here. if (in_array($key, ["csrf_token", "save_settings", "reset_settings", "restart", "page", "_ts", "XX_END_XX"])) continue; @@ -57,112 +92,171 @@ function DisplayAllskyConfig(){ // Because we are passing the changes enclosed in single quotes below, // we need to escape the single quotes, but I never figured out how to do that, // so convert them to HTML codes instead. - $isOLD = substr($key, 0, 4) === "OLD_"; - if ($isOLD) { - $key = substr($key, 4); // everything after "OLD_" - $oldValue = str_replace("'", "'", $value); - $newValue = getVariableOrDefault($settings, $key, ""); - if ($oldValue !== $newValue) { - if ($key === $cameraTypeName) { - if ($newValue === "Refresh") { - // Refresh the same Camera Type - $refreshingCameraType = true; - $newCameraType = $oldValue; - $newValue = $oldValue; - } else { - $newCameraType = $newValue; - } - } elseif ($key === $cameraModelName) { - $newCameraModel = $newValue; - } elseif ($key === $cameraNumberName) { - $newCameraNumber = $newValue; + $source_array = getVariableOrDefault($sourceFilesContents, $key, null); + if ($source_array !== null) { + $oldValue = getVariableOrDefault($source_array, $key, ""); + $isSettingsField = false; + } else { + $oldValue = getVariableOrDefault($settings_array, $key, ""); + $isSettingsField = true; // this field is in the settings file. + } + if ($oldValue !== "") + $oldValue = str_replace("'", "'", $oldValue); + + if ($type_array[$key] == "boolean") { + if ($oldValue === 0 || $oldValue === "0") $oldValue = "false"; + else if ($oldValue === 1 || $oldValue === "1") $oldValue = "true"; + } + + if ($oldValue !== $newValue) { + $nonCameraChangesExist = false; + if ($isSettingsField) $numSettingsChanges++; + else $numSourceChanges++; +if ($debug) echo "
          after $key, numSettingsChanges=$numSettingsChanges, numSourceChanges=$numSourceChanges"; + + if ($key === $cameraTypeName) { + if ($newValue === "Refresh") { + // Refresh the same Camera Type + $refreshingCameraType = true; + $newCameraType = $oldValue; + $newValue = $oldValue; } else { - $somethingChanged = true; // want to know about OTHER changes + $newCameraType = $newValue; } + } elseif ($key === $cameraModelName) { + $newCameraModel = $newValue; + } elseif ($key === $cameraNumberName) { + $newCameraNumber = $newValue; + } else { + // want to know changes other than camera + $nonCameraChangesExist = true; + } - $checkchanges = false; - foreach ($options_array as $option){ - if ($option['name'] === $key) { - $optional = $optional_array[$key]; - if ($newValue !== "" || $optional) { - $checkchanges = getVariableOrDefault($option, 'checkchanges', false); - $label = getVariableOrDefault($option, 'label', ""); - } - break; + $checkchanges = false; + foreach ($options_array as $option){ + if ($option['name'] === $key) { + $optional = $optional_array[$key]; + if ($newValue !== "" || $optional) { + $checkchanges = getVariableOrDefault($option, 'checkchanges', false); + $label = getVariableOrDefault($option, 'label', ""); } + break; } - if ($checkchanges) - $changes .= " '$key' '$label' '$oldValue' '$newValue'"; } - } else { - // Check for empty non-optional settings and valid numbers. - $span = "span class='WebUISetting'"; - $spanValue = "span class='WebUIValue'"; - foreach ($options_array as $option) { - if ($option['name'] === $key) { - $type = getVariableOrDefault($option, 'type', null); - $lab = $option['label']; + if ($checkchanges) { // Changes for makeChanges.sh to check + $changes .= " '$key' '$label' '$oldValue' '$newValue'"; + } - if ($value == "" && ! $optional_array[$key]) { - $msg = "<$span>$lab is empty"; - $status->addMessage($msg, 'danger', false); - $ok = false; + if ($nonCameraChangesExist) { + if ($nonCameraChanges === "") + $nonCameraChanges = "$label"; + else + $nonCameraChanges .= ", $label"; + $nonCameraChanges .= " (from '$oldValue' to '$newValue')"; + } + } - } else if ($type !== null) { - $msg = ""; - // $value will be of type string, even if it's actually a number, - // and only is_numeric() accounts for types of string. - if ($type === "integer" || $type == "percent") { - if (! is_numeric($value) || ! is_int($value + 0)) - $msg = "without a fraction"; - } else if ($type === "float") { - if (! is_numeric($value) || ! is_float($value + 0.0)) - $msg = "with, or without, a fraction"; - } - if ($msg !== "") { - $msg2 = "<$span>$lab must be a number $msg."; - $msg2 .= " You entered: <$spanValue>$value"; - $status->addMessage($msg2, 'danger', false); - $ok = false; - } + // Check for empty non-optional settings and valid numbers. + $span = "span class='WebUISetting'"; + $spanValue = "span class='WebUIValue'"; + foreach ($options_array as $option) { + if ($option['name'] === $key) { + $type = getVariableOrDefault($option, 'type', null); + $lab = $option['label']; + + if ($newValue == "" && ! $optional_array[$key]) { + $msg = "<$span>$lab is empty"; + $status->addMessage($msg, 'danger', false); + $ok = false; + + } else if ($type !== null && $newValue != "") { + $msg = ""; + // $newValue will be of type string, even if it's actually a number, + // and only is_numeric() accounts for types of string. + if ($type === "integer" || $type == "percent") { + if (! is_numeric($newValue) || ! is_int($newValue + 0)) + $msg = "without a fraction"; + } else if ($type === "float") { + if (! is_numeric($newValue) || ! is_float($newValue + 0.0)) + $msg = "with, or without, a fraction"; + } + if ($msg !== "") { + $msg2 = "<$span>$lab must be a number $msg."; + $msg2 .= " You entered: <$spanValue>$newValue"; + $status->addMessage($msg2, 'danger', false); + $ok = false; } } } + } - if ($ok) { - $settings[$key] = str_replace("'", "'", $value); + if ($ok && ($numSettingsChanges > 0 || $numSourceChanges > 0)) { + // Update the appropriate array with the new value. + $n = str_replace("'", "'", $newValue); + if (isset($sourceFilesContents[$key])) { +if ($debug) echo "
      sourceFilesContent[$key][$key] = " . $sourceFilesContents[$key][$key] . ", newValue=$newValue"; + $sourceFilesContents[$key][$key] = $n; + $fileName = $sourceFiles[$key]; + $sourceFilesChanged[$fileName] = $fileName; + } else { +if ($debug) echo "
      settings[$key] = " . $settings_array[$key] . ", newValue=$newValue"; + $settings_array[$key] = $n; + } - if ($key === $debugLevelName && $value >= 4) { - $debugArg = "--debug"; - } + if ($key === $debugLevelName && $newValue >= 4) { + $debugArg = "--debug"; } } } $msg = ""; - if ($ok) { - if ($somethingChanged || $lastChanged === "") { + if ($ok && ($numSettingsChanges > 0 || $numSourceChanges > 0)) { + if ($nonCameraChanges !== "" || $lastChanged === "") { if ($newCameraType !== "" || $newCameraModel !== "" || $newCameraNumber != "") { $msg = "If you change Camera Type, Camera Model,"; $msg .= " or Camera Number you cannot change anything else."; + $msg .= "
      You also changed: $nonCameraChanges."; $status->addMessage($msg, 'danger', false); $ok = false; } else { - // Keep track of the last time the file changed. - // If we end up not updating the file this will be ignored. - $lastChanged = date('Y-m-d H:i:s'); - $settings[$lastChangedName] = $lastChanged; - $content = json_encode($settings, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES); - // updateFile() only returns error messages. - $msg = updateFile($settings_file, $content, "settings", true); - if ($msg === "") - $msg = "Settings saved"; - else - $ok = false; + $mode = JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES; // |JSON_NUMERIC_CHECK; + if ($numSettingsChanges > 0) { + // Keep track of the last time the file changed. + // If we end up not updating the file this will be ignored. + $lastChanged = date('Y-m-d H:i:s'); + $settings_array[$lastChangedName] = $lastChanged; + $content = json_encode($settings_array, $mode); + // updateFile() only returns error messages. +if ($debug) echo "
      Updating settings_file $settings_file, # changes = $numSettingsChanges"; +if ($debug) { echo "
      "; var_dump($content); echo "
      "; } + $msg = updateFile($settings_file, $content, "settings", true); + if ($msg === "") { + $msg = "Settings saved"; + } else { + $status->addMessage("Failed to update settings in '$settings_file': $msg", 'danger'); + $ok = false; + } + } + if ($ok && $numSourceChanges > 0) { + // Now save the settings from the source files that changed. + foreach($sourceFilesChanged as $fileName) { + $content = json_encode(getSourceArray($fileName), $mode); +if ($debug) echo "
      Updating fileName $fileName, # changes=$numSourceChanges"; +if ($debug) { echo "
      "; var_dump($content); echo "
      "; } + $msg = updateFile($fileName, $content, "source_settings", true); + if ($msg === "") { + $msg = "Settings saved"; + } else { + $status->addMessage("Failed to update settings in '$fileName': $msg", 'danger'); + $ok = false; + } + } + } } } else { if ($newCameraType !== "") { + if ($msg !== "") $msg = "
      $msg"; if ($refreshingCameraType) $msg .= "Camera Type $newCameraType refreshed"; else @@ -187,7 +281,9 @@ function DisplayAllskyConfig(){ $doingRestart = getVariableOrDefault($_POST, 'restart', false); if ($doingRestart === "on") $doingRestart = true; - if ($changes !== "") { + if ($numSettingsChanges == 0 && $numSourceChanges == 0) { + $msg = "No settings changed"; + } else if ($changes !== "") { // This must run with different permissions so makeChanges.sh can // write to the allsky directory. $moreArgs = ""; @@ -226,38 +322,70 @@ function DisplayAllskyConfig(){ if (isset($_POST['reset_settings'])) { if (CSRFValidate()) { - $settings = array(); + $settings_array = array(); + $sourceFilesChanged = array(); + $sourceFilesContents = array(); foreach ($options_array as $option){ $key = $option['name']; - $value = getVariableOrDefault($option, 'default', null); - if ($value !== null) $settings[$key] = $value; + $newValue = getVariableOrDefault($option, 'default', null); + if ($newValue !== null) { + $s = getVariableOrDefault($option, 'source', null); + if ($s !== null) { + $fileName = getFileName($s); + $sourceFilesChanged[$fileName] = $fileName; + $sourceFilesContents[$key] = &getSourceArray($fileName); + $sourceFilesContents[$key][$key] = $newValue; + } else { + $settings_array[$key] = $newValue; + } + } } - $content = json_encode($settings, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES|JSON_NUMERIC_CHECK); + $mode = JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES; // |JSON_NUMERIC_CHECK; + $content = json_encode($settings_array, $mode); $msg = updateFile($settings_file, $content, "settings", true); - if ($msg === "") + if ($msg === "") { $status->addMessage("Settings reset to default", 'info'); - else + + foreach($sourceFilesChanged as $fileName) { + $content = json_encode(getSourceArray($fileName), $mode); + $msg = updateFile($fileName, $content, "source_settings", true); + if ($msg !== "") { + $status->addMessage("Failed to reset settings in '$fileName': $msg", 'danger'); + } + } + } else { $status->addMessage("Failed to reset settings: $msg", 'danger'); + } } else { $status->addMessage('Unable to reset settings - session timeout', 'danger'); } } - // Determine if the advanced settings should always be shown. - $errorMsg = "ERROR: Unable to process settings file '$settings_file'."; - $settings_array = get_decoded_json_file($settings_file, true, $errorMsg); - if ($settings_array === null) { - exit; + // If the settings file changed above, re-read the file. + if (isset($_POST['save_settings']) || isset($_POST['reset_settings'])) { + $errorMsg = "ERROR: Unable to process settings file '$settings_file'."; + $settings_array = get_decoded_json_file($settings_file, true, $errorMsg); + if ($settings_array === null) { + exit; + } } + $cameraType = getVariableOrDefault($settings_array, $cameraTypeName, ""); $cameraModel = getVariableOrDefault($settings_array, $cameraModelName, ""); - $initial_display = $settings_array['alwaysshowadvanced'] == 1 ? "table-row" : "none"; + // Determine if the advanced settings should always be shown. + if ($formReadonly == "readonly") { + $alwaysShowAdvanced = true; + } else { + $alwaysShowAdvanced = getVariableOrDefault($settings_array, 'alwaysshowadvanced', false); + } + $initial_display = $alwaysShowAdvanced ? "table-row" : "none"; check_if_configured($page, "settings"); if ($formReadonly != "readonly") { $settingsDescription = ""; ?> + +
      @@ -121,24 +140,15 @@ function DisplayEditor()

      showMessages(); ?>

      - No files to edit
      "; + } else { ?> +
      diff --git a/html/includes/functions.php b/html/includes/functions.php index eb3c0fb03..d694e4857 100644 --- a/html/includes/functions.php +++ b/html/includes/functions.php @@ -73,20 +73,13 @@ function get_decoded_json_file($file, $associative, $errorMsg, &$returnedMsg=nul $temptype = null; $lastChanged = null; $websiteURL = null; +$settings_array = null; function initialize_variables() { global $status, $needToDisplayMessages; global $image_name, $delay, $daydelay, $nightdelay; global $darkframe, $useLogin, $temptype, $lastChanged, $lastChangedName; global $websiteURL; - - // The Camera Type should be set during the installation, so this "should" never fail... - $cam_type = getCameraType(); - if ($cam_type == '') { - echo "
      "; - echo "'Camera Type' not defined in config.sh. Please update it."; - echo "
      "; - exit; - } + global $settings_array; $settings_file = getSettingsFile(); $errorMsg = "ERROR: Unable to process settings file '$settings_file'."; @@ -94,20 +87,19 @@ function initialize_variables() { if ($settings_array === null) { exit; } - // $img_dir is an alias in the web server's config that points to where the current image is. // It's the same as ${ALLSKY_TMP} which is the physical path name on the server. - $img_dir = get_variable(ALLSKY_CONFIG . '/config.sh', 'IMG_DIR=', 'current/tmp'); + $img_dir = get_variable(ALLSKY_HOME . '/variables.sh', 'IMG_DIR=', 'current/tmp'); $image_name = $img_dir . "/" . $settings_array['filename']; - $darkframe = $settings_array['takeDarkFrames']; - $useLogin = getVariableOrDefault($settings_array, 'useLogin', true); + $darkframe = $settings_array['takedarkframes']; + $useLogin = getVariableOrDefault($settings_array, 'uselogin', true); $temptype = getVariableOrDefault($settings_array, 'temptype', "C"); $lastChanged = getVariableOrDefault($settings_array, $lastChangedName, ""); - $websiteURL = getVariableOrDefault($settings_array, 'websiteurl', ""); + $websiteURL = getVariableOrDefault($settings_array, 'remotewebsiteurl', ""); ////////////////// Determine delay between refreshes of the image. - $consistentDelays = $settings_array["consistentDelays"] == 1 ? true : false; + $consistentDelays = getVariableOrDefault($settings_array, 'consistentdelays', false); $daydelay = $settings_array["daydelay"]; $daymaxautoexposure = $settings_array["daymaxautoexposure"]; $dayexposure = $settings_array["dayexposure"]; @@ -118,33 +110,33 @@ function initialize_variables() { $ok = true; if (! is_numeric($daydelay)) { $ok = false; - $status->addMessage("daydelay is not a number.", 'danger', false); + $status->addMessage("daydelay is not a number: $daydelay.", 'danger', false); } if (! is_numeric($daymaxautoexposure)) { $ok = false; - $status->addMessage("daymaxautoexposure is not a number.", 'danger', false); + $status->addMessage("daymaxautoexposure is not a number: $daymaxautoexposure.", 'danger', false); } if (! is_numeric($dayexposure)) { $ok = false; - $status->addMessage("dayexposure is not a number.", 'danger', false); + $status->addMessage("dayexposure is not a number: $dayexposure.", 'danger', false); } if (! is_numeric($nightdelay)) { $ok = false; - $status->addMessage("nightdelay is not a number.", 'danger', false); + $status->addMessage("nightdelay is not a number: $nightdelay.", 'danger', false); } if (! is_numeric($nightmaxautoexposure)) { $ok = false; - $status->addMessage("nightmaxautoexposure is not a number.", 'danger', false); + $status->addMessage("nightmaxautoexposure is not a number: $nightmaxautoexposure.", 'danger', false); } if (! is_numeric($nightexposure)) { $ok = false; - $status->addMessage("nightexposure is not a number.", 'danger', false); + $status->addMessage("nightexposure is not a number: $nightexposure.", 'danger', false); } if ($ok) { $daydelay += ($consistentDelays ? $daymaxautoexposure : $dayexposure); $nightdelay += ($consistentDelays ? $nightmaxautoexposure : $nightexposure); - $showDelay = getVariableOrDefault($settings_array, 'showDelay', true); + $showDelay = getVariableOrDefault($settings_array, 'showdelay', true); if ($showDelay) { // Determine if it's day or night so we know which delay to use. $angle = $settings_array['angle']; @@ -458,14 +450,19 @@ function handle_interface_POST_and_status($interface, $input, &$status) { * so there shouldn't be a comment on the line, * however, there can be optional spaces or tabs before the string. * -* This function will go away once the config.sh and ftp-settings.sh files are merged -* into the settings.json file. */ function get_variable($file, $searchfor, $default) { // get the file contents + if (! file_exists($file)) { + $msg = "
      "; + $msg .= "
      File '$file' not found!"; + $msg .= "
      "; + echo $msg; + return($default); + } $contents = file_get_contents($file); - if ("$contents" == "") return($default); // file not found or not readable + if ($contents == "") return($default); // file not found or not readable // escape special characters in the query $pattern = preg_quote($searchfor, '/'); @@ -498,11 +495,13 @@ function get_variable($file, $searchfor, $default) } } + /** * * List a type of file - either "All" (case sensitive) for all days, or only for the specified day. +* If $dir is not null, it ends in "/". */ -function ListFileType($dir, $imageFileName, $formalImageTypeName, $type) { // if $dir is not null, it ends in "/" +function ListFileType($dir, $imageFileName, $formalImageTypeName, $type) { $num = 0; // Let the user know when there are no images for the specified day // "/images" is an alias in the web server for ALLSKY_IMAGES $images_dir = "/images"; @@ -678,10 +677,6 @@ function updateFile($file, $contents, $fileName, $toConsole) { return ""; } -function getCameraType() { - return get_variable(ALLSKY_CONFIG . '/config.sh', 'CAMERA_TYPE=', ''); -} - // Return the settings file for the specified camera. function getSettingsFile() { return ALLSKY_CONFIG . "/settings.json"; @@ -692,6 +687,25 @@ function getOptionsFile() { return ALLSKY_CONFIG . "/options.json"; } +// Return the file name after accounting for any ${} variables. +// Since there will often only be one file used by multiple settings, +// as an optimization save the last name. +$lastFileName = null; +function getFileName($file) { + global $lastFileName; + + if ($lastFileName === $file) return $lastFileName; + + if (strpos('${HOME}', $file) !== false) { + $lastFileName = str_replace('${HOME}', HOME, $file); + } else { + $lastFileName = get_variable(ALLSKY_HOME . '/variables.sh', "$file=", ''); +// TODO: don't hard code +$lastFileName = str_replace('${ALLSKY_HOME}', ALLSKY_HOME, $lastFileName); + } + return $lastFileName; +} + // Check if the specified variable is in the specified array. // If so, return it; if not, return default value; // This is used to make the code easier to read. diff --git a/html/includes/moduleutil.php b/html/includes/moduleutil.php index f5f300572..b67e239a6 100644 --- a/html/includes/moduleutil.php +++ b/html/includes/moduleutil.php @@ -188,13 +188,10 @@ public function postModulesSettings() { } public function getModuleBaseData() { - $cam_type = getCameraType(); - $settings_file = getSettingsFile($cam_type); - $camera_settings_str = file_get_contents($settings_file, true); - $camera_settings_array = json_decode($camera_settings_str, true); - $angle = $camera_settings_array['angle']; - $lat = $camera_settings_array['latitude']; - $lon = $camera_settings_array['longitude']; + global $settings_array; // defined in initialize_variables() + $angle = $settings_array['angle']; + $lat = $settings_array['latitude']; + $lon = $settings_array['longitude']; $result['lat'] = $lat; $result['lon'] = $lon; diff --git a/html/includes/outputJSONwithEqual.php b/html/includes/outputJSONwithEqual.php new file mode 100644 index 000000000..ea51c0148 --- /dev/null +++ b/html/includes/outputJSONwithEqual.php @@ -0,0 +1,82 @@ +#!/usr/bin/php + $val) { + if ($debug || $opt === "debug") echo "===== Argument $opt $val\n"; + + if ($opt === "debug") { + $debug = true; + } else if ($opt === "settings-file") { + $settings_file = $val; + if (! file_exists($settings_file)) { + echo "ERROR: settings file '$settings_file' not found!\n"; + $ok = false; + } + } else if ($opt === "capture-only") { + $capture_only = true; + $options_file = $val; + if (! file_exists($options_file)) { + echo "ERROR: options file '$options_file' not found!\n"; + $ok = false; + } + } +} + +if ($settings_file === null) { + // use default + $settings_file = getSettingsFile(); +} + +$errorMsg = "ERROR: Unable to process settings file '$settings_file'."; +$settings_array = get_decoded_json_file($settings_file, true, $errorMsg); +if ($settings_array === null) { + exit(1); +} + +if ($capture_only) { + $errorMsg = "ERROR: Unable to process options file '$options_file'.\n"; + $options_array = get_decoded_json_file($options_file, true, $errorMsg); + if ($options_array === null) { + exit(1); + } + + foreach ($options_array as $option) { + $type = getVariableOrDefault($option, 'type', ""); + if (substr($type, 0, 6) == "header" || $type == "") + continue; + + $usage = getVariableOrDefault($option, 'usage', ""); + if ($usage == "capture") { + $name = $option['name']; + $val = getVariableOrDefault($settings_array, $name, null); + if ($val !== null) + echo "$name=$val\n"; + } + } +} else { + foreach ($settings_array as $name => $val) { + echo "$name=$val\n"; + } +} + +exit(0); +?> diff --git a/html/includes/system.php b/html/includes/system.php index f179293d3..5963f1fe5 100644 --- a/html/includes/system.php +++ b/html/includes/system.php @@ -270,7 +270,7 @@ function displayUserData($file, $displayType) */ function DisplaySystem() { - global $status, $temptype, $page; + global $status, $temptype, $page, $settings_array; $status = new StatusMessages(); $top_dir = dirname(ALLSKY_WEBSITE, 1); @@ -399,7 +399,7 @@ function DisplaySystem() // Optional user-specified data. // TODO: read each file once and populate arrays for "data", "progress", and "button". - $udf = get_variable(ALLSKY_CONFIG .'/config.sh', 'WEBUI_DATA_FILES=', ''); + $udf = getVariableOrDefault($settings_array, 'webuidatafiles', ''); if ($udf !== "") { $user_data_files = explode(':', $udf); $user_data_files_count = count($user_data_files); diff --git a/html/index.php b/html/index.php index 6d9ae674e..dc78da0d0 100644 --- a/html/index.php +++ b/html/index.php @@ -15,7 +15,7 @@ */ // Globals -$lastChangedName = "lastChanged"; // json setting name +$lastChangedName = "lastchanged"; // json setting name $formReadonly = false; // The WebUI isn't readonly $ME = htmlspecialchars($_SERVER["PHP_SELF"]); @@ -196,7 +196,6 @@ function getImage() { var img = $("").attr('src', '?_ts=' + new Date().getTime()) .attr("id", "current") .attr("class", "current") - .css("width", "100%") .on('load', function () { if (!this.complete || typeof this.naturalWidth == "undefined" || this.naturalWidth == 0) { console.log('broken image!'); diff --git a/html/public.php b/html/public.php index 63f4f5251..4782d1057 100644 --- a/html/public.php +++ b/html/public.php @@ -22,7 +22,7 @@
      - +
      @@ -33,7 +33,6 @@ function getImage(){ var img = $("").attr('src', '?_ts=' + new Date().getTime()) .attr("id", "current") .attr("class", "current") - .css("width", "100%") .on('load', function() { if (!this.complete || typeof this.naturalWidth == "undefined" || this.naturalWidth == 0) { console.log('broken image!'); diff --git a/install.sh b/install.sh index f865784f3..c3ef318d8 100755 --- a/install.sh +++ b/install.sh @@ -1,17 +1,15 @@ #!/bin/bash # shellcheck disable=SC2154 # referenced but not assigned -[[ -z ${ALLSKY_HOME} ]] && export ALLSKY_HOME="$(realpath "$(dirname "${BASH_ARGV0}")")" -ME="$(basename "${BASH_ARGV0}")" +[[ -z ${ALLSKY_HOME} ]] && export ALLSKY_HOME="$( realpath "$( dirname "${BASH_ARGV0}" )" )" +ME="$( basename "${BASH_ARGV0}" )" -#shellcheck disable=SC2086 source-path=. -source "${ALLSKY_HOME}/variables.sh" || exit ${ALLSKY_ERROR_STOP} -#shellcheck disable=SC2086 source-path=scripts -source "${ALLSKY_SCRIPTS}/functions.sh" || exit ${ALLSKY_ERROR_STOP} - -# This file defines functions plus sets many variables. -#shellcheck disable=SC2086 source-path=scripts -source "${ALLSKY_SCRIPTS}/installUpgradeFunctions.sh" || exit ${ALLSKY_ERROR_STOP} +#shellcheck source-path=. +source "${ALLSKY_HOME}/variables.sh" || exit "${ALLSKY_ERROR_STOP}" +#shellcheck source-path=scripts +source "${ALLSKY_SCRIPTS}/functions.sh" || exit "${ALLSKY_ERROR_STOP}" +#shellcheck source-path=scripts +source "${ALLSKY_SCRIPTS}/installUpgradeFunctions.sh" || exit "${ALLSKY_ERROR_STOP}" if [[ ${EUID} -eq 0 ]]; then display_msg error "This script must NOT be run as root, do NOT use 'sudo'." @@ -21,16 +19,9 @@ fi # This script assumes the user already did the "git clone" into ${ALLSKY_HOME}. # Some versions of Linux default to 750 so web server can't read it -#shellcheck disable=SC2086 -chmod 755 "${ALLSKY_HOME}" || exit ${ALLSKY_ERROR_STOP} - -#shellcheck disable=SC2086 -cd "${ALLSKY_HOME}" || exit ${ALLSKY_ERROR_STOP} +chmod 755 "${ALLSKY_HOME}" || exit "${ALLSKY_ERROR_STOP}" -# PRIOR_ALL_DIR is passed to us and is the location of an optional prior copy of Allsky. -PRIOR_CONFIG_DIR="${PRIOR_ALLSKY_DIR}/config" -PRIOR_CONFIG_FILE="${PRIOR_CONFIG_DIR}/config.sh" -PRIOR_FTP_FILE="${PRIOR_CONFIG_DIR}/ftp-settings.sh" # may change depending on old version +cd "${ALLSKY_HOME}" || exit "${ALLSKY_ERROR_STOP}" TITLE="Allsky Installer" FINAL_SUDOERS_FILE="/etc/sudoers.d/allsky" @@ -39,15 +30,26 @@ SETTINGS_FILE_NAME="$(basename "${SETTINGS_FILE}")" FORCE_CREATING_DEFAULT_SETTINGS_FILE="false" # should a default settings file be created? RESTORED_PRIOR_SETTINGS_FILE="false" PRIOR_SETTINGS_FILE="" # Full pathname to the prior settings file, if it exists -RESTORED_PRIOR_CONFIG_SH="false" # prior config.sh restored? -RESTORED_PRIOR_FTP_SH="false" # prior ftp-settings.sh restored? -ALLSKY_VERSION="$( get_version )" # version we're installing -PRIOR_ALLSKY="" # Set to "new" or "old" if they have a prior version -PRIOR_ALLSKY_VERSION="" # The version number of the prior version, if known +COPIED_PRIOR_CONFIG_SH="false" # prior config.sh's settings copied to settings file? +COPIED_PRIOR_FTP_SH="false" # prior ftp-settings.sh's settings copied to settings file? SUGGESTED_NEW_HOST_NAME="allsky" # Suggested new host name NEW_HOST_NAME="" # User-specified host name BRANCH="${GITHUB_MAIN_BRANCH}" # default branch +# Allsky versions +ALLSKY_VERSION="$( get_version )" # version we're installing +ALLSKY_BASE_VERSION="${ALLSKY_VERSION:0:11}" # without point release + # Base of first version with combined configuration files # TODO: UPDATE xxxx + #xxxxxxx COMBINED_BASE_VERSION="v2023.90.90" + # Base of first version with CAMERA_TYPE instead of CAMERA in config.sh and + # "cameratype" in the settings file. +FIRST_CAMERA_TYPE_BASE_VERSION="v2023.05.01" + # First Allsky version that used the "version" file. + # It's also when ftp-settings.sh moved to ${ALLSKY_CONFIG} +FIRST_VERSION_VERSION="v2022.03.01" + # versions before ${FIRST_VERSION_VERSION} that didn't have version numbers. +PRE_FIRST_VERSION_VERSION="old" + # Repo files REPO_SUDOERS_FILE="${ALLSKY_REPO}/sudoers.repo" REPO_WEBUI_DEFINES_FILE="${ALLSKY_REPO}/allskyDefines.inc.repo" @@ -91,7 +93,7 @@ OS="$(grep CODENAME /etc/os-release | cut -d= -f2)" # usually buster or bullseye ############################################## functions #### -# +# do_initial_heading() { if [[ ${UPDATE} == "true" ]]; then @@ -154,8 +156,7 @@ usage_and_exit() echo echo "'--function' executes the specified function and quits." echo - #shellcheck disable=SC2086 - exit_installation ${RET} + exit_installation "${RET}" } @@ -230,12 +231,18 @@ CAMERA_to_CAMERA_TYPE() ####### CONNECTED_CAMERAS="" +RPI_MODEL="" +ZWO_MODEL="" + get_connected_cameras() { local CC # If we can't determine the camera to use for RPi cameras it either means there is # no RPi camera, or something's wrong. - if determineCommandToUse "false" "" > /dev/null 2>&1 ; then + if C="$( determineCommandToUse "false" "" 2>&1 )" ; then + if [[ "${C}" == "libcamera-still" ]]; then + RPI_MODEL="$( libcamera-still --list-cameras | awk '{if ($1 == 0) print $3;}' 2>/dev/null )" + fi display_msg --log progress "RPi camera found." CC="RPi" fi @@ -282,57 +289,54 @@ CAMERA_TYPE="" select_camera_type() { if [[ -n ${PRIOR_ALLSKY} ]]; then - case "${PRIOR_ALLSKY_VERSION}" in - # New versions go here... - v2023.05.01*) - # New style Allsky using ${CAMERA_TYPE}. - CAMERA_TYPE="${PRIOR_CAMERA_TYPE}" - - # Don't bother with a message since this is a "similar" release. - if [[ -n ${CAMERA_TYPE} ]]; then - MSG="Using Camera Type '${CAMERA_TYPE}' from prior Allsky." - STATUS_VARIABLES+=("select_camera_type='true'\n") - STATUS_VARIABLES+=("CAMERA_TYPE='${CAMERA_TYPE}'\n") - display_msg --logonly info "${MSG}" - return - else - MSG="Camera Type not in prior new-style settings file." - display_msg --log error "${MSG}" - fi - ;; - - "v2022.03.01" | "old") - local CAMERA="$( get_variable "CAMERA" "${PRIOR_CONFIG_FILE}" )" - if [[ -n ${CAMERA} ]]; then - CAMERA_TYPE="$( CAMERA_to_CAMERA_TYPE "${CAMERA}" )" - STATUS_VARIABLES+=("select_camera_type='true'\n") - STATUS_VARIABLES+=("CAMERA_TYPE='${CAMERA_TYPE}'\n") - if [[ ${CAMERA} != "${CAMERA_TYPE}" ]]; then - NEW=" (now called ${CAMERA_TYPE})" - else - NEW="" - fi - display_msg --log progress "Using prior ${CAMERA} camera${NEW}." - return + if [[ ! ${PRIOR_ALLSKY_VERSION} < "${FIRST_CAMERA_TYPE_BASE_VERSION}" ]]; then + # New style Allsky using ${CAMERA_TYPE}. + CAMERA_TYPE="${PRIOR_CAMERA_TYPE}" + + if [[ -n ${CAMERA_TYPE} ]]; then + MSG="Using Camera Type '${CAMERA_TYPE}' from prior Allsky." + STATUS_VARIABLES+=("select_camera_type='true'\n") + STATUS_VARIABLES+=("CAMERA_TYPE='${CAMERA_TYPE}'\n") + display_msg --logonly info "${MSG}" + return + else + MSG="Camera Type not in prior new-style settings file." + display_msg --log error "${MSG}" + fi + else + # Older style using ${CAMERA} + local CAMERA="$( get_variable "CAMERA" "${PRIOR_CONFIG_FILE}" )" + if [[ -n ${CAMERA} ]]; then + CAMERA_TYPE="$( CAMERA_to_CAMERA_TYPE "${CAMERA}" )" + STATUS_VARIABLES+=("select_camera_type='true'\n") + STATUS_VARIABLES+=("CAMERA_TYPE='${CAMERA_TYPE}'\n") + if [[ ${CAMERA} != "${CAMERA_TYPE}" ]]; then + NEW=" (now called ${CAMERA_TYPE})" else - MSG="CAMERA not in prior old-style config.sh." - display_msg --log warning "${MSG}" + NEW="" fi - ;; - esac + display_msg --log progress "Using prior ${CAMERA} camera${NEW}." + return + else + MSG="CAMERA not in prior old-style config.sh." + display_msg --log warning "${MSG}" + fi + fi fi local CT=() local NUM=0 + RPI_MODEL="${RPI_MODEL:=Raspberry Pi (HQ, Module 3, and compatibles}" + ZWO_MODEL="${ZWO_MODEL:=ZWO_ASI}" if [[ ${CONNECTED_CAMERAS} == "RPi" ]]; then - CT+=("RPi" " Raspberry Pi (HQ, Module 3, and compatibles)") + CT+=("RPi" " ${RPI_MODEL}") ((NUM++)) elif [[ ${CONNECTED_CAMERAS} == "ZWO" ]]; then - CT+=("ZWO" " ZWO ASI") + CT+=("ZWO" " ${ZWO_MODEL}") ((NUM++)) elif [[ ${CONNECTED_CAMERAS} == "RPi ZWO" ]]; then - CT+=("RPi" " Raspberry Pi (HQ, Module 3, and compatibles)") - CT+=("ZWO" " ZWO ASI") + CT+=("RPi" " ${RPI_MODEL}") + CT+=("ZWO" " ${ZWO_MODEL}") ((NUM+=2)) else # shouldn't happen since we already checked MSG="INTERNAL ERROR:" @@ -378,6 +382,20 @@ check_for_raspistill() } +#### +# If the raspistill command exists on post-Buster releases, +# rename it so it's not used. +check_for_raspistill() +{ + STATUS_VARIABLES+=("check_for_raspistill='true'\n") + + if W="$( which raspistill )" && [[ ${OS} != "buster" ]]; then + display_msg --longonly info "Renaming 'raspistill' on ${OS}." + sudo mv "${W}" "${W}-OLD" + fi +} + + #### # Create the file that defines the WebUI variables. create_webui_defines() @@ -417,7 +435,7 @@ create_webui_defines() # This can be used after installation if the options file gets hosed. recreate_options_file() { - CAMERA_TYPE="$( get_variable "CAMERA_TYPE" "${ALLSKY_CONFIG}/config.sh" )" + CAMERA_TYPE="$( settings ".cameratype" )" save_camera_capabilities "true" set_permissions } @@ -467,9 +485,10 @@ save_camera_capabilities() MSG="${MSG} ${DEBUG_ARG} 'cameraType' 'Camera Type' '${PRIOR_CAMERA_TYPE}' '${CAMERA_TYPE}'" display_msg "${LOG_TYPE}" info "${MSG}" + local ERR="/tmp/makeChanges.errors.txt" #shellcheck disable=SC2086 MSG="$( "${ALLSKY_SCRIPTS}/makeChanges.sh" ${FORCE} ${OPTIONSONLY} --cameraTypeOnly \ - ${DEBUG_ARG} "cameraType" "Camera Type" "${PRIOR_CAMERA_TYPE}" "${CAMERA_TYPE}" 2>&1 )" + ${DEBUG_ARG} "cameraType" "Camera Type" "${PRIOR_CAMERA_TYPE}" "${CAMERA_TYPE}" 2> "${ERR}" )" RET=$? [[ -n ${MSG} ]] && display_msg "${LOG_TYPE}" info "${MSG}" @@ -484,12 +503,21 @@ save_camera_capabilities() display_msg --log error "Unable to save camera capabilities." fi return 1 + else + if [[ -s ${ERR} ]]; then + display_msg --log error "$( < "${ERR}" )" + fi + + if [[ ! -f ${SETTINGS_FILE} ]]; then + display_msg --log error "Settings file not created; cannot continue." + return 1 + fi fi #shellcheck disable=SC2012 MSG="$( /bin/ls -l "${ALLSKY_CONFIG}/settings"*.json 2>/dev/null | sed 's/^/ /' )" display_msg "${LOG_TYPE}" info "Settings files:\n${MSG}" - CAMERA_MODEL="$( settings ".cameraModel" "${SETTINGS_FILE}" )" + CAMERA_MODEL="$( settings ".cameramodel" "${SETTINGS_FILE}" )" STATUS_VARIABLES+=("save_camera_capabilities='true'\n") return 0 @@ -773,7 +801,7 @@ check_success() MSG="The full log file is in ${LOG}" MSG="${MSG}\nThe end of the file is:" display_msg --log info "${MSG}" - tail -5 "${LOG}" + tail "${LOG}" return 1 fi @@ -790,7 +818,7 @@ install_webserver() sudo systemctl stop hostapd 2> /dev/null sudo systemctl stop lighttpd 2> /dev/null - if [[ ${install_webserver_et_al} == "true" ]]; then + if [[ ${install_webserver} == "true" ]]; then display_msg --log progress "Preparing the web server." else display_msg --log progress "Installing the web server." @@ -802,7 +830,7 @@ install_webserver() if ! check_success $? "lighttpd installation failed" "${TMP}" "${DEBUG}" ; then exit_with_image 1 "${STATUS_ERROR}" "lighttpd installation failed" fi - STATUS_VARIABLES+=("install_webserver_et_al='true'\n") + STATUS_VARIABLES+=("install_webserver='true'\n") fi FINAL_LIGHTTPD_FILE="/etc/lighttpd/lighttpd.conf" @@ -922,10 +950,6 @@ set_permissions() # Not sure what to do about this... fi - # Remove any old entries; we now use /etc/sudoers.d/allsky instead of /etc/sudoers. - # TODO: Can remove this in the next release - sudo sed -i -e "/allsky/d" -e "/${WEBSERVER_GROUP}/d" /etc/sudoers - do_sudoers # The web server needs to be able to create and update many of the files in ${ALLSKY_CONFIG}. @@ -1329,11 +1353,12 @@ set_what_can_be_skipped() local OLD_VERSION="${1}" local OLD_BASE_VERSION="${OLD_VERSION:0:11}" # Without point release local NEW_VERSION="${2}" - if [[ ${NEW_VERSION} == "v2023.05.01_02" && ${OLD_BASE_VERSION} == "v2023.05.01" ]]; then + local NEW_BASE_VERSION="${NEW_VERSION:0:11}" # Without point release + if [[ ${NEW_BASE_VERSION} == "${OLD_BASE_VERSION}" ]]; then # No changes to these packages so no need to reinstall. MSG="Skipping installation of: webserver et.al., PHP modules, Trutype fonts, Python" display_msg --logonly info "${MSG}" - install_webserver_et_al="true" + install_webserver="true" installed_PHP_modules="true" installing_Trutype_fonts="true" installed_Python_dependencies="true" @@ -1348,8 +1373,10 @@ is_reboot_needed() local OLD_VERSION="${1}" local OLD_BASE_VERSION="${OLD_VERSION:0:11}" # Without point release local NEW_VERSION="${2}" - if [[ ${NEW_VERSION:0:11} == "v2023.05.01" && ${OLD_BASE_VERSION} == "v2023.05.01" ]]; then - # just bug fixes between the v2023.05.01 versions. + local NEW_BASE_VERSION="${NEW_VERSION:0:11}" + if [[ ${NEW_BASE_VERSION} == "${OLD_BASE_VERSION}" ]]; then +# TODO: this may not always be true. + # Assume just bug fixes between base versions. REBOOT_NEEDED="false" display_msg --logonly info "No reboot is needed." else @@ -1359,44 +1386,79 @@ is_reboot_needed() #### # See if a prior Allsky exists; if so, set some variables. + +# Globals +# PRIOR_ALLSKY_DIR set in variables.sh +PRIOR_ALLSKY="" # Set to "new" or "old" if they have a prior version +PRIOR_ALLSKY_VERSION="" # The version number of the prior version, if known +PRIOR_CAMERA_TYPE="" +PRIOR_CAMERA_MODEL="" +PRIOR_CONFIG_DIR="${PRIOR_ALLSKY_DIR}/$( basename "${ALLSKY_CONFIG}" )" # Prior "config" directory, if it exists +PRIOR_CONFIG_FILE="${PRIOR_CONFIG_DIR}/config.sh" # Location of prior "config.sh" file; varies by release +PRIOR_FTP_FILE="${PRIOR_CONFIG_DIR}/ftp-settings.sh" # Location of prior "ftp-settings.sh" file; varies by release + does_prior_Allsky_exist() { - PRIOR_ALLSKY="" - PRIOR_CAMERA_TYPE="" - PRIOR_CAMERA_MODEL="" - # Don't just look for the top-level directory. - if [[ ! -d ${PRIOR_CONFIG_DIR} ]]; then + local MSG CT_ CM_ + + # First just look for the top-level directory. + if [[ -d ${PRIOR_ALLSKY_DIR} ]]; then + display_msg --logonly info "Prior Allsky found in ${PRIOR_ALLSKY_DIR}." + else display_msg --logonly info "No prior Allsky found." return 1 fi + # All versions back to v0.6 (never checked prior ones) have a "scripts" directory. + if [[ ! -d "${PRIOR_ALLSKY_DIR}/scripts" ]]; then + MSG="Prior Allsky directory found at '${PRIOR_ALLSKY_DIR}' but it doesn't appear to be valid; ignoring it." + display_msg --log warning "${MSG}" + return 1 + fi - PRIOR_ALLSKY_VERSION="$( get_version "${PRIOR_ALLSKY_DIR}/" )" - if [[ -n ${PRIOR_ALLSKY_VERSION} ]]; then - display_msg --logonly info "Prior Allsky version ${PRIOR_ALLSKY_VERSION} found." - if [[ ${PRIOR_ALLSKY_VERSION} == "v2022.03.01" ]]; then # First Allsky version with a "version" file - # This is an old style Allsky with ${CAMERA} in config.sh. - # Don't do anything here; go to the "if" below. - : - else - # Newer version. - # PRIOR_SETTINGS_FILE should be a link to a camera-specific settings file. - PRIOR_ALLSKY="newStyle" - PRIOR_SETTINGS_FILE="${PRIOR_CONFIG_DIR}/${SETTINGS_FILE_NAME}" - if [[ -f ${PRIOR_SETTINGS_FILE} ]]; then - PRIOR_CAMERA_TYPE="$( settings ".cameraType" "${PRIOR_SETTINGS_FILE}" )" - PRIOR_CAMERA_MODEL="$( settings ".cameraModel" "${PRIOR_SETTINGS_FILE}" )" + # Determine the prior Allsky version and set some PRIOR_* locations. + PRIOR_ALLSKY_VERSION="$( get_version "${PRIOR_ALLSKY_DIR}/" )" # Returns "" if no version file. + if [[ -n ${PRIOR_ALLSKY_VERSION} && (! ${PRIOR_ALLSKY_VERSION} < "${FIRST_CAMERA_TYPE_BASE_VERSION}") ]]; then + PRIOR_ALLSKY="newStyle" + + # PRIOR_SETTINGS_FILE should be a link to a camera-specific settings file + # and that file will have the camera type and model. + PRIOR_SETTINGS_FILE="${PRIOR_CONFIG_DIR}/${SETTINGS_FILE_NAME}" + if [[ -f ${PRIOR_SETTINGS_FILE} ]]; then + if [[ ${ALLSKY_BASE_VERSION} == "${FIRST_CAMERA_TYPE_BASE_VERSION}" ]]; then + CT_=".cameraType" + CM_=".cameraModel" else - # This shouldn't happen... - PRIOR_SETTINGS_FILE="" - display_msg --log warning "No prior new style settings file found!" + CT_=".cameratype" + CM_=".cameramodel" fi + PRIOR_CAMERA_TYPE="$( settings "${CT_}" "${PRIOR_SETTINGS_FILE}" )" + PRIOR_CAMERA_MODEL="$( settings "${CM_}" "${PRIOR_SETTINGS_FILE}" )" + MSG="Prior Camera Type = ${PRIOR_CAMERA_TYPE}, prior model = ${PRIOR_CAMERA_MODEL}" + display_msg --logonly info "${MSG}" + else + # This shouldn't happen... + PRIOR_SETTINGS_FILE="" + display_msg --log warning "No prior new style settings file (${PRIOR_SETTINGS_FILE}) found!" fi - fi - if [[ -z ${PRIOR_ALLSKY} ]]; then + else # pre-${FIRST_VERSION_VERSION} + # V0.6, v0.7, and v0.8: + # "allsky" directory contained capture.cpp, config.sh. + # "scripts" directory had ftp-settings.sh. + # No "src" directory. + # NOTE: v0.6's capture.cpp said v0.5. + # V0.8.1 added "scr" and "config" directories and "variables.sh" file. + PRIOR_ALLSKY="oldStyle" - PRIOR_ALLSKY_VERSION="${PRIOR_ALLSKY_VERSION:-old}" + if [[ -z ${PRIOR_ALLSKY_VERSION} ]]; then + # No version file so try to determine version via .cpp file. + # sample: printf("%s *** Allsky Camera Software v0.8.3 | 2021 ***\n", c(KGRN)); + DIR="${PRIOR_ALLSKY_DIR}/src" + [[ ! -d "${DIR}" ]] && DIR="${PRIOR_ALLSKY_DIR}" + PRIOR_ALLSKY_VERSION="$( grep "Camera Software" "${DIR}/capture.cpp" | awk '{print $6}' )" + fi + PRIOR_ALLSKY_VERSION="${PRIOR_ALLSKY_VERSION:-${PRE_FIRST_VERSION_VERSION}}" local CAMERA="$( get_variable "CAMERA" "${PRIOR_CONFIG_FILE}" )" PRIOR_CAMERA_TYPE="$( CAMERA_to_CAMERA_TYPE "${CAMERA}" )" # PRIOR_CAMERA_MODEL wasn't stored anywhere so can't set it. @@ -1433,10 +1495,12 @@ prompt_for_prior_Allsky() PRIOR_ALLSKY_DIR="" PRIOR_ALLSKY="" PRIOR_ALLSKY_VERSION="" + PRIOR_SETTINGS_FILE="" CAMERA_TYPE="" PRIOR_CAMERA_TYPE="" MSG="If you want your old images, darks, settings, etc. from the prior version" MSG="${MSG} of Allsky, you'll need to manually move them to the new version." + MSG="${MSG}\nThis can take quite a while." whiptail --title "${TITLE}" --msgbox "${MSG}" 12 "${WT_WIDTH}" 3>&1 1>&2 2>&3 display_msg --logonly info "Will NOT restore from prior version of Allsky." fi @@ -1492,21 +1556,16 @@ install_dependencies_etc() #### -# Update config.sh -update_config_sh() +# Update variables.sh +update_variables_sh() { - local C="${ALLSKY_CONFIG}/config.sh" + local C="${ALLSKY_HOME}/variables.sh" display_msg --log progress "Updating some '${C}' variables." - if [[ -z ${CAMERA_TYPE} ]]; then - display_msg --log error "CAMERA_TYPE is empty in update_config_sh()" - CAMERA_TYPE="$( settings .cameraType )" - fi sed -i \ -e "s;^ALLSKY_VERSION=.*$;ALLSKY_VERSION=\"${ALLSKY_VERSION}\";" \ - -e "s;^CAMERA_TYPE=.*$;CAMERA_TYPE=\"${CAMERA_TYPE}\";" \ "${C}" - STATUS_VARIABLES+=( "update_config_sh='true'\n" ) + STATUS_VARIABLES+=( "update_variables_sh='true'\n" ) } @@ -1586,19 +1645,20 @@ get_lat_long() # then link the new one to the camera-specific name. convert_settings() # prior_version, new_version, prior_file, new_file { - PRIOR_VERSION="${1}" - NEW_VERSION="${2}" - NEW_BASE_VERSION="${NEW_VERSION:0:11}" # without point release - PRIOR_FILE="${3}" - NEW_FILE="${4}" + local PRIOR_VERSION="${1}" + local NEW_VERSION="${2}" + local NEW_BASE_VERSION="${NEW_VERSION:0:11}" # without point release + local PRIOR_FILE="${3}" + local NEW_FILE="${4}" [[ ${NEW_VERSION} == "${PRIOR_VERSION}" ]] && return - # TODO: new versions go here - if [[ ${NEW_BASE_VERSION} == "v2023.05.01" ]]; then + # This version moved the config.sh and ftp-setting.sh settings to settings.json + # or to ${ALLSKY_ENV} and made the setting names lowercase. Plus other changes. - # Replaced "meanthreshold" with "daymeanthreshold" and "nightmeanthreshold" + if [[ ${PRIOR_VERSION} < "v2023.05.01_02" ]]; then + # Replace "meanthreshold" with "daymeanthreshold" and "nightmeanthreshold" # if they don't already exist. # They were added in v2023.05.01_02. local F="meanthreshold" @@ -1606,34 +1666,32 @@ convert_settings() # prior_version, new_version, prior_file, new_file NIGHTMEANTHRESHOLD="$( settings ".night${F}" "${NEW_FILE}" )" if [[ -n ${DAYMEANSETTING} && -n ${NIGHTMEANSETTING} ]]; then display_msg --logonly info " day and night '${F}' already exist." - return - fi - - MEANTHRESHOLD="$( settings ".${F}" "${PRIOR_FILE}" )" - if [[ -n ${MEANTHRESHOLD} ]]; then - if [[ -z ${DAYMEANTHRESHOLD} ]]; then - display_msg --logonly info " Updating 'day${F}' in '${NEW_FILE}'." - update_json_file ".day${F}" "${MEANTHRESHOLD}" "${NEW_FILE}" - fi - if [[ -z ${NIGHTMEANTHRESHOLD} ]]; then - display_msg --logonly info " Updating 'night${F}' in '${NEW_FILE}'." - update_json_file ".night${F}" "${MEANTHRESHOLD}" "${NEW_FILE}" - fi - - # If ${F} exists in the new file - MEANTHRESHOLD="$( settings ".${F}" "${NEW_FILE}" )" + else + MEANTHRESHOLD="$( settings ".${F}" "${PRIOR_FILE}" )" if [[ -n ${MEANTHRESHOLD} ]]; then - display_msg --logonly info " Deleting '${F}' from '${NEW_FILE}'." - sed -i "/\"${F}\"/d" "${NEW_FILE}" + if [[ -z ${DAYMEANTHRESHOLD} ]]; then + display_msg --logonly info " Updating 'day${F}' in '${NEW_FILE}'." + update_json_file ".day${F}" "${MEANTHRESHOLD}" "${NEW_FILE}" + fi + if [[ -z ${NIGHTMEANTHRESHOLD} ]]; then + display_msg --logonly info " Updating 'night${F}' in '${NEW_FILE}'." + update_json_file ".night${F}" "${MEANTHRESHOLD}" "${NEW_FILE}" + fi + + # If ${F} exists in the new file + MEANTHRESHOLD="$( settings ".${F}" "${NEW_FILE}" )" + if [[ -n ${MEANTHRESHOLD} ]]; then + display_msg --logonly info " Deleting '${F}' from '${NEW_FILE}'." + sed -i "/\"${F}\"/d" "${NEW_FILE}" + fi + else + display_msg --logonly info " '${F}' was not in prior settings file." + fi - else - display_msg --logonly info " '${F}' was not in prior settings file." fi - - return fi - if [[ ${NEW_BASE_VERSION} == "v2023.05.01" && ${PRIOR_VERSION} == "v2022.03.01" ]]; then + if [[ ${PRIOR_VERSION} == "${FIRST_VERSION_VERSION}" ]]; then local B="$( basename "${NEW_FILE}" )" local NAME="${B%.*}" # before "." local EXT="${B##*.}" # after "." @@ -1641,44 +1699,49 @@ convert_settings() # prior_version, new_version, prior_file, new_file # For each field in prior file, update new file with old value. # Then handle new fields and fields that changed locations or names. - # convert_json_to_tabs outputs fields and values separated by tabs. + # outputJSONwithEqual.php outputs fields and values separated by "=". - convert_json_to_tabs "${PRIOR_FILE}" | + "${ALLSKY_WEBUI}/includes/outputJSONwithEqual.php" --settings-file "${PRIOR_FILE}" | + sed 's/=/\t/' | while read -r F V do - case "${F,,}" in + F="${F,,}" + case "${F}" in "lastchanged") V="$( date +'%Y-%m-%d %H:%M:%S' )" ;; # These don't exist anymore. - "autofocus"|"background") + "autofocus" | "background") + continue; + ;; + "brightness" | "daybrightness" | "nightbrightness" | "showbrightness") continue; ;; # These changed names. "darkframe") - F="takeDarkFrames" - ;; - "daymaxautoexposure") - F="daymaxautoexposure" + F="takedarkframes" ;; "daymaxgain") F="daymaxautogain" ;; - "nightmaxautoexposure") + "nightmaxexposure") F="nightmaxautoexposure" ;; "nightmaxgain") F="nightmaxautogain" ;; + "websiteurl") + F="remotewebsiteurl" + ;; + "imageurl") + F="remotewebsiteimageurl" + ;; # These now have day and night versions. - "brightness") - update_json_file ".day${F}" "${V}" "${NEW_FILE}" - F="night${F}" - ;; - "awb"|"autowhitebalance") + "awb" | "autowhitebalance") + F="awb" update_json_file ".day${F}" "${V}" "${NEW_FILE}" F="night${F}" ;; @@ -1691,17 +1754,15 @@ convert_settings() # prior_version, new_version, prior_file, new_file F="night${F}" ;; "targettemp") - F="TargetTemp" update_json_file ".day${F}" "${V}" "${NEW_FILE}" F="night${F}" ;; "coolerenabled") - F="EnableCooler" + F="enablecooler" # name change update_json_file ".day${F}" "${V}" "${NEW_FILE}" F="night${F}" ;; "meanthreshold") - F="meanthreshold" update_json_file ".day${F}" "${V}" "${NEW_FILE}" F="night${F}" ;; @@ -1709,19 +1770,282 @@ convert_settings() # prior_version, new_version, prior_file, new_file update_json_file ".${F}" "${V}" "${NEW_FILE}" done + fi +} - # Fields whose location changed. - x="$( get_variable "DAYTIME_CAPTURE" "${PRIOR_CONFIG_FILE}" )" - update_json_file ".takeDaytimeImages" "${x}" "${NEW_FILE}" +# Update the specified file with the specified new value. +doV() +{ + local V="${1}" # name of the variable + local VAL="${!V}" # value of the variable + local jV="${2}" # new json variable name + local TYPE="${3}" + local FILE="${4}" + + [[ -z ${VAL} ]] && return + + if [[ ${TYPE} == "boolean" ]]; then + # Some booleans used "true/false" and some used "1/0". + if [[ ${VAL} == "true" || ${VAL} == "1" ]]; then + VAL="true" + else + VAL="false" + fi + elif [[ ${TYPE} == "number" && -z ${VAL} ]]; then + VAL=0 # give it a default + fi - x="$( get_variable "DAYTIME_SAVE" "${PRIOR_CONFIG_FILE}" )" - update_json_file ".saveDaytimeImages" "${x}" "${NEW_FILE}" + if update_json_file "${jV}" "${VAL}" "${FILE}" "${TYPE}" ; then + display_msg --logonly info " ${V} (${VAL})" + else + display_msg --logonly error "Unable to update ${jV} from ${V} (${!V})" + fi +} + +# Copy everything from old config.sh to the settings file. +convert_config_sh() +{ + [[ ${convert_config_sh} == "true" ]] && return - x="$( get_variable "DARK_FRAME_SUBTRACTION" "${PRIOR_CONFIG_FILE}" )" - update_json_file ".useDarkFrames" "${x}" "${NEW_FILE}" + local OLD_CONFIG_FILE="${1}" + local NEW_FILE="${2}" + + if [[ ! -e ${OLD_CONFIG_FILE} ]]; then + display_msg --log info "No prior config.sh file to process." + return 1 fi + + display_msg --log progress "Copying contents of prior config.sh to the settings file." + ( + #shellcheck disable=SC1090 + if ! source "${OLD_CONFIG_FILE}" ; then + display_msg --log error "Unable to process prior config.sh file (${OLD_CONFIG_FILE})." + return 1 + fi + + local X # temporary variable + + if [[ -n ${DAYTIME} ]]; then # old name + X="${DAYTIME}" + else + X="${DAYTIME_CAPTURE}" + fi + if [[ -n ${CAPTURE_24HR} ]]; then # old name + X="${CAPTURE_24HR}" + else + X="${DAYTIME_SAVE}" + fi + doV "X" ".savedaytimeimages" "boolean" "${NEW_FILE}" + X="true"; doV "X" ".takenighttimeimages" "boolean" "${NEW_FILE}" # new + X="true"; doV "X" ".savenighttimeimages" "boolean" "${NEW_FILE}" # new + + doV "DARK_FRAME_SUBTRACTION" ".usedarkframes" "boolean" "${NEW_FILE}" + + # IMG_UPLOAD no longer used; instead, upload if FREQUENCY > 0. + # shellcheck disable=SC2034 + [[ ${IMG_UPLOAD} != "true" ]] && IMG_UPLOAD_FREQUENCY=0 + doV "IMG_UPLOAD_FREQUENCY" ".imageuploadfrequency" "number" "${NEW_FILE}" + + # IMG_RESIZE no longer used; only resize if width and height are > 0. + if [[ -n ${IMG_WIDTH} && ${IMG_WIDTH} -gt 0 && -n ${IMG_HEIGHT} && ${IMG_HEIGHT} -gt 0 ]]; + then + doV "IMG_WIDTH" ".imageresizewidth" "number" "${NEW_FILE}" + doV "IMG_HEIGHT" ".imageresizeheight" "number" "${NEW_FILE}" + else + MSG="Ignoring IMG_RESIZE since IMG_WIDTH (${IMG_WIDTH}) and/or IMG_HEIGHT (${IMG_HEIGHT}) are not positive numbers." + display_msg --log info "${MSG}" + X=0; doV "X" ".imageresizewidth" "number" "${NEW_FILE}" + X=0; doV "X" ".imageresizeheight" "number" "${NEW_FILE}" + fi + + # CROP_IMAGE, CROP_WIDTH, CROP_HEIGHT, CROP_OFFSET_X, and CROP_OFFSET_Y are no longer used. + # Instead the user specifies the number of pixels to crop from the top, right, bottom, and left. + # It's too difficult to convert old numbers to new, so force the user to enter new numbers. + # We'd need to know actual number of image pixels, bin level, and .width and .height to get + # the effective width and height, then convert. + if [[ ${CROP_IMAGE} == "true" ]]; then + MSG="The way to specify cropping images has changed." + MSG="${MSG}You need to reenter your crop settings." + MSG="${MSG}\nSpecify the amount to crop from the top, right, bottom, and left." + display_msg --log info "${MSG}" + X=0; doV "X" ".imagecroptop" "number" "${NEW_FILE}" + X=0; doV "X" ".imagecropright" "number" "${NEW_FILE}" + X=0; doV "X" ".imagecropbottom" "number" "${NEW_FILE}" + X=0; doV "X" ".imagecropleft" "number" "${NEW_FILE}" + fi + + # AUTOSTRETCH no longer used; only stretch if AMOUNT > 0 and MID_POINT != "" + X=0; doV "X" ".imagestretchamountdaytime" "number" "${NEW_FILE}" # new + X=0; doV "X" ".imagestretchmidpointdaytime" "text" "${NEW_FILE}" # new + # shellcheck disable=SC2034 + [[ ${AUTOSTRETCH} != "true" || -n ${AUTOSTRETCH_MID_POINT} ]] && AUTOSTRETCH_AMOUNT=0 + doV "AUTOSTRETCH_AMOUNT" ".imagestretchamountnighttime" "number" "${NEW_FILE}" + doV "AUTOSTRETCH_MID_POINT" ".imagestretchmidpointnighttime" "text" "${NEW_FILE}" + + # RESIZE_UPLOADS no longer used; resize only if width > 0 and height > 0. + if [[ ${RESIZE_UPLOADS} != "true" ]]; then + # shellcheck disable=SC2034 + RESIZE_UPLOADS_WIDTH=0; + # shellcheck disable=SC2034 + RESIZE_UPLOADS_HEIGHT=0 + fi + doV "RESIZE_UPLOADS_WIDTH" ".imageresizeuploadswidth" "number" "${NEW_FILE}" + doV "RESIZE_UPLOADS_HEIGHT" ".imageresizeuploadsheight" "number" "${NEW_FILE}" + + doV "IMG_CREATE_THUMBNAILS" ".imagecreatethumbnails" "boolean" "${NEW_FILE}" + + # REMOVE_BAD_IMAGES no longer used; remove only if low > 0 or high > 0. + if [[ ${REMOVE_BAD_IMAGES} != "true" ]]; then + # shellcheck disable=SC2034 + REMOVE_BAD_IMAGES_THRESHOLD_LOW=0; + # shellcheck disable=SC2034 + REMOVE_BAD_IMAGES_THRESHOLD_HIGH=0 + fi + doV "REMOVE_BAD_IMAGES_THRESHOLD_LOW" ".imageremovebadlow" "number" "${NEW_FILE}" + doV "REMOVE_BAD_IMAGES_THRESHOLD_HIGH" ".imageremovebadhigh" "number" "${NEW_FILE}" + + doV "TIMELAPSE" ".timelapsegenerate" "boolean" "${NEW_FILE}" + doV "TIMELAPSEWIDTH" ".timelapsewidth" "number" "${NEW_FILE}" + doV "TIMELAPSEHEIGHT" ".timelapseheight" "number" "${NEW_FILE}" + # We no longer include the trailing "k". + TIMELAPSE_BITRATE="${TIMELAPSE_BITRATE//k/}" + doV "TIMELAPSE_BITRATE" ".timelapsebitrate" "number" "${NEW_FILE}" + doV "FPS" ".timelapsefps" "number" "${NEW_FILE}" + doV "VCODEC" ".timelapsevcodec" "text" "${NEW_FILE}" + doV "PIX_FMT" ".timelapsepixfmt" "text" "${NEW_FILE}" + doV "FFLOG" ".timelapsefflog" "text" "${NEW_FILE}" + doV "KEEP_SEQUENCE" ".timelapsekeepsequence" "boolean" "${NEW_FILE}" + doV "TIMELAPSE_EXTRA_PARAMETERS" ".timelapseextraparameters" "text" "${NEW_FILE}" + doV "UPLOAD_VIDEO" ".timelapseupload" "boolean" "${NEW_FILE}" + doV "TIMELAPSE_UPLOAD_THUMBNAIL" ".timelapseuploadthumbnail" "boolean" "${NEW_FILE}" + + doV "TIMELAPSE_MINI_IMAGES" ".minitimelapsenumimages" "number" "${NEW_FILE}" + doV "TIMELAPSE_MINI_FORCE_CREATION" ".minitimelapseforcecreation" "boolean" "${NEW_FILE}" + doV "TIMELAPSE_MINI_FREQUENCY" ".minitimelapsefrequency" "number" "${NEW_FILE}" + doV "TIMELAPSE_MINI_UPLOAD_VIDIO" ".minitimelapseupload" "boolean" "${NEW_FILE}" + doV "TIMELAPSE_MINI_UPLOAD_THUMBNAIL" ".minitimelapseuploadthumbnail" "boolean" "${NEW_FILE}" + doV "TIMELAPSE_MINI_FPS" ".minitimelapsefps" "number" "${NEW_FILE}" + TIMELAPSE_MINI_BITRATE="${TIMELAPSE_MINI_BITRATE//k/}" + doV "TIMELAPSE_MINI_BITRATE" ".minitimelapsebitrate" "number" "${NEW_FILE}" + doV "TIMELAPSE_MINI_WIDTH" ".minitimelapsewidth" "number" "${NEW_FILE}" + doV "TIMELAPSE_MINI_HEIGHT" ".minitimelapseheight" "number" "${NEW_FILE}" + + doV "KEOGRAM" ".keogramgenerate" "boolean" "${NEW_FILE}" + doV "KEOGRAM_EXTRA_PARAMETERS" ".keogramextraparameters" "text" "${NEW_FILE}" + doV "UPLOAD_KEOGRAM" ".keogramupload" "boolean" "${NEW_FILE}" + X="true"; doV "X" ".keogramexpand" "boolean" "${NEW_FILE}" # new + X="simplex"; doV "X" ".keogramfontname" "text" "${NEW_FILE}" # new + X="#ffff"; doV "X" ".keogramfontcolor" "text" "${NEW_FILE}" # new + X=1; doV "X" ".keogramfontsize" "text" "${NEW_FILE}" # new + X=3; doV "X" ".keogramlinethickness" "text" "${NEW_FILE}" # new + + doV "STARTRAILS" ".startrailsgramgenerate" "boolean" "${NEW_FILE}" + doV "BRIGHTNESS_THRESHOLD" ".startrailsbrightnessthreshold" "number" "${NEW_FILE}" + doV "STARTRAILS_EXTRA_PARAMETERS" ".startrailsextraparameters" "text" "${NEW_FILE}" + doV "UPLOAD_STARTRAILS" ".startrailsupload" "boolean" "${NEW_FILE}" + + [[ -z ${THUMBNAIL_SIZE_X} ]] && THUMBNAIL_SIZE_X=100 + doV "THUMBNAIL_SIZE_X" ".thumbnailsizex" "number" "${NEW_FILE}" + [[ -z ${THUMBNAIL_SIZE_Y} ]] && THUMBNAIL_SIZE_Y=75 + doV "THUMBNAIL_SIZE_Y" ".thumbnailsizey" "number" "${NEW_FILE}" + + # NIGHTS_TO_KEEP was replaced by DAYS_TO_KEEP and the AUTO_DELETE boolean was deleted. + if [[ -n ${NIGHTS_TO_KEEP} && ${AUTO_DELETE} == "true" ]]; then + doV "NIGHTS_TO_KEEP" ".daystokeep" "number" "${NEW_FILE}" + else + doV "DAYS_TO_KEEP" ".daystokeep" "number" "${NEW_FILE}" + fi + doV "WEB_DAYS_TO_KEEP" ".daystokeeplocalwebsite" "number" "${NEW_FILE}" + X=0; doV "X" ".daystokeepremotewebsite" "number" "${NEW_FILE}" + doV "WEBUI_DATA_FILES" ".webuidatafiles" "text" "${NEW_FILE}" + doV "UHUBCTL_PATH" ".uhubctlpath" "text" "${NEW_FILE}" + doV "UHUBCTL_PORT" ".uhubctlport" "number" "${NEW_FILE}" + + ) || return 1 + + STATUS_VARIABLES+=( "convert_config_sh='true'\n" ) + convert_config_sh="true" + + return 0 } +# Copy everything from ftp-settings.sh to the settings file. +convert_ftp_sh() +{ + [[ ${convert_ftp_sh} == "true" ]] && return + + local FTP_FILE="${1}" + local NEW_FILE="${2}" + + if [[ ! -e ${FTP_FILE} ]]; then + display_msg --log info "No prior ftp-settings.sh file to process (${FTP_FILE})." + return 1 + fi + + display_msg --log progress "Copying contents of prior ftp-settings.sh to settings file." + ( + #shellcheck disable=SC1090 + if ! source "${FTP_FILE}" ; then + display_msg --log error "Unable to process prior ftp-settings.sh file (${FTP_FILE})." + return 1 + fi + + # Ignore the WEB_*_DIR entries - the user can no longer specify local directories. + # Ignore VIDEOS_DIR, KEOGRAM_DIR, STARTRAILS_DIR - the user can no longer specify them. + # Don't update REMOTEWEBSITE_* settings since they are new so have no prior value. + + # "local" PROTOCOL means they're using local Website. + # WEB_IMAGE_DIR means they have both local and remote Website. + if [[ -d ${ALLSKY_WEBSITE} && (${PROTOCOL} == "local" || -n ${WEB_IMAGE_DIR}) ]]; then + X="true"; doV "X" ".uselocalwebsite" "boolean" "${NEW_FILE}" + else + X="false"; doV "X" ".uselocalwebsite" "boolean" "${NEW_FILE}" + fi + if [[ (-n ${PROTOCOL} && ${PROTOCOL} != "local") || -n ${REMOTE_HOST} ]]; then + doV "PROTOCOL" ".remotewebsiteprotocol" "text" "${NEW_FILE}" + doV "IMAGE_DIR" ".remotewebsiteimagedir" "text" "${NEW_FILE}" + X="true"; doV "X" ".useremotewebsite" "boolean" "${NEW_FILE}" + else + X=""; doV "X" ".remotewebsiteprotocol" "text" "${NEW_FILE}" + X=""; doV "X" ".remotewebsiteimagedir" "text" "${NEW_FILE}" + X="false"; doV "X" ".useremotewebsite" "boolean" "${NEW_FILE}" + fi + doV "IMG_UPLOAD_ORIGINAL_NAME" ".remotewebsiteimageuploadoriginalname" "boolean" "${NEW_FILE}" + doV "VIDEOS_DESTINATION_NAME" ".remotewebsitevideodestinationname" "text" "${ALLSKY_ENV}" + doV "KEOGRAM_DESTINATION_NAME" ".remotewebsitekeogramdestinationname" "text" "${ALLSKY_ENV}" + doV "STARTRAILS_DESTINATION_NAME" ".remotewebsitestartrailsdestinationname" "text" "${ALLSKY_ENV}" + # shellcheck disable=SC2034 + [[ -n ${HOST} ]] && REMOTE_HOST="${HOST}" + doV "REMOTE_HOST" ".REMOTEWEBSITE_HOST" "text" "${ALLSKY_ENV}" + if [[ -z ${REMOTE_PORT} ]]; then + # Don't want a default value. + doV "REMOTE_PORT" ".REMOTEWEBSITE_PORT" "text" "${ALLSKY_ENV}" + else + doV "REMOTE_PORT" ".REMOTEWEBSITE_PORT" "number" "${ALLSKY_ENV}" + fi + # shellcheck disable=SC2034 + [[ -n ${USER} ]] && REMOTE_USER="${USER}" + doV "REMOTE_USER" ".REMOTEWEBSITE_USER" "text" "${ALLSKY_ENV}" + # shellcheck disable=SC2034 + [[ -n ${PASSWORD} ]] && REMOTE_PASSWORD="${PASSWORD}" + doV "REMOTE_PASSWORD" ".REMOTEWEBSITE_PASSWORD" "text" "${ALLSKY_ENV}" + doV "LFTP_COMMANDS" ".REMOTEWEBSITE_LFTP_COMMANDS" "text" "${ALLSKY_ENV}" + doV "SSH_KEY_FILE" ".REMOTEWEBSITE_SSH_KEY_FILE" "text" "${ALLSKY_ENV}" + doV "AWS_CLI_DIR" ".REMOTEWEBSITE_AWS_CLI_DIR" "text" "${ALLSKY_ENV}" + doV "S3_BUCKET" ".REMOTEWEBSITE_S3_BUCKET" "text" "${ALLSKY_ENV}" + doV "S3_ACL" ".REMOTEWEBSITE_S3_ACL" "text" "${ALLSKY_ENV}" + doV "GCS_BUCKET" ".REMOTEWEBSITE_GCS_BUCKET" "text" "${ALLSKY_ENV}" + doV "GCS_ACL" ".REMOTEWEBSITE_GCS_ACL" "text" "${ALLSKY_ENV}" + + # Remote server + doV "IMG_UPLOAD_ORIGINAL_NAME" ".remoteserverimageuploadoriginalname" "boolean" "${NEW_FILE}" + ) || return 1 + + STATUS_VARIABLES+=( "convert_ftp_sh='true'\n" ) + convert_ftp_sh="true" + + return 0 +} #### # Restore the prior settings file(s) if the user wanted to use them. @@ -1821,13 +2145,15 @@ restore_prior_settings_file() # Transfer prior settings to the new file. case "${PRIOR_ALLSKY_VERSION}" in - "v2022.03.01") + "${FIRST_VERSION_VERSION}") convert_settings "${PRIOR_ALLSKY_VERSION}" "${ALLSKY_VERSION}" \ "${PRIOR_SETTINGS_FILE}" "${SETTINGS_FILE}" MSG="Your old WebUI settings were transfered to the new release," - MSG="${MSG}\n but note that there have been some changes to the settings file." - MSG="${MSG}\n\nCheck your settings in the WebUI's 'Allsky Settings' page." + MSG="${MSG}\n but note that there have been some changes to the settings file" + MSG="${MSG} (e.g., settings in ftp-settings.sh are now in the settings file)." + MSG="${MSG}\n\nPlease check your settings in the WebUI's 'Allsky Settings' page." + whiptail --title "${TITLE}" --msgbox "${MSG}" 18 "${WT_WIDTH}" 3>&1 1>&2 2>&3 display_msg info "\n${MSG}\n" echo -e "\n\n==========\n${MSG}" >> "${POST_INSTALLATION_ACTIONS}" @@ -1851,8 +2177,9 @@ restore_prior_settings_file() MSG="You need to manually transfer your old settings to the WebUI.\n" MSG="${MSG}\nNote that there have been many changes to the settings file" - MSG="${MSG} since you last installed Allsky, so you will need" - MSG="${MSG} to re-enter everything via the WebUI's 'Allsky Settings' page." + MSG="${MSG} since you last installed Allsky, so please use the " + MSG="${MSG} the WebUI's 'Allsky Settings' page." + whiptail --title "${TITLE}" --msgbox "${MSG}" 18 "${WT_WIDTH}" 3>&1 1>&2 2>&3 display_msg info "\n${MSG}\n" echo -e "\n\n==========\n${MSG}" >> "${POST_INSTALLATION_ACTIONS}" @@ -1862,7 +2189,7 @@ restore_prior_settings_file() esac # Set to null to force the user to look at the settings before Allsky will run. - update_json_file ".lastChanged" "" "${SETTINGS_FILE}" + update_json_file ".lastchanged" "" "${SETTINGS_FILE}" RESTORED_PRIOR_SETTINGS_FILE="true" FORCE_CREATING_DEFAULT_SETTINGS_FILE="false" @@ -1889,7 +2216,7 @@ restore_prior_files() fi if [[ -z ${PRIOR_ALLSKY} ]]; then - get_lat_long # get them to put in new config file + get_lat_long # prompt for them to put in new settings file mkdir -p "${ALLSKY_EXTRA}" # default permissions is ok return # Nothing left to do in this function, so return @@ -1902,20 +2229,22 @@ restore_prior_files() local SPACE=" " local NOT_RESTORED="NO PRIOR VERSION" - # TODO: endOfNight_additionalStepts.sh script is going away in the next major release. + local ITEM="${SPACE}endOfNight_additionalSteps.sh" if [[ -f ${PRIOR_ALLSKY_DIR}/scripts/endOfNight_additionalSteps.sh ]]; then - display_msg --log progress "${ITEM}" - cp -a "${PRIOR_ALLSKY_DIR}/scripts/endOfNight_additionalSteps.sh" "${ALLSKY_SCRIPTS}" - - MSG="The ${ALLSKY_SCRIPTS}/endOfNight_additionalSteps.sh file will be removed" - MSG="${MSG}\nin the next version of Allsky. You appear to be using this file," - MSG="${MSG}\nso please move your code to the 'Script' module in" + MSG="The ${ALLSKY_SCRIPTS}/endOfNight_additionalSteps.sh file is no longer supported." + MSG="${MSG}\nPlease move your code in that file to the 'Script' module in" MSG="${MSG}\nthe 'Night to Day Transition Flow' of the Module Manager." MSG="${MSG}\nSee the 'Explanations --> Module' documentation for more details." display_msg --log warning "\n${MSG}\n" echo -e "\n\n==========\n${MSG}" >> "${POST_INSTALLATION_ACTIONS}" - else + fi + + local E="$( basename "${ALLSKY_ENV}" )" + ITEM="${SPACE}'${E}' file" + if [[ -f ${PRIOR_ALLSKY_DIR}/${E} ]]; then + display_msg --log progress "${ITEM}" + cp -ar "${PRIOR_ALLSKY_DIR}/${E}" "${ALLSKY_ENV}" display_msg --log progress "${ITEM}: ${NOT_RESTORED}" fi @@ -1936,7 +2265,7 @@ restore_prior_files() display_msg --log progress "${ITEM}: ${NOT_RESTORED}" fi - ITEM="${SPACE}'modules' directory" + ITEM="${SPACE}'config/modules' directory" if [[ -d ${PRIOR_CONFIG_DIR}/modules ]]; then display_msg --log progress "${ITEM}" @@ -1948,7 +2277,7 @@ restore_prior_files() display_msg --log progress "${ITEM}: ${NOT_RESTORED}" fi - ITEM="${SPACE}'overlay' directory" + ITEM="${SPACE}'config/overlay' directory" if [[ -d ${PRIOR_CONFIG_DIR}/overlay ]]; then display_msg --log progress "${ITEM}" cp -ar "${PRIOR_CONFIG_DIR}/overlay" "${ALLSKY_CONFIG}" @@ -1960,7 +2289,7 @@ restore_prior_files() display_msg --log progress "${ITEM}: ${NOT_RESTORED}" fi - ITEM="${SPACE}'ssl' directory" + ITEM="${SPACE}'config/ssl' directory" if [[ -d ${PRIOR_CONFIG_DIR}/ssl ]]; then display_msg --log progress "${ITEM}" cp -ar "${PRIOR_CONFIG_DIR}/ssl" "${ALLSKY_CONFIG}" @@ -1970,8 +2299,9 @@ restore_prior_files() fi # This is not in a "standard" directory so we need to determine where it was. - local EXTRA="${PRIOR_ALLSKY_DIR}${ALLSKY_EXTRA//${ALLSKY_HOME}/}" - ITEM="${SPACE}'${EXTRA}' directory" + local E="${ALLSKY_EXTRA//${ALLSKY_HOME}\//}" + local EXTRA="${PRIOR_ALLSKY_DIR}${E}" + ITEM="${SPACE}'${E}' directory" if [[ -d ${EXTRA} ]]; then display_msg --log progress "${ITEM}" cp -ar "${EXTRA}" "${ALLSKY_EXTRA}/.." @@ -1986,8 +2316,9 @@ restore_prior_files() # raspap.auth was in a different directory in older versions. D="${OLD_RASPAP_DIR}" fi - ITEM="${SPACE}WebUI security settings" - if [[ -f ${D}/raspap.auth ]]; then + local R="raspap.auth" + ITEM="${SPACE}WebUI security settings (${R})." + if [[ -f ${D}/${R} ]]; then display_msg --log progress "${ITEM}" cp -a "${D}/raspap.auth" "${ALLSKY_CONFIG}" else @@ -2049,68 +2380,33 @@ restore_prior_files() # Do NOT restore options.json - it will be recreated. - # See if the prior config.sh and ftp-setting.sh are the same version as - # the new ones; if so, we can copy them to the new version. - # Currently what's in ${ALLSKY_CONFIG} are copies of the repo files. - RESTORED_PRIOR_CONFIG_SH="false" # Global variable - RESTORED_PRIOR_FTP_SH="false" # Global variable - - local CONFIG_SH_VERSION="$( get_variable "CONFIG_SH_VERSION" "${ALLSKY_CONFIG}/config.sh" )" - local PRIOR_CONFIG_SH_VERSION="$( get_variable "CONFIG_SH_VERSION" "${PRIOR_CONFIG_FILE}" )" - ITEM="${SPACE}'config.sh' file" - if [[ ${CONFIG_SH_VERSION} == "${PRIOR_CONFIG_SH_VERSION}" ]]; then - display_msg --log progress "${ITEM}, as is" - cp "${PRIOR_CONFIG_FILE}" "${ALLSKY_CONFIG}" && RESTORED_PRIOR_CONFIG_SH="true" - else - if [[ -z ${PRIOR_CONFIG_SH_VERSION} ]]; then - MSG="no prior version specified" - else - # v2023.05.01 is hopefully the last version with config.sh so don't - # bother writing a function to convert from the prior version to this. - MSG="prior version is old (${PRIOR_CONFIG_SH_VERSION})" - fi - display_msg --log progress "${ITEM}: ${NOT_RESTORED}: ${MSG}" + # Done with restores, now the updates. + + COPIED_PRIOR_CONFIG_SH="true" # Global variable + if [[ -s ${PRIOR_CONFIG_FILE} ]]; then + # This copies the settings from the prior config file to the settings file. + convert_config_sh "${PRIOR_CONFIG_FILE}" "${SETTINGS_FILE}" || COPIED_PRIOR_CONFIG_SH="false" fi - MSG=" CONFIG_SH_VERSION=${CONFIG_SH_VERSION}, PRIOR=${PRIOR_CONFIG_SH_VERSION}" - display_msg "${LOG_TYPE}" info "${MSG}" + STATUS_VARIABLES+=( "COPIED_PRIOR_CONFIG_SH='${COPIED_PRIOR_CONFIG_SH}'\n" ) - # Unlike the config.sh file which was always in allsky/config, - # the ftp-settings.sh file used to be in allsky/scripts. + # The ftp-settings.sh file used to be in allsky/scripts + # but moved to allsky/config in version ${FIRST_VERSION_VERSION} # Get the current and prior (if any) file version. - local FTP_SH_VERSION="$( get_variable "FTP_SH_VERSION" "${ALLSKY_CONFIG}/ftp-settings.sh" )" - local PRIOR_FTP_SH_VERSION if [[ -f ${PRIOR_FTP_FILE} ]]; then - # Allsky v2022.03.01 and newer. v2022.03.01 doesn't have FTP_SH_VERSION. - PRIOR_FTP_SH_VERSION="$( get_variable "FTP_SH_VERSION" "${PRIOR_FTP_FILE}" )" - PRIOR_FTP_SH_VERSION="${PRIOR_FTP_SH_VERSION:-"no version"}" + # Version ${FIRST_VERSION_VERSION} and newer. + : elif [[ -f ${PRIOR_ALLSKY_DIR}/scripts/ftp-settings.sh ]]; then - # pre v2022.03.01 + # pre ${FIRST_VERSION_VERSION} PRIOR_FTP_FILE="${PRIOR_ALLSKY_DIR}/scripts/ftp-settings.sh" - PRIOR_FTP_SH_VERSION="old" else - display_msg --log error "Unable to find prior ftp-settings.sh" + display_msg --log error "Unable to find prior ftp-settings.sh (${PRIOR_FTP_FILE})." PRIOR_FTP_FILE="" - PRIOR_FTP_SH_VERSION="no file" fi - - ITEM="${SPACE}ftp-settings.sh" - if [[ ${FTP_SH_VERSION} == "${PRIOR_FTP_SH_VERSION}" ]]; then - display_msg --log progress "${ITEM}, as is" - cp "${PRIOR_FTP_FILE}" "${ALLSKY_CONFIG}" && RESTORED_PRIOR_FTP_SH="true" - else - if [[ ${PRIOR_FTP_SH_VERSION} == "no version" ]]; then - MSG=": unknown prior FTP_SH_VERSION" - elif [[ ${PRIOR_FTP_SH_VERSION} == "old" ]]; then - MSG=": old location so no FTP_SH_VERSION" - elif [[ ${PRIOR_FTP_SH_VERSION} != "no file" ]]; then - MSG=": unknown PRIOR_FTP_SH_VERSION: '${PRIOR_FTP_SH_VERSION}'" - fi - display_msg --log progress "${ITEM}: ${NOT_RESTORED}${MSG}" + COPIED_PRIOR_FTP_SH="true" # Global variable + if [[ -s ${PRIOR_FTP_FILE} ]]; then + convert_ftp_sh "${PRIOR_FTP_FILE}" "${SETTINGS_FILE}" || COPIED_PRIOR_FTP_SH="false" fi - MSG=" FTP_SH_VERSION=${FTP_SH_VERSION}, PRIOR=${PRIOR_FTP_SH_VERSION}" - display_msg "${LOG_TYPE}" info "${MSG}" - - # Done with restores, now the updates. + STATUS_VARIABLES+=( "COPIED_PRIOR_FTP_SH='${COPIED_PRIOR_FTP_SH}'\n" ) if [[ -f ${PRIOR_CONFIG_DIR}/${ALLSKY_REMOTE_WEBSITE_CONFIGURATION_NAME} ]]; then if [[ ${V} != "${ALLSKY_VERSION}" ]]; then @@ -2123,70 +2419,22 @@ restore_prior_files() fi fi - if [[ ${CONFIG_SH_VERSION} == "${PRIOR_CONFIG_SH_VERSION}" ]]; then - # This version should be the same as the what's in the prior "version" file. - local PRIOR="$( get_variable "ALLSKY_VERSION" "${PRIOR_CONFIG_FILE}" )" - if [[ ${PRIOR} != "${ALLSKY_VERSION}" ]]; then - MSG="Updating ALLSKY_VERSION in 'config.sh' to '${ALLSKY_VERSION}'." - sed -i "/ALLSKY_VERSION=/ c ALLSKY_VERSION=\"${ALLSKY_VERSION}\"" "${PRIOR_CONFIG_FILE}" - display_msg --log progress "${MSG}" - else - MSG="ALLSKY_VERSION (${PRIOR}) in prior config.sh same as new version." - display_msg --logonly info "${MSG}" - fi - fi - - STATUS_VARIABLES+=( "RESTORED_PRIOR_CONFIG_SH='${RESTORED_PRIOR_CONFIG_SH}'\n" ) - STATUS_VARIABLES+=( "RESTORED_PRIOR_FTP_SH='${RESTORED_PRIOR_FTP_SH}'\n" ) - - if [[ ${RESTORED_PRIOR_CONFIG_SH} == "true" && ${RESTORED_PRIOR_FTP_SH} == "true" ]]; then + if [[ ${COPIED_PRIOR_CONFIG_SH} == "true" && ${COPIED_PRIOR_FTP_SH} == "true" ]]; then return 0 fi - if [[ ${PRIOR_ALLSKY} == "newStyle" ]]; then - # The prior versions are similar to the new ones. - MSG="" - # If it has a version number it's probably close to the current version. - if [[ ${RESTORED_PRIOR_CONFIG_SH} == "false" && -n ${PRIOR_CONFIG_SH_VERSION} ]]; then - MSG="${MSG}\nYour prior 'config.sh' file is similar to the new one." - fi - if [[ ${RESTORED_PRIOR_FTP_SH} == "false" && ${PRIOR_FTP_SH_VERSION} == "no version" ]]; then - MSG="${MSG}\nYour prior 'ftp-settings.sh' file is similar to the new one." - fi - # Don't wantn this line in the post-installation file. - MSGb="\nAfter installation, see ${POST_INSTALLATION_ACTIONS} for details." - - MSG2="You can compare the old and new configuration files using the following commands," - MSG2="${MSG2}\nand apply your changes from the prior file to the new file." - MSG2="${MSG2}\nDo NOT simply copy the old files to the new location because" - MSG2="${MSG2}\ntheir formats are different." - MSG2="${MSG2}\n\ndiff ${PRIOR_CONFIG_DIR}/config.sh ${ALLSKY_CONFIG}" - MSG2="${MSG2}\n\n and" - MSG2="${MSG2}\n\ndiff ${PRIOR_FTP_FILE} ${ALLSKY_CONFIG}" - else - MSG="You need to manually move the CONTENTS of:" - if [[ ${RESTORED_PRIOR_CONFIG_SH} == "false" ]]; then - MSG="${MSG}\n ${PRIOR_CONFIG_DIR}/config.sh" - fi - if [[ ${RESTORED_PRIOR_FTP_SH} == "false" ]]; then - MSG="${MSG}\n ${PRIOR_FTP_FILE}" - fi - MSG="${MSG}\n\nto the new files in ${ALLSKY_CONFIG}." - MSG="${MSG}\n\nNOTE: some settings are no longer in the new files and some changed names" - MSG="${MSG}\nso NOT add the old/deleted settings back in or simply copy the files." - MSG="${MSG}\n*** This will take several minutes ***" - MSGb="" - MSG2="" + MSG="You need to manually move the CONTENTS of:" + if [[ ${COPIED_PRIOR_CONFIG_SH} == "false" ]]; then + MSG="${MSG}\n ${PRIOR_CONFIG_DIR}/config.sh" + fi + if [[ ${COPIED_PRIOR_FTP_SH} == "false" ]]; then + MSG="${MSG}\n ${PRIOR_FTP_FILE}" fi - MSG="${MSG}" - whiptail --title "${TITLE}" --msgbox "${MSG}${MSGb}" 20 "${WT_WIDTH}" 3>&1 1>&2 2>&3 + MSG="${MSG}\n\nto the settings in the WebUI." + whiptail --title "${TITLE}" --msgbox "${MSG}" 20 "${WT_WIDTH}" 3>&1 1>&2 2>&3 - display_msg --log info "\n${MSG}${MSGb}\n" + display_msg --log info "\n${MSG}\n" echo -e "\n\n==========\n${MSG}" >> "${POST_INSTALLATION_ACTIONS}" - if [[ -n ${MSG2} ]]; then - display_msg --log info "\n${MSG2}\n" - echo -e "\n${MSG2}" >> "${POST_INSTALLATION_ACTIONS}" - fi } @@ -2195,12 +2443,10 @@ restore_prior_files() # This can be needed if the user hosed something up, or there was a problem somewhere. do_update() { -# TODO: get from settings file - #shellcheck disable=SC2086,SC1091 # file doesn't exist in GitHub - source "${ALLSKY_CONFIG}/config.sh" || exit ${ALLSKY_ERROR_STOP} # Get current CAMERA_TYPE + CAMERA_TYPE="$( settings ".cameratype" )" if [[ -z ${CAMERA_TYPE} ]]; then - display_msg --log error "CAMERA_TYPE not set in config.sh." - exit_installation 1 "${STATUS_ERROR}" "No CAMERA_TYPE in config.sh during update." + display_msg --log error "Camera Type not set in settings file." + exit_installation 1 "${STATUS_ERROR}" "No Camera Type in settings file during update." fi [[ ${create_webui_defines} != "true" ]] && create_webui_defines @@ -2211,9 +2457,10 @@ do_update() # Update the sudoers file if it's missing some entries. # Look for the last entry added (should be the last entry in the file). # Don't simply copy the repo file to the final location in case the repo file isn't up to date. - if ! grep --silent "/date" "${FINAL_SUDOERS_FILE}" ; then + local LAST_COMMAND="/date" + if ! grep --silent "${LAST_COMMAND}" "${FINAL_SUDOERS_FILE}" ; then display_msg --log progress "Updating sudoers list." - if ! grep --silent "/date" "${REPO_SUDOERS_FILE}" ; then + if ! grep --silent "${LAST_COMMAND}" "${REPO_SUDOERS_FILE}" ; then local F="$( basename "${REPO_SUDOERS_FILE}" )" MSG="Please get the newest '${F}' file from Git and try again." display_msg --log error "${MSG}" @@ -2324,7 +2571,7 @@ install_overlay() # Do the rest, even if we already did it in a previous installation, # in case something in the directories changed. - display_msg --log progress "Setting up modules and overlays." + display_msg --log progress "Setting up default modules and overlays." # These will get overwritten if the user has prior versions. cp -ar "${ALLSKY_REPO}/overlay" "${ALLSKY_CONFIG}" cp -ar "${ALLSKY_REPO}/modules" "${ALLSKY_CONFIG}" @@ -2440,9 +2687,9 @@ exit_with_image() check_restored_settings() { if [[ ${RESTORED_PRIOR_SETTINGS_FILE} == "true" && \ - ${RESTORED_PRIOR_CONFIG_SH} == "true" && \ - ${RESTORED_PRIOR_FTP_SH} == "true" ]]; then - # We restored all the prior settings no configuration is needed. + ${COPIED_PRIOR_CONFIG_SH} == "true" && \ + ${COPIED_PRIOR_FTP_SH} == "true" ]]; then + # We restored all the prior settings so no configuration is needed. # However, check if a reboot is needed. CONFIGURATION_NEEDED="false" IMG="" # Removes existing image @@ -2450,7 +2697,7 @@ check_restored_settings() IMG="RebootNeeded" fi display_image "${IMG}" - return 0 + return fi local AFTER @@ -2465,15 +2712,6 @@ check_restored_settings() MSG="${MSG} 'Allsky Settings' page in the WebUI after ${AFTER}." whiptail --title "${TITLE}" --msgbox "${MSG}" 12 "${WT_WIDTH}" 3>&1 1>&2 2>&3 fi - if [[ ${RESTORED_PRIOR_CONFIG_SH} == "false" || \ - ${RESTORED_PRIOR_FTP_SH} == "false" ]]; then - MSG="Default files were created for:" - [[ ${RESTORED_PRIOR_CONFIG_SH} == "false" ]] && MSG="${MSG}\n config.sh" - [[ ${RESTORED_PRIOR_FTP_SH} == "false" ]] && MSG="${MSG}\n ftp-settings.sh" - MSG="${MSG}\n\nHowever, you must update them by going to the" - MSG="${MSG} 'Editor' page in the WebUI after ${AFTER}." - whiptail --title "${TITLE}" --msgbox "${MSG}" 12 "${WT_WIDTH}" 3>&1 1>&2 2>&3 - fi display_image "ConfigurationNeeded" CONFIGURATION_NEEDED="true" @@ -2484,16 +2722,16 @@ check_restored_settings() # See if the new ZWO exposure algorithm should be used. check_new_exposure_algorithm() { - local FIELD="experimentalExposure" + local FIELD="experimentalexposure" local NEW="$( settings ".${FIELD}" )" - [[ ${NEW} -eq 1 ]] && return + [[ ${NEW} == "true" ]] && return - MSG="There is a new auto-exposure algorithm for nighttime images that initial testing indicates" - MSG="${MSG} it creates better images at night and during the day-to-night transition." + MSG="There is a new auto-exposure algorithm for nighttime images that" + MSG="${MSG} creates better images at night and during the day-to-night transition." MSG="${MSG}\n\nDo you want to use it?" if whiptail --title "${TITLE}" --yesno "${MSG}" 15 "${WT_WIDTH}" 3>&1 1>&2 2>&3; then display_msg --logonly info "Enabling ${FIELD}." - update_json_file ".${FIELD}" 1 "${SETTINGS_FILE}" + update_json_file ".${FIELD}" "true" "${SETTINGS_FILE}" MSG="Please provide feedback on the new auto-exposure algorithm" MSG="${MSG} by entering a Discussion item in GitHub." @@ -2582,8 +2820,7 @@ exit_installation() fi # Don't exit for negative numbers. - #shellcheck disable=SC2086 - [[ ${RET} -ge 0 ]] && exit ${RET} + [[ ${RET} -ge 0 ]] && exit "${RET}" } @@ -2618,17 +2855,16 @@ if [[ ${IN_TESTING} == "true" ]]; then MSG="\n" MSG="${MSG}Testers, until we go-live with this release, debugging is automatically on." MSG="${MSG}\n\nPlease set Debug Level to 3 during testing." + MSG="${MSG}\n\n" MSG="${MSG}\n" - MSG="${MSG}\nChanges from prior release:" + MSG="${MSG}\nChanges from prior dev release:" - MSG="${MSG}\n * Support for RPi Version 1 camera" - MSG="${MSG}\n * Bug fixes, especially with the Overlay Editor" + MSG="${MSG}\n * ftp-settings.sh and config.sh are gone." + MSG="${MSG}\n Their settings are now in the WebUI's 'Allsky Settings' page." -# MSG="${MSG}\n" -# MSG="${MSG}\n * change 2" - MSG="${MSG}\n\nIf you agree, enter: yes" + MSG="${MSG}\n\nIf you want to continue with the installation, enter: yes" A=$(whiptail --title "*** MESSAGE FOR TESTERS ***" --inputbox "${MSG}" 26 "${WT_WIDTH}" 3>&1 1>&2 2>&3) if [[ $? -ne 0 || ${A} != "yes" ]]; then MSG="\nYou need to TYPE 'yes' to continue the installation." @@ -2642,7 +2878,6 @@ fi UPDATE="false" FUNCTION="" -TESTING="false" while [ $# -gt 0 ]; do ARG="${1}" case "${ARG}" in @@ -2661,10 +2896,6 @@ while [ $# -gt 0 ]; do FUNCTION="${2}" shift ;; - --testing) - TESTING="true" # TODO: developer testing - skip many steps -TESTING="${TESTING}" # xxx keeps shellcheck quiet - ;; *) display_msg --log error "Unknown argument: '${ARG}'." OK="false" @@ -2674,12 +2905,8 @@ TESTING="${TESTING}" # xxx keeps shellcheck quiet done -if [[ -n ${FUNCTION} ]]; then - # Don't log when a single function is executed. - DISPLAY_MSG_LOG="" -else - mkdir -p "${ALLSKY_INSTALLATION_LOGS}" - +mkdir -p "${ALLSKY_INSTALLATION_LOGS}" +if [[ -z ${FUNCTION} ]]; then display_msg "${LOG_TYPE}" info "STARTING INSTALLATON AT $(date).\n" fi @@ -2869,8 +3096,8 @@ install_overlay ##### Restore prior files if needed [[ ${restore_prior_files} != "true" ]] && restore_prior_files # prompts if prior Allsky exists -##### Update config.sh -[[ ${update_config_sh} != "true" ]] && update_config_sh +##### Update variables.sh +[[ ${update_variables_sh} != "true" ]] && update_variables_sh ##### Set permissions. Want this at the end so we make sure we get all files. # Re-run every time in case permissions changed. diff --git a/scripts/check_allsky.sh b/scripts/check_allsky.sh index 595a00303..b3a293a83 100755 --- a/scripts/check_allsky.sh +++ b/scripts/check_allsky.sh @@ -7,15 +7,15 @@ # TODO: Right now the checks within each heading are in the order I thought of them! # Allow this script to be executed manually, which requires several variables to be set. -[[ -z ${ALLSKY_HOME} ]] && export ALLSKY_HOME="$(realpath "$(dirname "${BASH_ARGV0}")/..")" -ME="$(basename "${BASH_ARGV0}")" +[[ -z ${ALLSKY_HOME} ]] && export ALLSKY_HOME="$( realpath "$( dirname "${BASH_ARGV0}")/.." )" +ME="$( basename "${BASH_ARGV0}" )" -#shellcheck disable=SC2086 source-path=. -source "${ALLSKY_HOME}/variables.sh" || exit ${ALLSKY_ERROR_STOP} -#shellcheck disable=SC2086 source-path=scripts -source "${ALLSKY_SCRIPTS}/functions.sh" || exit ${ALLSKY_ERROR_STOP} -#shellcheck disable=SC2086 source-path=scripts -source "${ALLSKY_SCRIPTS}/installUpgradeFunctions.sh" || exit ${ALLSKY_ERROR_STOP} +#shellcheck source-path=. +source "${ALLSKY_HOME}/variables.sh" || exit "${ALLSKY_ERROR_STOP}" +#shellcheck source-path=scripts +source "${ALLSKY_SCRIPTS}/functions.sh" || exit "${ALLSKY_ERROR_STOP}" +#shellcheck source-path=scripts +source "${ALLSKY_SCRIPTS}/installUpgradeFunctions.sh" || exit "${ALLSKY_ERROR_STOP}" usage_and_exit() { @@ -28,12 +28,13 @@ usage_and_exit() # Don't show the "--newer", "--no-check", or "--force-check" options since users # should never use them. echo - echo -e "${C}Usage: ${ME} [--help] [--debug] [--no-check]${NC}" + echo -e "${C}Usage: ${ME} [--help] [--debug] [--no-check] [--no-info]${NC}" echo echo "'--help' displays this message and exits." + echo "'--no-check' skips checking for newer version, typically used for testing." + echo "'--no-info' skips checking for Informational items." echo - # shellcheck disable=SC2086 - exit ${RET} + exit "${RET}" } # Check arguments @@ -42,6 +43,9 @@ HELP="false" DEBUG="false" NEWER="" FORCE_CHECK="true" +CHECK_INFORMATIONAL="true" +CHECK_WARNINGS="true" +CHECK_ERRORS="true" while [[ $# -gt 0 ]]; do ARG="${1}" case "${ARG}" in @@ -51,15 +55,24 @@ while [[ $# -gt 0 ]]; do --debug) DEBUG="true" ;; - --newer) - NEWER="true" - ;; --no-check) FORCE_CHECK="false" ;; + --no-info) + CHECK_INFORMATIONAL="false" + ;; + --no-warn) + CHECK_WARNINGS="false" + ;; + --no-error) + CHECK_ERRORS="false" + ;; --force-check) FORCE_CHECK="true" ;; + --newer) # This is passed in from the newer version + NEWER="true" + ;; *) display_msg error "Unknown argument: '${ARG}'." OK="false" @@ -70,12 +83,6 @@ done [[ ${HELP} == "true" ]] && usage_and_exit 0 [[ ${OK} == "false" ]] && usage_and_exit 1 -#shellcheck disable=SC2086,SC1091 # file doesn't exist in GitHub -source "${ALLSKY_CONFIG}/config.sh" || exit ${ALLSKY_ERROR_STOP} -#shellcheck disable=SC2086,SC1091 # file doesn't exist in GitHub -source "${ALLSKY_CONFIG}/ftp-settings.sh" || exit ${ALLSKY_ERROR_STOP} -PROTOCOL="${PROTOCOL,,}" # set to lowercase to make comparing easier - BRANCH="$( get_branch "" )" [[ -z ${BRANCH} ]] && BRANCH="${GITHUB_MAIN_BRANCH}" [[ ${DEBUG} == "true" ]] && echo "DEBUG: using '${BRANCH}' branch." @@ -83,7 +90,7 @@ BRANCH="$( get_branch "" )" # Unless forced to, only do the version check if we're on the main branch, # not on development branches, because when we're updating this script we # don't want to have the updates overwritten from an older version on GitHub. -if [[ ${FORCE_CHECK} == "true" || ${BRANCH} == "${GITHUB_MAIN_BRANCH}" ]]; then +if [[ ${FORCE_CHECK} == "true" ]]; then CURRENT_SCRIPT="${ALLSKY_SCRIPTS}/${ME}" if [[ -n ${NEWER} ]]; then # This is a newer version @@ -93,7 +100,7 @@ if [[ ${FORCE_CHECK} == "true" || ${BRANCH} == "${GITHUB_MAIN_BRANCH}" ]]; then else # See if there's a newer version of this script; if so, download it and execute it. - FILE_TO_CHECK="$(basename "${ALLSKY_SCRIPTS}")/${ME}" + FILE_TO_CHECK="$( basename "${ALLSKY_SCRIPTS}" )/${ME}" NEWER_SCRIPT="/tmp/${ME}" checkAndGetNewerFile --branch "${BRANCH}" "${CURRENT_SCRIPT}" "${FILE_TO_CHECK}" "${NEWER_SCRIPT}" RET=$? @@ -116,21 +123,21 @@ function heading() local DISPLAY_HEADER="false" case "${HEADER}" in Information) - NUM_INFOS=$((NUM_INFOS + 1)) + ((NUM_INFOS++)) if [[ $NUM_INFOS -eq 1 ]]; then DISPLAY_HEADER="true" SUB_HEADER=" (items that will not stop any part of Allsky from running)" fi ;; Warnings) - NUM_WARNINGS=$((NUM_WARNINGS + 1)) + ((NUM_WARNINGS++)) if [[ $NUM_WARNINGS -eq 1 ]]; then DISPLAY_HEADER="true" SUB_HEADER=" (items that may keep parts of Allsky running)" fi ;; Errors) - NUM_ERRORS=$((NUM_ERRORS + 1)) + ((NUM_ERRORS++)) if [[ $NUM_ERRORS -eq 1 ]]; then DISPLAY_HEADER="true" SUB_HEADER=" (items that may keep Allsky from running)" @@ -151,6 +158,9 @@ function heading() fi } + +# =================================================== FUNCTIONS + # Determine if the specified value is a number. function is_number() { @@ -168,7 +178,8 @@ function is_number() } # Return the min of two numbers. -function min() { +function min() +{ local ONE="${1}" local TWO="${2}" if [[ ${ONE} -lt ${TWO} ]]; then @@ -178,23 +189,6 @@ function min() { fi } -# =================================================== CHECKING FUNCTIONS - -# The various upload protocols need different variables defined. -# For the specified protocol, make sure the specified variable is defined. -function check_PROTOCOL() -{ - P="${1}" # Protocol - V="${2}" # Variable - if [[ -z ${!V} ]]; then - heading "Warnings" - echo "PROTOCOL (${P}) set but not '${V}'." - echo "Uploads will not work." - return 1 - fi - return 0 -} - # Check that when a variable holds a location, the location exists. function check_exists() { local VALUE="${!1}" @@ -207,249 +201,354 @@ function check_exists() { fi } +# Make sure the env file exists. +function check_for_env_file() +{ + [[ -s ${ALLSKY_ENV} ]] && return 0 + heading "Errors" + if [[ ! -f ${ALLSKY_ENV} ]]; then + echo "'${ALLSKY_ENV}' not found!" + else + echo "'${ALLSKY_ENV}' is empty!" + fi + echo "Unable to check any remote server settings." + return 1 +} -DAYDELAY_MS=$(settings .daydelay) || echo "Problem getting .daydelay" -NIGHTDELAY_MS=$(settings .nightdelay) || echo "Problem getting .nightdelay" +DAY_DELAY_MS=$( settings ".daydelay" ) || echo "Problem getting .daydelay" +NIGHT_DELAY_MS=$( settings ".nightdelay" ) || echo "Problem getting .nightdelay" - # Use min() for worst case. -MIN_DELAY_MS=$( min "${DAYDELAY_MS}" "${NIGHTDELAY_MS}" ) - # This is typically the max daytime exposure, which is shorter than nighttime so use it. -MIN_EXPOSURE_MS=250 - # Minimum total time spent on each image -MIN_IMAGE_TIME_MS=$((MIN_EXPOSURE_MS + MIN_DELAY_MS)) +# Typical minimum daytime and nighttime exposures. +DAY_MIN_EXPOSURE_MS=250 +NIGHT_MIN_EXPOSURE_MS=5000 +# Minimum total time spent on each image. +DAY_MIN_IMAGE_TIME_MS=$(( DAY_MIN_EXPOSURE_MS + DAY_DELAY_MS )) +NIGHT_MIN_IMAGE_TIME_MS=$(( NIGHT_MIN_EXPOSURE_MS + NIGHT_DELAY_MS )) +MIN_IMAGE_TIME_MS="$( min "${DAY_MIN_IMAGE_TIME_MS}" "${NIGHT_MIN_IMAGE_TIME_MS}" )" ##### Check if the delay is so short it's likely to cause problems. function check_delay() { -# TODO: use the module average flow times for day and night + local DAY_OR_NIGHT="${1}" + local DELAY_MS MIN_MS OVERLAY_METHOD + + if [[ ${DAY_OR_NIGHT} == "daytime" ]]; then + DELAY_MS="${DAY_DELAY_MS}" + MIN_MS="${DAY_MIN_IMAGE_TIME_MS}" + else + DELAY_MS="${NIGHT_DELAY_MS}" + MIN_MS="${NIGHT_MIN_IMAGE_TIME_MS}" + fi + +# TODO: use the module average flow times for day and night when using "module" method. # With the legacy overlay method it might take up to a couple seconds to save an image. # With the module method it can take up to 5 seconds. - local OVERLAY_METHOD=$(settings .overlayMethod) || echo "Problem getting .overlayMethod." >&2 + OVERLAY_METHOD=$( settings ".overlaymethod" ) || echo "Problem getting .overlayMethod." >&2 if [[ ${OVERLAY_METHOD} -eq 1 ]]; then - MAX_TIME_TO_SAVE_MS=5000 + MAX_TIME_TO_PROCESS_MS=5000 else - MAX_TIME_TO_SAVE_MS=2000 + MAX_TIME_TO_PROCESS_MS=2000 fi - if [[ ${MAX_TIME_TO_SAVE_MS} -gt ${MIN_IMAGE_TIME_MS} ]]; then + if [[ ${MIN_MS} -lt ${MAX_TIME_TO_PROCESS_MS} ]]; then heading "Warnings" - echo "The minimum delay of ${MIN_DELAY_MS} may be too short" - echo "given the maximum expected time to save and process" - echo "an image (${MAX_TIME_TO_SAVE_MS} ms)." + echo "The ${DAY_OR_NIGHT}delay of ${DELAY_MS} ms may be too short given the maximum" + echo "expected time to save and process an image (${MAX_TIME_TO_PROCESS_MS} ms)." echo "A new image may appear before the prior one has finished processing." echo "Consider increasing your delay." fi } +function get_setting() +{ + local S="${1}" + local V="$( settings "${S}" )" || echo "Problem getting ${S}." >&2 + echo "${V}" +} + # # ====================================================== MAIN PART OF PROGRAM # -# Variables used below. -TAKING_DARKS="$(settings .takeDarkFrames)" || echo "Problem getting .takeDarkFrames." >&2 -# per the WebUI, width and height are usually 0 -WIDTH="$(settings .width)" || echo "Problem getting .width." >&2 -HEIGHT="$(settings .height)" || echo "Problem getting .height." >&2 -# physical sensor size -SENSOR_WIDTH="$(settings .sensorWidth "${CC_FILE}")" || echo "Problem getting .sensorWidth." >&2 -SENSOR_HEIGHT="$(settings .sensorHeight "${CC_FILE}")" || echo "Problem getting .sensorHeight." >&2 -TAKE="$(settings .takeDaytimeImages)" || echo "Problem getting .takeDaytimeImages." >&2 -SAVE="$(settings .saveDaytimeImages)" || echo "Problem getting .saveDaytimeImages." >&2 -ANGLE="$(settings .angle)" || echo "Problem getting .angle" >&2 -LATITUDE="$(settings .latitude)" || echo "Problem getting .latitude." >&2 -LONGITUDE="$(settings .longitude)" || echo "Problem getting .longitude" >&2 -# shellcheck disable=SC2034 -LOCALE="$(settings .locale)" || echo "Problem getting .locale" >&2 -USING_DARKS="$(settings .useDarkFrames)" || echo "Problem getting .useDarkFrames" >&2 -WEBSITES="$(whatWebsites)" +# Settings used in multiple sections. +# For the most part we use the names that used to be in config.sh since we're familiar with them. + +# User-specified width and height are usually 0 which means use SENSOR size. +WIDTH="$( get_setting ".width" )" +HEIGHT="$( get_setting ".height" )" + +# Physical sensor size. +SENSOR_WIDTH="$( settings ".sensorWidth" "${CC_FILE}" )" || echo "Problem getting .sensorWidth." >&2 +SENSOR_HEIGHT="$( settings ".sensorHeight" "${CC_FILE}" )" || echo "Problem getting .sensorHeight." >&2 + +IMG_RESIZE_WIDTH="$( get_setting ".imageresizewidth" )" +IMG_RESIZE_HEIGHT="$( get_setting ".imageresizeheight" )" +CROP_TOP="$( get_setting ".imagecroptop" )" +CROP_RIGHT="$( get_setting ".imagecropright" )" +CROP_BOTTOM="$( get_setting ".imagecropbottom" )" +CROP_LEFT="$( get_setting ".imagecropleft" )" +ANGLE="$( get_setting ".angle" )" +LATITUDE="$( get_setting ".latitude" )" +LONGITUDE="$( get_setting ".longitude" )" +UPLOAD_VIDEO="$( get_setting ".timelapseupload" )" +TIMELAPSE_UPLOAD_THUMBNAIL="$( get_setting ".timelapseuploadthumbnail" )" +TIMELAPSE_MINI_UPLOAD_VIDEO="$( get_setting ".minitimelapseupload" )" +TIMELAPSE_MINI_UPLOAD_THUMBNAIL="$( get_setting ".minitimelapseuploadthumbnail" )" +KEEP_SEQUENCE="$( get_setting ".timelapsekeepsequence" )" +KEOGRAM="$( get_setting ".keogramgenerate" )" +UPLOAD_KEOGRAM="$( get_setting ".keogramupload" )" +STARTRAILS="$( get_setting ".startrailsgenerate" )" +UPLOAD_STARTRAILS="$( get_setting ".startrailsupload" )" +TAKE="$( get_setting ".takedaytimeimages" )" +SAVE="$( get_setting ".savedaytimeimages" )" +BRIGHTNESS_THRESHOLD="$( get_setting ".startrailsbrightnessthreshold" )" # ====================================================================== # ================= Check for informational items. -# There is nothing wrong with these, it's just that they typically don't exist. -# Is Allsky set up to take dark frames? This isn't done often, so if it is, inform the user. -if [[ ${TAKING_DARKS} -eq 1 ]]; then - heading "Information" - echo "'Take Dark Frames' is set." - echo "Unset when you are done taking dark frames." -fi +if [[ ${CHECK_INFORMATIONAL} == "true" ]]; then + + # Settings used in this section. + WEBSITES="$( whatWebsites )" + # shellcheck disable=SC2034 + TAKING_DARKS="$( get_setting ".takedarkframes" )" + THUMBNAIL_SIZE_X="$( get_setting ".thumbnailsizex" )" + THUMBNAIL_SIZE_Y="$( get_setting ".thumbnailsizey" )" + DAYS_TO_KEEP="$( get_setting ".daystokeep" )" + LOCAL_WEB_DAYS_TO_KEEP="$( get_setting ".daystokeeplocalwebsite" )" + REMOTE_WEB_DAYS_TO_KEEP="$( get_setting ".daystokeepremotewebsite" )" + KEOGRAM_EXTRA_PARAMETERS="$( get_setting ".keogramextraparameters" )" + + # Is Allsky set up to take dark frames? This isn't done often, so if it is, inform the user. + if [[ ${TAKING_DARKS} == "true" ]]; then + heading "Information" + echo "'Take Dark Frames' is set." + echo -e "\tUnset when you are done taking dark frames." + fi -if [[ ${KEEP_SEQUENCE} == "true" ]]; then - heading "Information" - echo "KEEP_SEQUENCE in config.sh is 'true'." - echo "If you are not testing / debugging timelapse videos consider changing this to 'false'" - echo "to save disk space." -fi + if [[ ${KEEP_SEQUENCE} == "true" ]]; then + heading "Information" + echo "'Keep Timelapse Sequence' in enabled." + echo -e "\tIf you are not testing / debugging timelapse videos consider disabling this" + echo -e "\tto save disk space." + fi -if [[ ${THUMBNAIL_SIZE_X} -ne 100 || ${THUMBNAIL_SIZE_Y} -ne 75 ]]; then - heading "Information" - echo -n "You are using a non-standard thumbnail size" - echo " (${THUMBNAIL_SIZE_X} x ${THUMBNAIL_SIZE_Y}) in config.sh." - echo -e "\tPlease note non-standard sizes have not been thoroughly tested and" - echo -e "\tyou will likely need to modify some code to get them working." -fi + if [[ ${THUMBNAIL_SIZE_X} -ne 100 || ${THUMBNAIL_SIZE_Y} -ne 75 ]]; then + heading "Information" + echo -n "You are using a non-standard thumbnail size (${THUMBNAIL_SIZE_X} x ${THUMBNAIL_SIZE_Y})." + echo -e "\tPlease note non-standard sizes have not been thoroughly tested and" + echo -e "\tyou will likely need to modify some code to get them working." + fi -DAYS_TO_KEEP=${DAYS_TO_KEEP:-0} # old versions allowed "" to disable -if [[ ${DAYS_TO_KEEP} -eq 0 ]]; then - heading "Information" - echo "DAYS_TO_KEEP is 0 which means images and videos will be kept forever" - echo -e "\tor until you manually delete them." -fi + if [[ ${DAYS_TO_KEEP} -eq 0 ]]; then + heading "Information" + echo "'Days To Keep' is 0 which means images and videos will" + echo -e "\tbe kept forever or until you manually delete them." + fi -WEB_DAYS_TO_KEEP=${WEB_DAYS_TO_KEEP:-0} # old versions allowed "" to disable -if [[ ${WEB_DAYS_TO_KEEP} -eq 0 ]]; then - if [[ ${WEBSITES} == "both" || ${WEBSITES} == "remote" ]]; then + if [[ (${WEBSITES} == "both" || ${WEBSITES} == "local") && + ${LOCAL_WEB_DAYS_TO_KEEP} -eq 0 ]]; then + heading "Information" + echo "'Days To Keep on Pi Website' is 0 which means local web images and videos will" + echo -e "\tbe kept forever or until you manually delete them." + fi + if [[ (${WEBSITES} == "both" || ${WEBSITES} == "remote") && + ${REMOTE_WEB_DAYS_TO_KEEP} -eq 0 ]]; then heading "Information" - echo "WEB_DAYS_TO_KEEP is 0 which means local web images and videos will be kept forever" - echo -e "\tor until you manually delete them." + echo "'Days To Keep on Remote Website' is 0 which means remote web images and videos will" + echo -e "\tbe kept forever or until you manually delete them." fi -else # -gt 0 - if [[ ${WEBSITES} == "none" || ${WEBSITES} == "remote" ]]; then + + if [[ ${IMG_RESIZE_WIDTH} -gt 0 && ${IMG_RESIZE_HEIGHT} -eq 0 ]]; then heading "Information" - echo "WEB_DAYS_TO_KEEP is set to ${WEB_DAYS_TO_KEEP} but there is no local Website." - echo -e "\tSet 'WEB_DAYS_TO_KEEP=0' in config.sh to keep this message from appearing." - if [[ ${WEBSITES} == "remote" ]]; then - echo -e "\tWEB_DAYS_TO_KEEP only works with LOCAL websites, not REMOTE." + echo "'Image Resize Width' set to ${IMG_RESIZE_WIDTH} but 'Image Resize Height' is 0." + echo -e "\tThe image will NOT be resized." + elif [[ ${IMG_RESIZE_WIDTH} -eq 0 && ${IMG_RESIZE_HEIGHT} -gt 0 ]]; then + heading "Information" + echo "'Image Resize Width' is 0 but 'Image Resize Height' is ${IMG_RESIZE_HEIGHT}." + echo -e "\tThe image will NOT be resized." + elif [[ ${IMG_RESIZE_WIDTH} -gt 0 && ${IMG_RESIZE_HEIGHT} -gt 0 ]]; then + if [[ ${SENSOR_WIDTH} == "${IMG_RESIZE_WIDTH}" && ${SENSOR_HEIGHT} == "${IMG_RESIZE_HEIGHT}" ]]; then + heading "Information" + echo "Images will be resized to the same size as the sensor; this does nothing useful." + echo -e "\tCheck 'Image Reize Width' (${IMG_RESIZE_WIDTH}) and" + echo -e "\t'Image Reize Height' (${IMG_RESIZE_HEIGHT})." fi fi -fi -if [[ ${IMG_RESIZE} == "true" && ${SENSOR_WIDTH} == "${IMG_WIDTH}" && ${SENSOR_HEIGHT} == "${IMG_HEIGHT}" ]]; then - heading "Information" - echo "Images will be resized to the same size as the sensor; this does nothing useful." - echo "Check IMG_RESIZE, IMG_WIDTH (${IMG_WIDTH}), and IMG_HEIGHT (${IMG_HEIGHT})." -fi -#shellcheck disable=SC2153 # it thinks CROP_HEIGHT may be misspelled -if [[ ${CROP_IMAGE} == "true" && ${SENSOR_WIDTH} == "${CROP_WIDTH}" && ${SENSOR_HEIGHT} == "${CROP_HEIGHT}" ]]; then - heading "Information" - echo "Images will be cropped to the same size as the sensor; this does nothing useful." - echo "Check CROP_IMAGE, CROP_WIDTH (${CROP_WIDTH}), and CROP_HEIGHT (${CROP_HEIGHT})." -fi + if [[ ${CROP_TOP} -gt 0 || ${CROP_RIGHT} -gt 0 || ${CROP_BOTTOM} -gt 0 || ${CROP_LEFT} -gt 0 ]]; then + ERR="$( checkCropValues "${CROP_TOP}" "${CROP_RIGHT}" "${CROP_BOTTOM}" "${CROP_LEFT}" \ + "${SENSOR_WIDTH}" "${SENSOR_HEIGHT}" )" + if [[ $? -ne 0 ]]; then + heading "Information" + echo "${ERR}" + echo -e "\tCheck the 'Image Crop Top/Right/Bottom/Left' settings." + fi + fi + + if [[ -n ${KEOGRAM_EXTRA_PARAMETERS} ]]; then + FOUND="false" + # These used to be set in the default KEOGRAM_EXTRA_PARAMETERS. + echo "${KEOGRAM_EXTRA_PARAMETERS}" | + grep -E --silent "image-expand|-x|font-size|-S|font-line|-L|font-color|-C" && FOUND="true" + if [[ ${FOUND} == "true" ]]; then + heading "Information" + echo "Check your 'Keogram Extra Parameters' setting:" + echo -e "\t--image-expand" + echo -e "\t--font-size" + echo -e "\t--font-line" + echo -e "\t--font-color" + echo -e "are separate settings now." + fi + fi +fi # end of checking for informational items -LAST_CHANGED="$( settings ".lastChanged" )" || echo "Problem getting .lastChanged" >&2 -if [[ ${LAST_CHANGED} == "" || ${LAST_CHANGED} == "null" ]]; then - heading "Information" - echo "Allsky needs to be configured before it will run." - echo "See the 'Allsky Settings' page in the WebUI." -fi -if reboot_needed ; then - heading "Information" - echo "The Pi needs to be rebooted before Allsky will start." -fi # ====================================================================== # ================= Check for warning items. # These are wrong and won't stop Allsky from running, but # may break part of Allsky, e.g., uploads may not work. -##### Check if the delay is so short it's likely to cause problems. -check_delay +if [[ ${CHECK_WARNINGS} == "true" ]]; then + + # Settings used in this section. + LAST_CHANGED="$( get_setting ".lastchanged" )" + REMOVE_BAD_IMAGES_LOW="$( get_setting ".imageremovebadlow" )" + REMOVE_BAD_IMAGES_HIGH="$( get_setting ".imageremovebadhigh" )" + TIMELAPSE="$( get_setting ".timelapsegenerate" )" + TIMELAPSEWIDTH="$( get_setting ".timelapsewidth" )" + TIMELAPSEHEIGHT="$( get_setting ".timelapseheight" )" + VCODEC="$( get_setting ".timelapsevcodec" )" + TIMELAPSE_BITRATE="$( get_setting ".timelapsebitrate" )" + TIMELAPSE_MINI_IMAGES="$( get_setting ".minitimelapsenumimages" )" + TIMELAPSE_MINI_WIDTH="$( get_setting ".minitimelapsewidth" )" + TIMELAPSE_MINI_HEIGHT="$( get_setting ".minitimelapseheight" )" + TIMELAPSE_MINI_BITRATE="$( get_setting ".minitimelapsebitrate" )" + TIMELAPSE_MINI_FREQUENCY="$( get_setting ".minitimelapsefrequency" )" + RESIZE_UPLOADS_WIDTH="$( get_setting ".imageresizeuploadswidth" )" + RESIZE_UPLOADS_HEIGHT="$( get_setting ".imageresizeuploadsheight" )" + IMG_UPLOAD_FREQUENCY="$( get_setting ".imageuploadfrequency" )" + + if [[ ${LAST_CHANGED} == "" ]]; then + heading "Warning" + echo "Allsky needs to be configured before it will run." + echo -e "\tSee the 'Allsky Settings' page in the WebUI." + fi + + if reboot_needed ; then + heading "Warning" + echo "The Pi needs to be rebooted before Allsky will start." + fi + + check_delay "daytime" + check_delay "nighttime" + + + ##### Timelapse and mini-timelapse + if [[ ${VCODEC} == "libx264" ]]; then + # Check if timelapse size is "too big" and will likely cause an error. + # This is normally only an issue with the libx264 video codec which has + # a dimension limit that we put in PIXEL_LIMIT. + PIXEL_LIMIT=$((4096 * 2304)) # Limit of libx264 +# TODO: This PIXEL_LIMIT might only apply to Buster. Bullseye (I believe) has a larger limit. + + function check_timelapse_size() + { + local TYPE="${1}" # type of video + local RESIZED_WIDTH="${2}" # video width + local RESIZED_HEIGHT="${3}" + local W H + + # Determine the final image size and put in ${W} and ${H}. + # This is dependent on the these, in this order: + # if the images is resized, use that size + # else if the size is set in the WebUI (WIDTH, HEIGHT), use that size + # else use sensor size minus crop amount(s) + if [[ ${RESIZED_WIDTH} -ne 0 ]]; then + W="${RESIZED_WIDTH}" + elif [[ ${WIDTH} -gt 0 ]]; then + W="${WIDTH}" + else + W=$(( SENSOR_WIDTH - CROP_LEFT - CROP_RIGHT)) + fi + if [[ ${RESIZED_HEIGHT} -ne 0 ]]; then + H="${RESIZED_HEIGHT}" + elif [[ ${HEIGHT} -gt 0 ]]; then + H="${HEIGHT}" + else + H=$(( SENSOR_HEIGHT - CROP_TOP - CROP_BOTTOM)) + fi + local TIMELAPSE_PIXELS=$(( W * H )) + if [[ ${TIMELAPSE_PIXELS} -gt ${PIXEL_LIMIT} ]]; then + heading "Warnings" + echo "The ${TYPE} width (${W}) and height (${H}) may cause errors while creating the video." + echo "Consider either decreasing the video size or decreasing" + echo "each captured image via resizing and/or cropping." + fi + } -##### Check if timelapse size is "too big" and will likely cause an error. -# This is normally only an issue with the libx264 video codec which has a dimension limit -# that we put in PIXEL_LIMIT -if [[ ${VCODEC} == "libx264" ]]; then - PIXEL_LIMIT=$((4096 * 2304)) - function check_timelapse_size() - { - local TYPE="${1}" # type of video - local V_WIDTH="${2}" # video width - local W_WIDTH="${3}" # width per the WebUI, adjusted for if it's 0 - local V_HEIGHT="${4}" - local W_HEIGHT="${5}" - - if [[ ${V_WIDTH} -eq 0 ]]; then - W="${W_WIDTH}" - else - W="${V_WIDTH}" - fi - if [[ ${V_HEIGHT} -eq 0 ]]; then - H="${W_HEIGHT}" - else - H="${V_HEIGHT}" + fi + + # Timelapse + if [[ ${TIMELAPSE} == "true" ]]; then + if [[ ${VCODEC} == "libx264" ]]; then + check_timelapse_size "timelapse" "${TIMELAPSEWIDTH}" "${TIMELAPSEHEIGHT}" fi - TIMELAPSE_PIXELS=$(( W * H )) - if [[ ${TIMELAPSE_PIXELS} -gt ${PIXEL_LIMIT} ]]; then + if [[ ${UPLOAD_VIDEO} == "false" ]]; then heading "Warnings" - echo "The ${TYPE} width (${W}) and height (${H}) may cause errors while creating the video." - echo "Consider either decreasing the video size via TIMELAPSEWIDTH and TIMELAPSEHEIGHT" - echo "or decrease each captured image via the WebUI and/or IMG_RESIZE and/or CROP_IMAGE." + echo "Timelapse videos are being created (Generate Timelapse='true') but not uploaded (Upload Timelapse='false')" fi - } - - # Determine the final image size. - # This is dependent on the these, in this order: - # if: CROP_IMAGE=true (CROP_WIDTH, CROP_HEIGHT) use it. - # else if: IMG_RESIZE=true (IMG_WIDTH, IMG_HEIGHT), use it - # else if: size set in WebUI (width, height), use it - # else use sensor size - - if [[ ${CROP_IMAGE} == "true" ]]; then - W="${CROP_WIDTH}" - H="${CROP_HEIGHT}" - elif [[ ${IMG_RESIZE} == "true" ]]; then - W="${IMG_WIDTH}" - H="${IMG_HEIGHT}" - else - if [[ ${WIDTH} -gt 0 ]]; then - W="${WIDTH}" - else - W="${SENSOR_WIDTH}" - fi - if [[ ${HEIGHT} -gt 0 ]]; then - H="${HEIGHT}" - else - H="${SENSOR_HEIGHT}" + if echo "${TIMELAPSE_BITRATE}" | grep -i --silent "k" ; then + heading "Warnings" + echo "Timelapse bitrate should be only a number and no longer have 'k'." fi + elif [[ ${UPLOAD_VIDEO} == "true" ]]; then + heading "Warnings" + echo "Timelapse videos are not being created (Generate Timelapse='false') but Upload Timelapse='true'" fi - if [[ ${TIMELAPSE} == "true" ]]; then - check_timelapse_size "timelapse" "${TIMELAPSEWIDTH}" "${W}" "${TIMELAPSEHEIGHT}" "${H}" - fi + # Mini-timelapse if [[ ${TIMELAPSE_MINI_IMAGES} -gt 0 ]]; then - check_timelapse_size "mini timelapse" "${TIMELAPSE_MINI_WIDTH}" "${W}" "${TIMELAPSE_MINI_HEIGHT}" "${H}" - fi -fi - -##### Timelapse and mini timelapse -if [[ ${TIMELAPSE} == "true" && ${UPLOAD_VIDEO} == "false" ]]; then - heading "Warnings" - echo "Timelapse videos are being created (TIMELAPSE='true') but not uploaded (UPLOAD_VIDEO='false')" -fi -if [[ ${TIMELAPSE} == "false" && ${UPLOAD_VIDEO} == "true" ]]; then - heading "Warnings" - echo "Timelapse videos are not being created (TIMELAPSE='false') but UPLOAD_VIDEO='true'" -fi - - -if [[ ${TIMELAPSE_MINI_IMAGES} -gt 0 ]]; then - - # See if there's likely to be a problem with mini timelapse creations - # starting before the prior one finishes. - # This is dependent on: - # 1. Delay: the delay between images: min(daytime_delay, nighttime_delay) - # 2. Frequency: how often mini timelapse are created (i.e., after how many images) - # 3. NumImages: how many images are used (the more the longer processing takes) - # 4. the speed of the Pi - this is the biggest unknown - function get_exposure() { # return the time spent on one image, prior to delay - local TIME="${1}" - if [[ $(settings ".${TIME}autoexposure") -eq 1 ]]; then - settings ".${TIME}maxautoexposure" || echo "Problem getting .${TIME}maxautoexposure." >&2 - else - settings ".${TIME}exposure" || echo "Problem getting .${TIME}exposure." >&2 + if [[ ${VCODEC} == "libx264" ]]; then + check_timelapse_size "mini timelapse" "${TIMELAPSE_MINI_WIDTH}" "${TIMELAPSE_MINI_HEIGHT}" + fi + if [[ ${TIMELAPSE_MINI_UPLOAD_VIDEO} == "false" ]]; then + heading "Warnings" + echo "Mini timelapse videos are being created (TIMELAPSE='true') but not uploaded (Upload Timelapse='false')" + fi + if echo "${TIMELAPSE_MINI_BITRATE}" | grep -i --silent "k" ; then + heading "Warnings" + echo "Timelapse bitrate should be only a number and no longer have 'k'." fi - } - # Minimum total time between start of timelapse creations. - MIN_IMAGE_TIME_SEC=$(( MIN_IMAGE_TIME_MS / 1000)) - MIN_TIME_BETWEEN_TIMELAPSE_SEC=$(echo "scale=0; ${TIMELAPSE_MINI_FREQUENCY} * ${MIN_IMAGE_TIME_SEC}" | bc -l) - MIN_TIME_BETWEEN_TIMELAPSE_SEC=${MIN_TIME_BETWEEN_TIMELAPSE_SEC/.*/} + + # See if there's likely to be a problem with mini timelapse creations + # starting before the prior one finishes. + # This is dependent on: + # 1. Delay: the delay between images: min(daydelay, nightdelay) + # 2. Frequency: how often mini timelapse are created (i.e., after how many images) + # 3. NumImages: how many images are used (the more the longer processing takes) + # 4. the speed of the Pi - this is the biggest unknown + + function get_exposure() { # return the time spent on one image, prior to delay + local TIME="${1}" + if [[ $( settings ".${TIME}autoexposure") -eq 1 ]]; then + settings ".${TIME}maxautoexposure" || echo "Problem getting .${TIME}maxautoexposure." >&2 + else + settings ".${TIME}exposure" || echo "Problem getting .${TIME}exposure." >&2 + fi + } + # Minimum total time between start of timelapse creations. + MIN_IMAGE_TIME_SEC=$(( MIN_IMAGE_TIME_MS / 1000 )) + MIN_TIME_BETWEEN_TIMELAPSE_SEC=$( echo "scale=0; ${TIMELAPSE_MINI_FREQUENCY} * ${MIN_IMAGE_TIME_SEC}" | bc -l) + MIN_TIME_BETWEEN_TIMELAPSE_SEC=${MIN_TIME_BETWEEN_TIMELAPSE_SEC/.*/} if false; then # for testing echo "CONSISTENT_DELAYS=${CONSISTENT_DELAYS}" + echo "MIN_IMAGE_TIME_MS=${MIN_IMAGE_TIME_MS}" echo "MIN_IMAGE_TIME_SEC=${MIN_IMAGE_TIME_SEC}" echo "MIN_TIME_BETWEEN_TIMELAPSE_SEC=${MIN_TIME_BETWEEN_TIMELAPSE_SEC}" echo "TIMELAPSE_MINI_IMAGES=${TIMELAPSE_MINI_IMAGES}" @@ -457,136 +556,125 @@ if false; then # for testing TIMELAPSE_MINI_IMAGES=120 fi - # On a Pi 4, creating a 50 image timelapse takes - # - a few seconds on a small ZWO camera - # - about a minute with an RPi HQ + # On a Pi 4, creating a 50 image timelapse takes + # - a few seconds on a small ZWO camera + # - about a minute with an RPi HQ camera - if [[ ${CAMERA_TYPE} == "ZWO" ]]; then - S=3 - else - S=60 - fi - EXPECTED_TIME=$(echo "scale=0; (${TIMELAPSE_MINI_IMAGES} / 50) * ${S}" | bc -l) - if [[ ${EXPECTED_TIME} -gt ${MIN_TIME_BETWEEN_TIMELAPSE_SEC} ]]; then + if [[ ${CAMERA_TYPE} == "ZWO" ]]; then + S=3 + else + S=60 + fi + EXPECTED_TIME=$( echo "scale=0; (${TIMELAPSE_MINI_IMAGES} / 50) * ${S}" | bc -l) + if [[ ${EXPECTED_TIME} -gt ${MIN_TIME_BETWEEN_TIMELAPSE_SEC} ]]; then + heading "Warnings" + echo "Your mini timelapse settings may cause multiple timelapse to be created simultaneously." + echo "Consider increasing the Delay between pictures," + echo "increasing Mini-Timelapse Frequency," + echo "decreasing Number Of Images," + echo "or a combination of those changes." + echo "Expected time to create a mini timelapse on a Pi 4 is ${EXPECTED_TIME} seconds" + echo "but with your settings one could be created as short as" + echo "every ${MIN_TIME_BETWEEN_TIMELAPSE_SEC} seconds." + fi + elif [[ ${TIMELAPSE_MINI_UPLOAD_VIDEO} == "true" ]]; then heading "Warnings" - echo "Your mini timelapse settings may cause multiple timelapse to be created simultaneously." - echo "Consider increasing DELAY between pictures, increasing TIMELAPSE_MINI_FREQUENCY," - echo "decrease TIMELAPSE_MINI_IMAGES, or a combination of those changes." - echo "Expected time to create a mini timelapse on a Pi 4 is ${EXPECTED_TIME} seconds" - echo "but with your settings one will be created as short as every ${MIN_TIME_BETWEEN_TIMELAPSE_SEC} seconds." + echo "Mini timelapse videos are not being created (Number of Images=0) but Upload Mini-Timelapse='true'" fi -fi - -##### Keograms -if [[ ${KEOGRAM} == "true" && ${UPLOAD_KEOGRAM} == "false" ]]; then - heading "Warnings" - echo "Keograms are being created (KEOGRAM='true') but not uploaded (UPLOAD_KEOGRAM='false')" -fi -if [[ ${KEOGRAM} == "false" && ${UPLOAD_KEOGRAM} == "true" ]]; then - heading "Warnings" - echo "Keograms are not being created (KEOGRAM='false') but UPLOAD_KEOGRAM='true'" -fi - -##### Startrails -if [[ ${STARTRAILS} == "true" && ${UPLOAD_STARTRAILS} == "false" ]]; then - heading "Warnings" - echo "Startrails are being created (STARTRAILS='true') but not uploaded (UPLOAD_STARTRAILS='false')" -fi -if [[ ${STARTRAILS} == "false" && ${UPLOAD_STARTRAILS} == "true" ]]; then - heading "Warnings" - echo "Startrails are not being created (STARTRAILS='false') but UPLOAD_STARTRAILS='true'" -fi - -if [[ ${BRIGHTNESS_THRESHOLD} == "0.0" ]]; then - heading "Warnings" - echo "BRIGHTNESS_THRESHOLD is 0.0 which means ALL images will be IGNORED when creating startrails." -elif [[ ${BRIGHTNESS_THRESHOLD} == "1.0" ]]; then - heading "Warnings" - echo "BRIGHTNESS_THRESHOLD is 1.0 which means ALL images will be USED when creating startrails, even daytime images." -fi -##### Images + ##### Keograms + if [[ ${KEOGRAM} == "true" && ${UPLOAD_KEOGRAM} == "false" ]]; then + heading "Warnings" + echo "Keograms are being created (KEOGRAM='true') but not uploaded (UPLOAD_KEOGRAM='false' )" + fi + if [[ ${KEOGRAM} == "false" && ${UPLOAD_KEOGRAM} == "true" ]]; then + heading "Warnings" + echo "Keograms are not being created (KEOGRAM='false') but UPLOAD_KEOGRAM='true'" + fi -if [[ ${TAKE} -eq 0 && ${SAVE} -eq 1 ]]; then - heading "Warnings" - echo "'Daytime Capture' is off but 'Daytime Save' is on in the WebUI." -fi + ##### Startrails + if [[ ${STARTRAILS} == "true" && ${UPLOAD_STARTRAILS} == "false" ]]; then + heading "Warnings" + echo "Startrails are being created (Generate Startrails='true') but not uploaded (Upload Startrails='false' )" + fi + if [[ ${STARTRAILS} == "false" && ${UPLOAD_STARTRAILS} == "true" ]]; then + heading "Warnings" + echo "Startrails are not being created (Generate Startrails='false') but Upload Startrails='true'" + fi -if [[ ${REMOVE_BAD_IMAGES} != "true" ]]; then - heading "Warnings" - echo "REMOVE_BAD_IMAGES is not 'true'." - echo We HIGHLY recommend setting it to 'true' unless you are debugging issues. -fi + if [[ ${BRIGHTNESS_THRESHOLD} == "0" ]]; then + heading "Warnings" + echo "BRIGHTNESS_THRESHOLD is 0 which means ALL images will be IGNORED when creating startrails." + elif [[ ${BRIGHTNESS_THRESHOLD} == "1" ]]; then + heading "Warnings" + echo "BRIGHTNESS_THRESHOLD is 1 which means ALL images will be USED when creating startrails, even daytime images." + fi -##### Uploads -if [[ ${RESIZE_UPLOADS} == "true" && ${IMG_UPLOAD} == "false" ]]; then - heading "Warnings" - echo "RESIZE_UPLOADS is 'true' but you aren't uploading images (IMG_UPLOAD='false')." -fi + ##### Images + if [[ ${TAKE} == "false" && ${SAVE} == "true" ]]; then + heading "Warnings" + echo "'Daytime Capture' is off but 'Daytime Save' is on in the WebUI." + fi -case "${PROTOCOL}" in - "" | local) # Nothing needed for these - ;; + # These can be floats which bash doesn't support, so treat as strings. + if [[ ${REMOVE_BAD_IMAGES_LOW} == "0" ]]; then + heading "Warnings" + echo "'Remove Bad Images Threshold Low' is 0 (disabled)." + echo We HIGHLY recommend setting it to a value greater than 0 unless you are debugging issues. + fi + if [[ ${REMOVE_BAD_IMAGES_HIGH} == "0" ]]; then + heading "Warnings" + echo "'Remove Bad Images Threshold High' is 0 (disabled)." + echo We HIGHLY recommend setting it to a value greater than 0 unless you are debugging issues. + fi - ftp | ftps | sftp) - check_PROTOCOL "${PROTOCOL}" "REMOTE_HOST" - check_PROTOCOL "${PROTOCOL}" "REMOTE_USER" - check_PROTOCOL "${PROTOCOL}" "REMOTE_PASSWORD" - if [[ ${PROTOCOL} == "ftp" ]]; then + ##### Uploads + if [[ ${RESIZE_UPLOADS_WIDTH} -ne 0 || ${RESIZE_UPLOADS_WIDTH} -ne 0 ]]; then + if [[ ${IMG_UPLOAD_FREQUENCY} -eq 0 ]]; then heading "Warnings" - echo "PROTOCOL set to insecure 'ftp'. Try to use 'ftps' or 'sftp' instead." + echo "'Resize Uploaded Images Width/Height' is set but you aren't uploading images (Upload Every X Images=0)." fi - ;; - - scp) - check_PROTOCOL "${PROTOCOL}" "REMOTE_HOST" - if check_PROTOCOL "${PROTOCOL}" "SSH_KEY_FILE" && [[ ! -e ${SSH_KEY_FILE} ]]; then + if [[ ${RESIZE_UPLOADS_WIDTH} -eq 0 && ${RESIZE_UPLOADS_HEIGHT} -ne 0 ]]; then heading "Warnings" - echo "PROTOCOL (${PROTOCOL}) set but 'SSH_KEY_FILE' (${SSH_KEY_FILE}) does not exist." - echo "Uploads will not work." - fi - ;; - - s3) - if check_PROTOCOL "${PROTOCOL}" "AWS_CLI_DIR" && [[ ! -e ${AWS_CLI_DIR} ]]; then + echo "'Resize Uploaded Images Width' = 0 but 'Resize Uploaded Images Height' > 0." + echo "'If one is set the other one must also be set." + elif [[ ${RESIZE_UPLOADS_WIDTH} -ne 0 && ${RESIZE_UPLOADS_HEIGHT} -eq 0 ]]; then heading "Warnings" - echo "PROTOCOL (${PROTOCOL}) set but 'AWS_CLI_DIR' (${AWS_CLI_DIR}) does not exist." - echo "Uploads will not work." + echo "'Resize Uploaded Images Height' > 0 but 'Resize Uploaded Images Width' = 0." + echo "'If one is set the other one must also be set." fi - check_PROTOCOL "${PROTOCOL}" "S3_BUCKET" - check_PROTOCOL "${PROTOCOL}" "S3_ACL" - ;; + fi - gcs) - check_PROTOCOL "${PROTOCOL}" "GCS_BUCKET" - check_PROTOCOL "${PROTOCOL}" "GCS_ACL" - ;; + X="$( check_remote_server "REMOTEWEBSITE" )" + RET=$? + if [[ ${RET} -eq 1 ]]; then + heading "Warnings" + echo -e "${X}" + elif [[ ${RET} -eq 2 ]]; then + heading "Errors" + echo -e "${X}" + fi - *) + X="$( check_remote_server "REMOTESERVER" )" + RET=$? + if [[ ${RET} -eq 1 ]]; then heading "Warnings" - echo "PROTOCOL (${PROTOCOL}) not blank or one of: local, ftp, ftps, sftp, scp, s3, gcs." - echo "Uploads will not work until this is corrected." - ;; -esac - -if [[ -n ${REMOTE_PORT} ]] && ! is_number "${REMOTE_PORT}" ; then - heading "Warnings" - echo "REMOTE_PORT (${REMOTE_PORT}) must be a number." - echo "Uploads will not work until this is corrected." -fi + echo -e "${X}" + elif [[ ${RET} -eq 2 ]]; then + heading "Errors" + echo -e "${X}" + fi -##### If these variables are set, their corresponding directory should exist. -check_exists "WEB_IMAGE_DIR" -check_exists "WEB_VIDEOS_DIR" -check_exists "WEB_KEOGRAM_DIR" -check_exists "WEB_STARTRAILS_DIR" -check_exists "UHUBCTL_PATH" - -##### Check for Allsky Website-related issues. -if [[ -f ${ALLSKY_REMOTE_WEBSITE_CONFIGURATION_FILE} && (${PROTOCOL} == "" || ${PROTOCOL} == "local") ]]; then - heading "Warnings" - echo "A remote Allsky Website configuration file was found but PROTOCOL doesn't support uploading files." -fi + + ##### If these variables are set, their corresponding directory should exist. + check_exists "UHUBCTL_PATH" + + ##### Check for Allsky Website-related issues. + if [[ -f ${ALLSKY_REMOTE_WEBSITE_CONFIGURATION_FILE} && (${PROTOCOL} == "" || ${PROTOCOL} == "local") ]]; then + heading "Warnings" + echo "A remote Allsky Website configuration file was found but PROTOCOL doesn't support uploading files." + fi +fi # end of checking for warning items @@ -594,185 +682,139 @@ fi # ================= Check for error items. # These are wrong and will likely keep Allsky from running. -##### Make sure it's a know camera type. -if [[ ${CAMERA_TYPE} != "ZWO" && ${CAMERA_TYPE} != "RPi" ]]; then - heading "Errors" - echo "INTERNAL ERROR: CAMERA_TYPE (${CAMERA_TYPE}) not valid." -fi +if [[ ${CHECK_ERRORS} == "true" ]]; then -##### Make sure the settings file is properly linked. -if ! MSG="$( check_settings_link "${SETTINGS_FILE}" )" ; then - heading "Errors" - echo -e "${MSG}" -fi + # Settings used in this section. + USING_DARKS="$( get_setting ".usedarkframes" )" + IMG_UPLOAD_ORIGINAL_NAME="$( get_setting ".imageuploadoriginalname" )" + IMG_CREATE_THUMBNAILS="$( get_setting ".imagecreatethumbnails" )" + TIMELAPSE_MINI_FORCE_CREATION="$( get_setting ".minitimelapseforcecreation" )" + # shellcheck disable=SC2034 + LOCALE="$( get_setting ".locale" )" -##### Make sure these booleans have boolean values, or are blank. -for i in IMG_UPLOAD IMG_UPLOAD_ORIGINAL_NAME IMG_RESIZE CROP_IMAGE AUTO_STRETCH \ - RESIZE_UPLOADS IMG_CREATE_THUMBNAILS REMOVE_BAD_IMAGES TIMELAPSE UPLOAD_VIDEO \ - TIMELAPSE_UPLOAD_THUMBNAIL TIMELAPSE_MINI_FORCE_CREATION TIMELAPSE_MINI_UPLOAD_VIDEO \ - TIMELAPSE_MINI_UPLOAD_THUMBNAIL KEOGRAM UPLOAD_KEOGRAM \ - STARTRAILS UPLOAD_STARTRAILS POST_END_OF_NIGHT_DATA -do - if [[ -n ${!i} && ${!i,,} != "true" && ${!i,,} != "false" ]]; then + ##### Make sure it's a know camera type. + if [[ ${CAMERA_TYPE} != "ZWO" && ${CAMERA_TYPE} != "RPi" ]]; then heading "Errors" - echo "${i} must be either 'true' or 'false'." + echo "INTERNAL ERROR: CAMERA_TYPE (${CAMERA_TYPE}) not valid." fi -done -##### Check that all required settings are set. All others are optional. -# TODO: determine from options.json file which are required. -for i in ANGLE LATITUDE LONGITUDE LOCALE -do - if [[ -z ${!i} || ${!i} == "null" ]]; then + ##### Make sure the settings file is properly linked. + if ! MSG="$( check_settings_link "${SETTINGS_FILE}" )" ; then heading "Errors" - echo "${i} must be set." + echo -e "${MSG}" fi -done -##### Check that the required settings' values are valid. -if [[ -n ${ANGLE} ]] && ! is_number "${ANGLE}" ; then - heading "Errors" - echo "ANGLE (${ANGLE}) must be a number." -fi -if [[ -n ${LATITUDE} ]]; then - if ! LAT="$(convertLatLong "${LATITUDE}" "latitude" 2>&1)" ; then - heading "Errors" - echo -e "${LAT}" # ${LAT} contains the error message - fi -fi -if [[ -n ${LONGITUDE} ]]; then - if ! LONG="$(convertLatLong "${LONGITUDE}" "longitude" 2>&1)" ; then - heading "Errors" - echo -e "${LONG}" - fi -fi + function check_bool() + { + local B="${1}" + local NAME="${2}" + if [[ ${B,,} != "true" && ${B,,} != "false" ]]; then + heading "Errors" + echo "'${NAME}' must be either 'true' or 'false'." + fi + } -##### Check dark frames -if [[ ${USING_DARKS} -eq 1 ]]; then - if [[ ! -d ${ALLSKY_DARKS} ]]; then - heading "Errors" - echo "'Use Dark Frames' is set but the '${ALLSKY_DARKS}' directory does not exist." - else - NUM_DARKS=$(find "${ALLSKY_DARKS}" -name "*.${EXTENSION}" 2>/dev/null | wc -l) - if [[ ${NUM_DARKS} -eq 0 ]]; then + ##### Make sure these booleans have boolean values. + # TODO: use options.json to determine which are type=boolean. + check_bool "${USING_DARKS}" "Use Dark Frames" + check_bool "${IMG_UPLOAD_ORIGINAL_NAME}" "Upload With Original Name" + check_bool "${IMG_CREATE_THUMBNAILS}" "Create Image Thumbnails" + check_bool "${TIMELAPSE}" "Generate Timelapse" + check_bool "${UPLOAD_VIDEO}" "Upload Timelapse" + check_bool "${KEEP_SEQUENCE}" "Keep Timelapse Sequence" + check_bool "${TIMELAPSE_UPLOAD_THUMBNAIL}" "Upload Timelapse Thumbnail" + check_bool "${TIMELAPSE_MINI_FORCE_CREATION}" "Force Creation (of mini-timelapse)" + check_bool "${TIMELAPSE_MINI_UPLOAD_VIDEO}" "Upload Mini-Timelapse" + check_bool "${TIMELAPSE_MINI_UPLOAD_THUMBNAIL}" "Upload Mini-Timelapse Thumbnail" + check_bool "${KEOGRAM}" "Generate Keogram" + check_bool "${UPLOAD_KEOGRAM}" "Upload Keogram" + check_bool "${STARTRAILS}" "Generate Startrails" + check_bool "${UPLOAD_STARTRAILS}" "Upload Startrails" + + ##### Check that all required settings are set. All others are optional. + # TODO: determine from options.json file which are required. + for i in ANGLE LATITUDE LONGITUDE LOCALE + do + if [[ -z ${!i} ]]; then heading "Errors" - echo -n "'Use Dark Frames' is set but there are no darks" - echo " in '${ALLSKY_DARKS}' with extension of '${EXTENSION}'." + echo "${i} must be set." fi - fi -fi + done -##### Check for valid numbers. -if ! is_number "${IMG_UPLOAD_FREQUENCY}" || [[ ${IMG_UPLOAD_FREQUENCY} -le 0 ]]; then - heading "Errors" - echo "IMG_UPLOAD_FREQUENCY (${IMG_UPLOAD_FREQUENCY}) must be 1 or greater." -fi -if [[ ${AUTO_STRETCH} == "true" ]]; then - if ! is_number "${AUTO_STRETCH_AMOUNT}" || \ - [[ ${AUTO_STRETCH_AMOUNT} -le 0 ]] || \ - [[ ${AUTO_STRETCH_AMOUNT} -gt 100 ]] ; then - heading "Errors" - echo "AUTO_STRETCH_AMOUNT (${AUTO_STRETCH_AMOUNT}) must be 1 - 100." - fi - if ! echo "${AUTO_STRETCH_MID_POINT}" | grep --silent "%" ; then + ##### Check that the required settings' values are valid. + if [[ -n ${ANGLE} ]] && ! is_number "${ANGLE}" ; then heading "Errors" - echo "AUTO_STRETCH_MID_POINT (${AUTO_STRETCH_MID_POINT}) must be an integer percent," - echo "for example: 10%." + echo "ANGLE (${ANGLE}) must be a number." fi -fi -if ! is_number "${BRIGHTNESS_THRESHOLD}" || \ - ! echo "${BRIGHTNESS_THRESHOLD}" | \ - awk '{if ($1 < 0.0 || $1 > 1.0) exit 1; exit 0; }' ; then - heading "Errors" - echo "BRIGHTNESS_THRESHOLD (${BRIGHTNESS_THRESHOLD}) must be 0.0 - 1.0" -fi -if [[ ${REMOVE_BAD_IMAGES} == "true" ]]; then - if ! is_number "${REMOVE_BAD_IMAGES_THRESHOLD_LOW}" || \ - ! echo "${REMOVE_BAD_IMAGES_THRESHOLD_LOW}" | \ - awk '{if ($1 < 0.0) exit 1; exit 0; }' ; then - heading "Errors" - echo "REMOVE_BAD_IMAGES_THRESHOLD_LOW (${REMOVE_BAD_IMAGES_THRESHOLD_LOW}) must be 0 - 100.0," - echo "although it's normally around 0.5. 0 disables the low threshold check." + if [[ -n ${LATITUDE} ]]; then + if ! LAT="$( convertLatLong "${LATITUDE}" "latitude" 2>&1 )" ; then + heading "Errors" + echo -e "${LAT}" # ${LAT} contains the error message + fi fi - if ! is_number "${REMOVE_BAD_IMAGES_THRESHOLD_HIGH}" || \ - ! echo "${REMOVE_BAD_IMAGES_THRESHOLD_HIGH}" | \ - awk '{if ($1 < 0.0) exit 1; exit 0; }' ; then - heading "Errors" - echo "REMOVE_BAD_IMAGES_THRESHOLD_HIGH (${REMOVE_BAD_IMAGES_THRESHOLD_HIGH}) must be 0 - 100.0," - echo "although it's normally around 90. 0 disables the high threshold check." + if [[ -n ${LONGITUDE} ]]; then + if ! LONG="$( convertLatLong "${LONGITUDE}" "longitude" 2>&1 )" ; then + heading "Errors" + echo -e "${LONG}" + fi fi -fi -##### If images are being resized or cropped, -# make sure the resized/cropped image is fully within the sensor image. -HAS_PIXEL_ERROR="false" -if [[ ${IMG_RESIZE} == "true" ]]; then - if ! X="$(checkPixelValue "IMG_WIDTH" "${IMG_WIDTH}" "width" "${SENSOR_WIDTH}")" ; then - heading "Errors" - echo -e "${X}" - HAS_PIXEL_ERROR="true" - fi - if ! X="$(checkPixelValue "IMG_HEIGHT" "${IMG_HEIGHT}" "height" "${SENSOR_HEIGHT}")" ; then - heading "Errors" - echo -e "${X}" - HAS_PIXEL_ERROR="true" + ##### Check dark frames + if [[ ${USING_DARKS} == "true" ]]; then + if [[ ! -d ${ALLSKY_DARKS} ]]; then + heading "Errors" + echo "'Use Dark Frames' is set but the '${ALLSKY_DARKS}' directory does not exist." + else + NUM_DARKS=$( find "${ALLSKY_DARKS}" -name "*.${EXTENSION}" 2>/dev/null | wc -l) + if [[ ${NUM_DARKS} -eq 0 ]]; then + heading "Errors" + echo -n "'Use Dark Frames' is set but there are no darks" + echo " in '${ALLSKY_DARKS}' with extension of '${EXTENSION}'." + fi + fi fi -fi -if [[ ${CROP_IMAGE} == "true" ]]; then - if ! X="$(checkPixelValue "CROP_WIDTH" "${CROP_WIDTH}" "width" "${SENSOR_WIDTH}")" ; then - heading "Errors" - echo -e "${X}" - HAS_PIXEL_ERROR="true" - fi - if ! X="$(checkPixelValue "CROP_HEIGHT" "${CROP_HEIGHT}" "height" "${SENSOR_HEIGHT}")" ; then + ##### Check for valid numbers. + if ! is_number "${IMG_UPLOAD_FREQUENCY}" || [[ ${IMG_UPLOAD_FREQUENCY} -le 0 ]]; then heading "Errors" - echo -e "${X}" - HAS_PIXEL_ERROR="true" - fi - # "any" means it can be any number, positive or negative. - if ! X="$(checkPixelValue "CROP_OFFSET_X" "${CROP_OFFSET_X}" "width" "${SENSOR_WIDTH}" "any")" ; then - heading "Errors" - echo -e "${X}" - HAS_PIXEL_ERROR="true" - fi - if ! X="$(checkPixelValue "CROP_OFFSET_Y" "${CROP_OFFSET_Y}" "height" "${SENSOR_HEIGHT}" "any")" ; then - heading "Errors" - echo -e "${X}" - HAS_PIXEL_ERROR="true" + echo "IMG_UPLOAD_FREQUENCY (${IMG_UPLOAD_FREQUENCY}) must be 1 or greater." fi - - # Do more intensive checks but only if there weren't IMG_RESIZE errors since we - # we can't use IMG_WIDTH or IMG_HEIGHT. - if [[ ${HAS_PIXEL_ERROR} == "false" ]]; then - if [[ ${IMG_RESIZE} == "true" ]]; then - MAX_X=${IMG_WIDTH} - MAX_Y=${IMG_HEIGHT} - else - MAX_X=${SENSOR_WIDTH} - MAX_Y=${SENSOR_HEIGHT} + if [[ ${AUTO_STRETCH} == "true" ]]; then + if ! is_number "${AUTO_STRETCH_AMOUNT}" || \ + [[ ${AUTO_STRETCH_AMOUNT} -le 0 ]] || \ + [[ ${AUTO_STRETCH_AMOUNT} -gt 100 ]] ; then + heading "Errors" + echo "AUTO_STRETCH_AMOUNT (${AUTO_STRETCH_AMOUNT}) must be 1 - 100." fi - if ! X="$(checkCropValues "${CROP_WIDTH}" "${CROP_HEIGHT}" \ - "${CROP_OFFSET_X}" "${CROP_OFFSET_Y}" \ - "${MAX_X}" "${MAX_Y}")" ; then + if ! echo "${AUTO_STRETCH_MID_POINT}" | grep --silent "%" ; then heading "Errors" - echo -e "${X}" + echo "AUTO_STRETCH_MID_POINT (${AUTO_STRETCH_MID_POINT}) must be an integer percent," + echo "for example: 10%." fi fi -fi - -if [[ ${RESIZE_UPLOADS} == "true" ]]; then - if ! X="$(checkPixelValue "RESIZE_UPLOADS_WIDTH" "${RESIZE_UPLOADS_WIDTH}" "width" "${SENSOR_WIDTH}")" ; then + if ! is_number "${BRIGHTNESS_THRESHOLD}" || \ + ! echo "${BRIGHTNESS_THRESHOLD}" | \ + awk '{if ($1 < 0.0 || $1 > 1.0) exit 1; exit 0; }' ; then heading "Errors" - echo -e "${X}" - echo "It is typically less than the sensor width of ${SENSOR_WIDTH}." + echo "BRIGHTNESS_THRESHOLD (${BRIGHTNESS_THRESHOLD}) must be 0.0 - 1.0" fi - if ! X="$(checkPixelValue "RESIZE_UPLOADS_HEIGHT" "${RESIZE_UPLOADS_HEIGHT}" "height" "${SENSOR_HEIGHT}")" ; then - heading "Errors" - echo -e "${X}" - echo "It is typically less than the sensor height of ${SENSOR_HEIGHT}." + if [[ ${REMOVE_BAD_IMAGES} == "true" ]]; then + if ! is_number "${REMOVE_BAD_IMAGES_THRESHOLD_LOW}" || \ + ! echo "${REMOVE_BAD_IMAGES_THRESHOLD_LOW}" | \ + awk '{if ($1 < 0.0) exit 1; exit 0; }' ; then + heading "Errors" + echo "REMOVE_BAD_IMAGES_THRESHOLD_LOW (${REMOVE_BAD_IMAGES_THRESHOLD_LOW}) must be 0 - 100.0," + echo "although it's normally around 0.5. 0 disables the low threshold check." + fi + if ! is_number "${REMOVE_BAD_IMAGES_THRESHOLD_HIGH}" || \ + ! echo "${REMOVE_BAD_IMAGES_THRESHOLD_HIGH}" | \ + awk '{if ($1 < 0.0) exit 1; exit 0; }' ; then + heading "Errors" + echo "REMOVE_BAD_IMAGES_THRESHOLD_HIGH (${REMOVE_BAD_IMAGES_THRESHOLD_HIGH}) must be 0 - 100.0," + echo "although it's normally around 90. 0 disables the high threshold check." + fi fi -fi +fi # end of checking for warning items # ====================================================================== diff --git a/scripts/copy_notification_image.sh b/scripts/copy_notification_image.sh index dc999407b..78bf6030b 100755 --- a/scripts/copy_notification_image.sh +++ b/scripts/copy_notification_image.sh @@ -1,17 +1,13 @@ #!/bin/bash # Allow this script to be executed manually, which requires several variables to be set. -[[ -z ${ALLSKY_HOME} ]] && export ALLSKY_HOME="$(realpath "$(dirname "${BASH_ARGV0}")/..")" -ME="$(basename "${BASH_ARGV0}")" - -#shellcheck disable=SC2086 source-path=. -source "${ALLSKY_HOME}/variables.sh" || exit ${ALLSKY_ERROR_STOP} -#shellcheck disable=SC2086 source-path=scripts -source "${ALLSKY_SCRIPTS}/functions.sh" || exit ${ALLSKY_ERROR_STOP} -#shellcheck disable=SC2086,SC1091 # file doesn't exist in GitHub -source "${ALLSKY_CONFIG}/config.sh" || exit ${ALLSKY_ERROR_STOP} -#shellcheck disable=SC2086,SC1091 # file doesn't exist in GitHub -source "${ALLSKY_CONFIG}/ftp-settings.sh" || exit ${ALLSKY_ERROR_STOP} +[[ -z ${ALLSKY_HOME} ]] && export ALLSKY_HOME="$( realpath "$( dirname "${BASH_ARGV0}" )/.." )" +ME="$( basename "${BASH_ARGV0}" )" + +#shellcheck source-path=. +source "${ALLSKY_HOME}/variables.sh" || exit "${ALLSKY_ERROR_STOP}" +#shellcheck source-path=scripts +source "${ALLSKY_SCRIPTS}/functions.sh" || exit "${ALLSKY_ERROR_STOP}" function usage_and_exit { @@ -25,8 +21,7 @@ function usage_and_exit echo " TextColor Font FontSize StrokeColor StrokeWidth BgColor BorderWidth BorderColor Extensions ImageSize 'Message'" [[ ${RET} -ne 0 ]] && echo -e "${NC}" ) >&2 - # shellcheck disable=SC2086 - exit ${RET} + exit "${RET}" } OK="true" @@ -64,9 +59,10 @@ done NOTIFICATION_TYPE="${1}" # filename, minus the extension, since the extension may vary [[ ${NOTIFICATION_TYPE} == "" ]] && usage_and_exit 1 +NUM_ARGS=12 if [[ ${NOTIFICATION_TYPE} == "custom" ]]; then - if [[ $# -ne 12 ]]; then - echo -e "${RED}'custom' notification type requires 12 arguments" >&2 + if [[ $# -ne "${NUM_ARGS}" ]]; then + echo -e "${RED}'custom' notification type requires ${NUM_ARGS} arguments" >&2 usage_and_exit 1 fi @@ -78,7 +74,7 @@ if [[ ${NOTIFICATION_TYPE} == "custom" ]]; then "${7}" "${8}" "${9}" "${10:-${EXTENSION}}" "${11}" "${12}" ; then exit 2 # it output error messages fi - NOTIFICATION_FILE="${ALLSKYCAPTURE_SAVE_DIR}/${NOTIFICATION_TYPE}.${EXTENSION}" + NOTIFICATION_FILE="${CAPTURE_SAVE_DIR}/${NOTIFICATION_TYPE}.${EXTENSION}" else NOTIFICATION_FILE="${ALLSKY_NOTIFICATION_IMAGES}/${NOTIFICATION_TYPE}.${EXTENSION}" if [[ ! -e ${NOTIFICATION_FILE} ]]; then @@ -122,8 +118,10 @@ else fi # Resize the image if required -if [[ ${IMG_RESIZE} == "true" ]]; then - if ! convert "${CURRENT_IMAGE}" -resize "${IMG_WIDTH}x${IMG_HEIGHT}" "${CURRENT_IMAGE}" ; then +RESIZE_W="$( settings ".imageresizewidth" )" +if [[ ${RESIZE_W} -gt 0 ]]; then + RESIZE_H="$( settings ".imageresizeheight" )" + if ! convert "${CURRENT_IMAGE}" -resize "${RESIZE_W}x${RESIZE_H}" "${CURRENT_IMAGE}" ; then echo -e "${RED}*** ${ME}: ERROR: IMG_RESIZE failed${NC}" exit 3 fi @@ -134,9 +132,9 @@ fi # Don't save in main image directory because we don't want the notification image in timelapses. # If at nighttime, save them in (possibly) yesterday's directory. # If during day, save in today's directory. -if [[ $(settings ".takeDaytimeImages") == "1" && \ - $(settings ".saveDaytimeImages") == "1" && \ - ${IMG_CREATE_THUMBNAILS} == "true" ]]; then +if [[ $( settings ".takedaytimeimages" ) == "true" && \ + $( settings ".savedaytimeimages" ) == "true" && \ + $( settings ".imagecreatethumbnails" ) == "true" ]]; then DATE_DIR="${ALLSKY_IMAGES}/$(date +'%Y%m%d')" # Use today's folder if it exists, otherwise yesterday's [[ ! -d ${DATE_DIR} ]] && DATE_DIR="${ALLSKY_IMAGES}/$(date -d '12 hours ago' +'%Y%m%d')" @@ -147,7 +145,9 @@ if [[ $(settings ".takeDaytimeImages") == "1" && \ else THUMB="${THUMBNAILS_DIR}/${FILENAME}-$(date +'%Y%m%d%H%M%S').${EXTENSION}" - if ! convert "${CURRENT_IMAGE}" -resize "${THUMBNAIL_SIZE_X}x${THUMBNAIL_SIZE_Y}" "${THUMB}" ; then + X="$( settings ".thumbnailsizex" )" + Y="$( settings ".thumbnailsizey" )" + if ! convert "${CURRENT_IMAGE}" -resize "${X}x${Y}" "${THUMB}" ; then echo -e "${YELLOW}*** ${ME}: WARNING: THUMBNAIL resize failed; continuing.${NC}" fi fi @@ -165,13 +165,15 @@ fi # Keep track of last notification type and time. # We don't use the type (at least not yet), but save it anyhow so we can manually look at # it for debugging purposes. -EXPIRE_TIME=$(date -d "${EXPIRES_IN_SECONDS} seconds" +'%Y-%m-%d %H:%M:%S') +EXPIRE_TIME=$( date -d "${EXPIRES_IN_SECONDS} seconds" +'%Y-%m-%d %H:%M:%S' ) echo "${NOTIFICATION_TYPE},${EXPIRES_IN_SECONDS},${EXPIRE_TIME}" >> "${ALLSKY_NOTIFICATION_LOG}" touch --date="${EXPIRE_TIME}" "${ALLSKY_NOTIFICATION_LOG}" # If upload is true, optionally create a smaller version of the image, either way, upload it. -if [[ ${IMG_UPLOAD} == "true" ]]; then - if [[ ${RESIZE_UPLOADS} == "true" ]]; then +if [[ $( settings ".imageuploadfrequency" ) -gt 0 ]]; then + RESIZE_UPLOADS_WIDTH="$( settings ".imageresizeuploadswidth" )" + if [[ ${RESIZE_UPLOADS_WIDTH} == "true" ]]; then + RESIZE_UPLOADS_HEIGHT="$( settings ".imageresizeuploadsheight" )" # Don't overwrite FINAL_IMAGE since the web server(s) may be looking at it. TEMP_FILE="${CAPTURE_SAVE_DIR}/resize-${FULL_FILENAME}" @@ -198,15 +200,13 @@ if [[ ${IMG_UPLOAD} == "true" ]]; then if [[ ${ALLSKY_DEBUG_LEVEL} -ge 4 ]]; then echo -e "${ME}: Uploading $(basename "${NOTIFICATION_FILE}")" fi - "${ALLSKY_SCRIPTS}/upload.sh" --wait --silent \ - "${UPLOAD_FILE}" "${IMAGE_DIR}" "${FULL_FILENAME}" "NotificationImage" "${WEB_IMAGE_DIR}" + upload_all --local-web --remote-web --wait --silent "${UPLOAD_FILE}" "" "${FULL_FILENAME}" "NotificationImage" RET=$? # If we created a temporary copy, delete it. [[ ${TEMP_FILE} != "" ]] && rm -f "${TEMP_FILE}" - # shellcheck disable=SC2086 - exit ${RET} + exit "${RET}" fi exit 0 diff --git a/scripts/darkCapture.sh b/scripts/darkCapture.sh index f3a7fa906..5bdb452c4 100755 --- a/scripts/darkCapture.sh +++ b/scripts/darkCapture.sh @@ -2,7 +2,7 @@ # This file is "source"d into another. # "${CURRENT_IMAGE}" is the full pathname of the current image we're working on and is passed to us. -ME2="$(basename "${BASH_SOURCE[0]}")" +ME2="$( basename "${BASH_SOURCE[0]}" )" # Make sure the input file exists; if not, something major is wrong so exit. if [[ -z ${CURRENT_IMAGE} ]]; then @@ -23,7 +23,7 @@ if [[ -z ${AS_TEMPERATURE_C} ]]; then # The camera doesn't support temperature so we'll keep overwriting the file until # AS_TEMPERATURE_C is set. # This allows users to continually look for a new dark file and rename it manually. - MOVE_TO_FILE="${DARKS_DIR}/$(basename "${CURRENT_IMAGE}")" + MOVE_TO_FILE="${DARKS_DIR}/$( basename "${CURRENT_IMAGE}" )" else MOVE_TO_FILE="${DARKS_DIR}/${AS_TEMPERATURE_C}.${DARK_EXTENSION}" fi @@ -35,8 +35,8 @@ mv "${CURRENT_IMAGE}" "${MOVE_TO_FILE}" || exit 3 # Some people may want to see the dark frame even if notification images # are being used, but no one's askef for that feature so don't worry about it. -if [[ $(settings ".notificationimages") -eq 0 ]]; then +if [[ $(settings ".notificationimages") == "false" ]]; then # We're copying back the file we just moved, but the assumption is few people # will want to see the dark frames so the performance hit is - cp "${MOVE_TO_FILE}" "${ALLSKY_TMP}/${FILENAME}.${EXTENSION}" + cp "${MOVE_TO_FILE}" "${ALLSKY_TMP}/${FILENAME}.${EXTENSION}" || exit 4 fi diff --git a/scripts/darkSubtract.sh b/scripts/darkSubtract.sh index e2550efc1..d5aafc75c 100755 --- a/scripts/darkSubtract.sh +++ b/scripts/darkSubtract.sh @@ -3,12 +3,12 @@ # This file is "source"d into another. # "${CURRENT_IMAGE}" is the name of the current image we're working on. -ME2="$(basename "${BASH_SOURCE[0]}")" +ME2="$( basename "${BASH_SOURCE[0]}" )" -# Subtract dark frame if there is one defined in config.sh +# Subtract dark frame if there is one. # This has to come after executing darkCapture.sh which sets ${AS_TEMPERATURE_C}. -if [[ $(settings ".useDarkFrames") -eq 1 ]]; then +if [[ $(settings ".usedarkframes") == "true" ]]; then # Make sure the input file exists; if not, something major is wrong so exit. if [[ -z ${CURRENT_IMAGE} ]]; then echo "*** ${ME2}: ERROR: 'CURRENT_IMAGE' not set; aborting." @@ -45,12 +45,12 @@ if [[ $(settings ".useDarkFrames") -eq 1 ]]; then # than ${AS_TEMPERATURE_C}, stop, then compare it to the previous file to # determine which is closer to ${AS_TEMPERATURE_C}. # Need "--general-numeric-sort" in case any files have a leading "-". - for file in $(find "${DARKS_DIR}" -maxdepth 1 -iname "*.${EXTENSION}" | sed 's;.*/;;' | sort --general-numeric-sort) + for file in $( find "${DARKS_DIR}" -maxdepth 1 -iname "*.${EXTENSION}" | + sed 's;.*/;;' | sort --general-numeric-sort ) do - [[ ${ALLSKY_DEBUG_LEVEL} -ge 5 ]] && echo "Looking at ${file}" # Example file name for 21 degree dark: "21.jpg". if [[ -s ${DARKS_DIR}/${file} ]]; then - file="$(basename "./${file}")" # need "./" in case file has "-" + file="$( basename "./${file}" )" # need "./" in case file has "-" # Get name of file (which is the temp) without extension DARK_TEMPERATURE=${file%.*} if [[ ${DARK_TEMPERATURE} -gt ${AS_TEMPERATURE_C} ]]; then diff --git a/scripts/endOfDay.sh b/scripts/endOfDay.sh index 720a1b045..225f37c95 100755 --- a/scripts/endOfDay.sh +++ b/scripts/endOfDay.sh @@ -1,17 +1,13 @@ #!/bin/bash # Allow this script to be executed manually, which requires several variables to be set. -[[ -z ${ALLSKY_HOME} ]] && export ALLSKY_HOME="$(realpath "$(dirname "${BASH_ARGV0}")/..")" -ME="$(basename "${BASH_ARGV0}")" +[[ -z ${ALLSKY_HOME} ]] && export ALLSKY_HOME="$( realpath "$( dirname "${BASH_ARGV0}" )/.." )" +ME="$( basename "${BASH_ARGV0}" )" -#shellcheck disable=SC2086 source-path=. -source "${ALLSKY_HOME}/variables.sh" || exit ${ALLSKY_ERROR_STOP} -#shellcheck disable=SC2086 source-path=scripts -source "${ALLSKY_SCRIPTS}/functions.sh" || exit ${ALLSKY_ERROR_STOP} -#shellcheck disable=SC2086,SC1091 # file doesn't exist in GitHub -source "${ALLSKY_CONFIG}/config.sh" || exit ${ALLSKY_ERROR_STOP} -#shellcheck disable=SC2086,SC1091 # file doesn't exist in GitHub -source "${ALLSKY_CONFIG}/ftp-settings.sh" || exit ${ALLSKY_ERROR_STOP} +#shellcheck source-path=. +source "${ALLSKY_HOME}/variables.sh" || exit "${ALLSKY_ERROR_STOP}" +#shellcheck source-path=scripts +source "${ALLSKY_SCRIPTS}/functions.sh" || exit "${ALLSKY_ERROR_STOP}" if [[ $# -eq 1 ]]; then if [[ ${1} == "--help" ]]; then @@ -25,9 +21,9 @@ else fi # If we weren't saving daytime images the directory won't exist. -SAVING="$( settings .saveDaytimeImages )" +SAVING="$( settings ".savedaytimeimages" )" DATE_DIR="${ALLSKY_IMAGES}/${DATE}" -if [[ ! -d ${DATE_DIR} && ${SAVING} -eq 1 ]]; then +if [[ ! -d ${DATE_DIR} && ${SAVING} == "true" ]]; then echo -e "${ME}: ${RED}ERROR: '${DATE_DIR}' not found!${NC}" exit 2 fi diff --git a/scripts/endOfNight.sh b/scripts/endOfNight.sh index 6e6267a3d..0c80340e5 100755 --- a/scripts/endOfNight.sh +++ b/scripts/endOfNight.sh @@ -5,17 +5,13 @@ # 2. Perform daily housekeeping not related to the specified day, like removing old files. # Allow this script to be executed manually, which requires several variables to be set. -[[ -z ${ALLSKY_HOME} ]] && export ALLSKY_HOME="$(realpath "$(dirname "${BASH_ARGV0}")/..")" -ME="$(basename "${BASH_ARGV0}")" - -#shellcheck disable=SC2086 source-path=. -source "${ALLSKY_HOME}/variables.sh" || exit ${ALLSKY_ERROR_STOP} -#shellcheck disable=SC2086 source-path=scripts -source "${ALLSKY_SCRIPTS}/functions.sh" || exit ${ALLSKY_ERROR_STOP} -#shellcheck disable=SC2086,SC1091 # file doesn't exist in GitHub -source "${ALLSKY_CONFIG}/config.sh" || exit ${ALLSKY_ERROR_STOP} -#shellcheck disable=SC2086,SC1091 # file doesn't exist in GitHub -source "${ALLSKY_CONFIG}/ftp-settings.sh" || exit ${ALLSKY_ERROR_STOP} +[[ -z ${ALLSKY_HOME} ]] && export ALLSKY_HOME="$( realpath "$( dirname "${BASH_ARGV0}" )/.." )" +ME="$( basename "${BASH_ARGV0}" )" + +#shellcheck source-path=. +source "${ALLSKY_HOME}/variables.sh" || exit "${ALLSKY_ERROR_STOP}" +#shellcheck source-path=scripts +source "${ALLSKY_SCRIPTS}/functions.sh" || exit "${ALLSKY_ERROR_STOP}" if [[ $# -eq 1 ]]; then if [[ ${1} = "--help" ]]; then @@ -36,7 +32,7 @@ if [[ ! -d ${DATE_DIR} ]]; then fi # Decrease priority when running in background. -if [[ ${ON_TTY} -eq 1 ]]; then +if [[ ${ON_TTY} == "true" ]]; then NICE="" NICE_ARG="" else @@ -45,7 +41,7 @@ else fi # Post end of night data. This includes next twilight time -WEBSITES="$(whatWebsites)" +WEBSITES="$( whatWebsites )" if [[ ${WEBSITES} != "none" ]]; then echo -e "${ME}: ===== Posting twilight data" @@ -53,26 +49,26 @@ if [[ ${WEBSITES} != "none" ]]; then fi # Generate keogram from collected images -if [[ ${KEOGRAM} == "true" ]]; then +if [[ $( settings ".keogramgenerate" ) == "true" ]]; then echo -e "${ME}: ===== Generating Keogram" #shellcheck disable=SC2086 "${ALLSKY_SCRIPTS}/generateForDay.sh" ${NICE_ARG} --silent --keogram "${DATE}" RET=$? echo -e "${ME}: ===== Keogram complete" - if [[ ${UPLOAD_KEOGRAM} == "true" && ${RET} = 0 ]] ; then + if [[ $( settings ".keogramupload" ) == "true" && ${RET} = 0 ]] ; then "${ALLSKY_SCRIPTS}/generateForDay.sh" --upload --keogram "${DATE}" fi fi # Generate startrails from collected images. -# Threshold set to 0.1 by default in config.sh to avoid stacking over-exposed images. -if [[ ${STARTRAILS} == "true" ]]; then +# Threshold set to 0.1 by default to avoid stacking over-exposed images. +if [[ $( settings ".startrailsgenerate" ) == "true" ]]; then echo -e "${ME}: ===== Generating Startrails" #shellcheck disable=SC2086 "${ALLSKY_SCRIPTS}/generateForDay.sh" ${NICE_ARG} --silent --startrails "${DATE}" RET=$? echo -e "${ME}: ===== Startrails complete" - if [[ ${UPLOAD_STARTRAILS} == "true" && ${RET} = 0 ]] ; then + if [[ $( settings ".startrailsupload" ) == "true" && ${RET} = 0 ]] ; then "${ALLSKY_SCRIPTS}/generateForDay.sh" --upload --startrails "${DATE}" fi fi @@ -80,26 +76,18 @@ fi # Generate timelapse from collected images. # Use generateForDay.sh instead of putting all the commands here so users can easily # test the timelapse creation, which sometimes has issues. -if [[ ${TIMELAPSE} == "true" ]]; then +if [[ $( settings ".timelapsegenerate" ) == "true" ]]; then echo -e "${ME}: ===== Generating Timelapse" #shellcheck disable=SC2086 "${ALLSKY_SCRIPTS}/generateForDay.sh" ${NICE_ARG} --silent --timelapse "${DATE}" RET=$? echo -e "${ME}: ===== Timelapse complete" - if [[ ${UPLOAD_VIDEO} == "true" && ${RET} = 0 ]] ; then + if [[ $( settings ".timelapseupload" ) == "true" && ${RET} = 0 ]] ; then "${ALLSKY_SCRIPTS}/generateForDay.sh" --upload --timelapse "${DATE}" fi fi -# Run custom script at the end of a night. This is run BEFORE the automatic deletion -# just in case you need to do something with the files before they are removed -# TODO: remove in next release. -CMD="${ALLSKY_SCRIPTS}/endOfNight_additionalSteps.sh" -[[ -x ${CMD} ]] && "${CMD}" - -DAYS_TO_KEEP=${DAYS_TO_KEEP:-0} # old versions allowed "" to disable -WEB_DAYS_TO_KEEP=${WEB_DAYS_TO_KEEP:-0} # old versions allowed "" to disable - +DAYS_TO_KEEP="$( settings ".daystokeep" )" # Automatically delete old images and videos. if [[ ${DAYS_TO_KEEP} -gt 0 ]]; then del=$(date --date="${DAYS_TO_KEEP} days ago" +%Y%m%d) @@ -116,16 +104,16 @@ if [[ ${DAYS_TO_KEEP} -gt 0 ]]; then done fi -# Automatically delete old LOCAL Website images and videos. +# Automatically delete old Website images and videos. -# TODO: work on remote Websites -if [[ ${WEB_DAYS_TO_KEEP} -gt 0 ]]; then +LOCAL_WEB_DAYS_TO_KEEP="$( settings ".daystokeeplocalwebsite" )" +if [[ ${LOCAL_WEB_DAYS_TO_KEEP} -gt 0 && $( settings ".uselocalwebsite" ) == "true" ]]; then if [[ ! -d ${ALLSKY_WEBSITE} ]]; then - echo -e "${ME}: ${YELLOW}WARNING: 'WEB_DAYS_TO_KEEP' set but no website found in '${ALLSKY_WEBSITE}!${NC}" - echo -e 'Set WEB_DAYS_TO_KEEP to ""' + echo -e "${ME}: ${YELLOW}WARNING: 'Days to Keep on Pi Website' set but no Website found in '${ALLSKY_WEBSITE}!${NC}" + echo -e 'Set "Days to Keep on Pi Website" to ""' else - del=$(date --date="${WEB_DAYS_TO_KEEP} days ago" +%Y%m%d) + del=$(date --date="${LOCAL_WEB_DAYS_TO_KEEP} days ago" +%Y%m%d) ( cd "${ALLSKY_WEBSITE}" || exit 1 NUM_DELETED=0 @@ -155,8 +143,16 @@ if [[ ${WEB_DAYS_TO_KEEP} -gt 0 ]]; then fi fi +REMOTE_WEB_DAYS_TO_KEEP="$( settings ".daystokeepremotewebsite" )" +if [[ ${REMOTE_WEB_DAYS_TO_KEEP} -gt 0 && $( settings ".useremotewebsite" ) == "true" ]]; then + # TODO: work on remote Websites. + # Possibly do a curl xxxx?keep=${REMOTE_WEB_DAYS_TO_KEEP} + # and pass something so it knows this is a valid request. + : +fi + SHOW_ON_MAP=$(settings ".showonmap") -if [[ ${SHOW_ON_MAP} -eq 1 ]]; then +if [[ ${SHOW_ON_MAP} == "true" ]]; then echo -e "${ME}: ===== Posting camera details to allsky map" "${ALLSKY_SCRIPTS}/postToMap.sh" --endofnight fi diff --git a/scripts/flow-runner.py b/scripts/flow-runner.py index c8070b813..13b5d3c6c 100755 --- a/scripts/flow-runner.py +++ b/scripts/flow-runner.py @@ -112,9 +112,10 @@ def signalHandler(sig, frame): shared.log(0, "ERROR: Unable to read SETTINGS_FILE - Aborting", exitCode=1) shared.fullFilename = os.environ["FULL_FILENAME"] - shared.createThumbnails = os.environ["IMG_CREATE_THUMBNAILS"] - shared.thumbnailWidth = int(os.environ["THUMBNAIL_SIZE_X"]) - shared.thumbnailHeight = int(os.environ["THUMBNAIL_SIZE_Y"]) + # TODO: imagecreatethumbnails is a boolean + shared.createThumbnails = bool(shared.getSetting("imagecreatethumbnails")) + shared.thumbnailWidth = int(shared.getSetting("thumbnailsizex")) + shared.thumbnailHeight = int(shared.getSetting("thumbnailsizey")) shared.websiteImageFile = os.path.join(shared.allskyTmp, shared.fullFilename) shared.TOD = shared.args.tod date = datetime.now() @@ -319,4 +320,4 @@ def signalHandler(sig, frame): shared.dbDeleteKey("flowtimer") if os.path.exists(flowTimingsFolder): - shutil.rmtree(flowTimingsFolder) \ No newline at end of file + shutil.rmtree(flowTimingsFolder) diff --git a/scripts/functions.sh b/scripts/functions.sh index d4ad80e08..e8802c36e 100644 --- a/scripts/functions.sh +++ b/scripts/functions.sh @@ -1,8 +1,7 @@ #!/bin/bash # Shell functions used by multiple scripts. -# This file is "source"d into others, and must be done AFTER source'ing variables.sh -# and config.sh. +# This file is "source"d into others, and must be done AFTER source'ing variables.sh. ##### @@ -39,8 +38,8 @@ function doExit() # even if the user has them turned off. if [[ -n ${CUSTOM_MESSAGE} ]]; then # Create a custom error message. - # If we error out before config.sh is sourced in, $FILENAME and $EXTENSION won't be - # set so guess at what they are. + # If we error out before variables.sh is sourced in, + # ${FILENAME} and ${EXTENSION} won't be set so guess at what they are. "${ALLSKY_SCRIPTS}/generate_notification_images.sh" --directory "${ALLSKY_TMP}" \ "${FILENAME:-"image"}" \ "${COLOR}" "" "85" "" "" \ @@ -57,8 +56,7 @@ function doExit() # Don't let the service restart us because we'll likely get the same error again. [[ ${EXITCODE} -ge ${EXIT_ERROR_STOP} ]] && sudo systemctl stop allsky - # shellcheck disable=SC2086 - exit ${EXITCODE} + exit "${EXITCODE}" } @@ -224,17 +222,15 @@ function get_sunrise_sunset() local ANGLE="${1}" local LATITUDE="${2}" local LONGITUDE="${3}" - #shellcheck disable=SC2086 source-path=. + #shellcheck source-path=. source "${ALLSKY_HOME}/variables.sh" || return 1 - #shellcheck disable=SC2086,SC1091 # file doesn't exist in GitHub - source "${ALLSKY_CONFIG}/config.sh" || return 1 - [[ -z ${ANGLE} ]] && ANGLE="$(settings ".angle")" - [[ -z ${LATITUDE} ]] && LATITUDE="$(settings ".latitude")" - [[ -z ${LONGITUDE} ]] && LONGITUDE="$(settings ".longitude")" + [[ -z ${ANGLE} ]] && ANGLE="$( settings ".angle" )" + [[ -z ${LATITUDE} ]] && LATITUDE="$( settings ".latitude" )" + [[ -z ${LONGITUDE} ]] && LONGITUDE="$( settings ".longitude" )" - LATITUDE="$(convertLatLong "${LATITUDE}" "latitude")" || return 2 - LONGITUDE="$(convertLatLong "${LONGITUDE}" "longitude")" || return 2 + LATITUDE="$( convertLatLong "${LATITUDE}" "latitude" )" || return 2 + LONGITUDE="$( convertLatLong "${LONGITUDE}" "longitude" )" || return 2 echo "Daytime start Nighttime start Angle" local X="$(sunwait list angle "0" "${LATITUDE}" "${LONGITUDE}")" @@ -249,47 +245,14 @@ function get_sunrise_sunset() # Return which Allsky Websites exist - local, remote, both, none function whatWebsites() { - #shellcheck disable=SC2086 source-path=. + #shellcheck source-path=. source "${ALLSKY_HOME}/variables.sh" || return 1 local HAS_LOCAL="false" local HAS_REMOTE="false" - # Determine local Website - this is easy. - [[ -f ${ALLSKY_WEBSITE_CONFIGURATION_FILE} ]] && HAS_LOCAL="true" - - # Determine remote Website - this is more involved. - # Not only must the file exist, but there also has to be a way to upload to it. - if [[ -f ${ALLSKY_REMOTE_WEBSITE_CONFIGURATION_FILE} ]]; then - local PROTOCOL="$(get_variable "PROTOCOL" "${ALLSKY_CONFIG}/ftp-settings.sh")" - PROTOCOL=${PROTOCOL,,} - if [[ -n ${PROTOCOL} && ${PROTOCOL} != "local" ]]; then - local X - case "${PROTOCOL}" in - "" | local) - ;; - - ftp | ftps | sftp | scp) # These require R - X="$(get_variable "REMOTE_HOST" "${ALLSKY_CONFIG}/ftp-settings.sh")" - [[ -n ${X} ]] && HAS_REMOTE="true" - ;; - - s3) - X="$(get_variable "AWS_CLI_DIR" "${ALLSKY_CONFIG}/ftp-settings.sh")" - [[ -n ${X} ]] && HAS_REMOTE="true" - ;; - - gcs) - X="$(get_variable "GCS_BUCKET" "${ALLSKY_CONFIG}/ftp-settings.sh")" - [[ -n ${X} ]] && HAS_REMOTE="true" - ;; - - *) - echo "ERROR: Unknown PROTOCOL: '${PROTOCOL}'" >&2 - ;; - esac - fi - fi + [[ "$( settings ".uselocalwebsite" )" == "true" ]] && HAS_LOCAL="true" + [[ "$( settings ".useremotewebsite" )" == "true" ]] && HAS_REMOTE="true" if [[ ${HAS_LOCAL} == "true" ]]; then if [[ ${HAS_REMOTE} == "true" ]]; then @@ -322,13 +285,13 @@ function checkAndGetNewerFile() local GIT_FILE="${GITHUB_RAW_ROOT}/allsky/${BRANCH}/${2}" local DOWNLOADED_FILE="${3}" # Download the file and put in DOWNLOADED_FILE - X="$(curl --show-error --silent "${GIT_FILE}")" + X="$( curl --show-error --silent "${GIT_FILE}" )" RET=$? if [[ ${RET} -eq 0 && ${X} != "404: Not Found" ]]; then # We really just check if the files are different. echo "${X}" > "${DOWNLOADED_FILE}" - DOWNLOADED_CHECKSUM="$(sum "${DOWNLOADED_FILE}")" - MY_CHECKSUM="$(sum "${CURRENT_FILE}")" + DOWNLOADED_CHECKSUM="$( sum "${DOWNLOADED_FILE}" )" + MY_CHECKSUM="$( sum "${CURRENT_FILE}" )" if [[ ${MY_CHECKSUM} == "${DOWNLOADED_CHECKSUM}" ]]; then rm -f "${DOWNLOADED_FILE}" return 0 @@ -372,10 +335,54 @@ function checkPixelValue() # variable name, variable value, width_or_height, res } +##### +# The crop rectangle needs to fit within the image and the numbers be even. +# TODO: should there be a maximum for any number (other than the image size)? +# Number of pixels to crop off top, right, bottom, left, plus max_resolution_x and max_resolution_y. +function checkCropValues() +{ + local CROP_TOP="${1}" + local CROP_RIGHT="${2}" + local CROP_BOTTOM="${3}" + local CROP_LEFT="${4}" + local MAX_RESOLUTION_X="${5}" + local MAX_RESOLUTION_Y="${6}" + + local ERR="" + if [[ ${CROP_TOP} -lt 0 || ${CROP_RIGHT} -lt 0 || + ${CROP_BOTTOM} -lt 0 || ${CROP_LEFT} -lt 0 ]]; then + ERR="${ERR}\nCrop numbers must all be positive." + fi + if [[ $((CROP_TOP % 2)) -eq 1 || $((CROP_RIGHT % 2)) -eq 1 || + $((CROP_BOTTOM % 2)) -eq 1 || $((CROP_LEFT % 2)) -eq 1 ]]; then + ERR="${ERR}\nCrop numbers must all be even." + fi + if [[ ${CROP_TOP} -gt $((MAX_RESOLUTION_Y -2)) ]]; then + ERR="${ERR}\nCropping on top (${CROP_TOP}) is larger than the image height (${MAX_RESOLUTION_Y})." + fi + if [[ ${CROP_RIGHT} -gt $((MAX_RESOLUTION_X - 2)) ]]; then + ERR="${ERR}\nCropping on right (${CROP_RIGHT}) is larger than the image width (${MAX_RESOLUTION_X})." + fi + if [[ ${CROP_BOTTOM} -gt $((MAX_RESOLUTION_Y - 2)) ]]; then + ERR="${ERR}\nCropping on bottom (${CROP_BOTTOM}) is larger than the image height (${MAX_RESOLUTION_Y})." + fi + if [[ ${CROP_LEFT} -gt $((MAX_RESOLUTION_X - 2)) ]]; then + ERR="${ERR}\nCropping on left (${CROP_LEFT}) is larger than the image width (${MAX_RESOLUTION_X})." + fi + + if [[ -z ${ERR} ]]; then + return 0 + else + echo -e "${ERR}" + return 1 + fi +} + ##### # The crop rectangle needs to fit within the image, be an even number, and be greater than 0. # x, y, offset_x, offset_y, max_resolution_x, max_resolution_y -function checkCropValues() +# TODO: remove this after testing. It's the old way of cropping. +function checkCropValuesOLD() { local X="${1}" local Y="${2}" @@ -422,21 +429,6 @@ function checkCropValues() fi } -##### -# Get a shell variable's value. The variable can have optional spaces and tabs before it. -# This function is useful when we can't "source" the file. -function get_variable() { - local VARIABLE="${1}" - local FILE="${2}" - local LINE="" - local SEARCH_STRING="^[ ]*${VARIABLE}=" - if ! LINE="$( /bin/grep -E "${SEARCH_STRING}" "${FILE}" 2>/dev/null )" ; then - return 1 - fi - - echo "${LINE}" | sed -e "s/${SEARCH_STRING}//" -e 's/"//g' - return 0 -} ##### # Simple way to get a setting that hides the details. @@ -455,6 +447,11 @@ function settings() fi local FILE="${2:-${SETTINGS_FILE}}" + if [[ ! -f ${FILE} ]]; then + echo "${M}: File '${FILE}' does not exist! Cannot get '${FIELD}'." >&2 + return 2 + fi + if j="$( jq -r "${FIELD}" "${FILE}" )" ; then [[ -z ${j} && ${DO_NULL} == "false" ]] && j="" echo "${j}" @@ -463,7 +460,7 @@ function settings() echo "${M}: Unable to get json value for '${FIELD}' in '${FILE}." >&2 - return 2 + return 3 } @@ -525,11 +522,11 @@ function check_settings_link() return "${EXIT_ERROR_STOP}" fi if [[ -z ${CAMERA_TYPE} ]]; then - CAMERA_TYPE="$( settings .cameraType "${FULL_FILE}" )" + CAMERA_TYPE="$( settings ".cameratype" "${FULL_FILE}" )" [[ $? -ne 0 || -z ${CAMERA_TYPE} ]] && return "${EXIT_ERROR_STOP}" fi if [[ -z ${CAMERA_MODEL} ]]; then - CAMERA_MODEL="$( settings .cameraModel "${FULL_FILE}" )" + CAMERA_MODEL="$( settings ".cameramodel" "${FULL_FILE}" )" [[ $? -ne 0 || -z ${CAMERA_TYPE} ]] && return "${EXIT_ERROR_STOP}" fi @@ -594,9 +591,13 @@ function update_json_file() # field, new value, file local NEW_VALUE="${2}" local FILE="${3:-${SETTINGS_FILE}}" + local TYPE="${4}" # optional + local DOUBLE_QUOTE='"' + [[ -n ${TYPE} && (${TYPE} == "number" || ${TYPE} == "boolean") ]] && DOUBLE_QUOTE="" + local TEMP="/tmp/$$" # Have to use "cp" instead of "mv" to keep any hard link. - if jq "${FIELD} = \"${NEW_VALUE}\"" "${FILE}" > "${TEMP}" ; then + if jq "${FIELD} = ${DOUBLE_QUOTE}${NEW_VALUE}${DOUBLE_QUOTE}" "${FILE}" > "${TEMP}" ; then cp "${TEMP}" "${FILE}" rm "${TEMP}" return 0 @@ -614,14 +615,14 @@ function one_instance() local SLEEP_TIME="5s" local MAX_CHECKS=3 local PID_FILE="" + local PID="" local ABORTED_FILE="" local ABORTED_FIELDS="" local ABORTED_MSG1="" local ABORTED_MSG2="" local CAUSED_BY="" - local P="" - OK="true" + local OK="true" local ERRORS="" while [[ $# -gt 0 ]]; do ARG="${1}" @@ -638,6 +639,10 @@ function one_instance() PID_FILE="${2}" shift ;; + --pid) + PID="${2}" + shift + ;; --aborted-count-file) ABORTED_FILE="${2}" shift @@ -685,7 +690,7 @@ function one_instance() ERRORS="${ERRORS}\nABORTED_MSG2 not specified." OK="false" fi - # CAUSED_BY isn't required + # CAUSED_BY and PID aren't required if [[ ${OK} == "false" ]]; then echo -e "${RED}${ME}: ERROR: ${ERRORS}.${NC}" >&2 @@ -693,24 +698,24 @@ function one_instance() fi - NUM_CHECKS=0 - while : ; do + local NUM_CHECKS=0 + while : ; + do [[ ! -f ${PID_FILE} ]] && break ((NUM_CHECKS++)) - PID=$( < "${PID_FILE}" ) - # Check that the process is still running. - P="$( ps -fp "${PID}" )" - [[ $? -ne 0 ]] && break; # not running - why is the file still here? + local CURRENT_PID=$( < "${PID_FILE}" ) + # Check that the process is still running. Looking in /proc is very quick. + [[ ! -d "/proc/${CURRENT_PID}" ]] && break - if [[ $NUM_CHECKS -eq ${MAX_CHECKS} ]]; then + if [[ ${NUM_CHECKS} -eq ${MAX_CHECKS} ]]; then echo -en "${YELLOW}" >&2 echo -e "${ABORTED_MSG1}" >&2 echo -n "Made ${NUM_CHECKS} attempts at waiting." >&2 echo -n " If this happens often, check your settings." >&2 echo -e "${NC}" >&2 - echo "${P}" >&2 + ps -fp "${CURRENT_PID}" >&2 # Keep track of aborts so user can be notified. # If it's happening often let the user know. @@ -737,7 +742,8 @@ function one_instance() fi done - echo $$ > "${PID_FILE}" || return 1 + [[ -z ${PID} ]] && PID="$$" + echo "${PID}" > "${PID_FILE}" || return 1 return 0 } @@ -750,8 +756,9 @@ function make_thumbnail() local SEC="${1}" local INPUT_FILE="${2}" local THUMBNAIL="${3}" + local THUMBNAIL_SIZE_X="$( settings ".thumbnailsizex" )" ffmpeg -loglevel error -ss "00:00:${SEC}" -i "${INPUT_FILE}" \ - -filter:v scale="${THUMBNAIL_SIZE_X:-100}:-1" -frames:v 1 "${THUMBNAIL}" + -filter:v scale="${THUMBNAIL_SIZE_X}:-1" -frames:v 1 "${THUMBNAIL}" } @@ -773,33 +780,82 @@ function reboot_needed() fi } + #### -# Read json on stdin and output each field and value separated by a tab. -function convert_json_to_tabs() +# Upload to the appropriate Websites and/or servers. +# Everything is put relative to the root directory. +# +# --local-web: copy to local website +# --remote-web: upload to remote website +# --remote-server: upload to remote server +function upload_all() { - # Possible input formats, all with and without trailing "," and - # with or without leading spaces or tabs. - # "field" : "value" - # "field" : number - # "field": "value" - # "field": number - # "field":"value" - # "field":number - # Want to output two fields (field name and value), separated by tabs. - # First get rid of the brackets, - # then the optional leading spaces and tabs, - # then everything between the field and and its value, - # then ending " and/or comma. - - local JSON_FILE="${1}" - if [[ ! -f ${JSON_FILE} ]]; then - echo -e "${RED}convert_json_to_tabs(): ERROR: json file '${JSON_FILE}' not found.${NC}" >&2 - return 1 + local ARGS="" + local LOCAL_WEB="false" + local REMOTE_WEB="false" + local REMOTE_SERVER="false" + while [[ ${1:0:2} == "--" ]] + do + if [[ ${1} == "--local-web" ]]; then + LOCAL_WEB="true" + elif [[ ${1} == "--remote-web" ]]; then + REMOTE_WEB="true" + elif [[ ${1} == "--remote-server" ]]; then + REMOTE_SERVER="true" + else + ARGS="${ARGS} ${1}" + fi + shift + done + if [[ ${LOCAL_WEB} == "false" && ${REMOTE_WEB} == "false" && ${REMOTE_SERVER} == "false" ]]; then + LOCAL_WEB="true" + REMOTE_WEB="true" + REMOTE_SERVER="true" + fi + + local UPLOAD_FILE="${1}" + local SUBDIR="${2}" + local DESTINATION_NAME="${3}" + local FILE_TYPE="${4}" # optional + local RET=0 + local ROOT REMOTE_DIR + if [[ ${LOCAL_WEB} == "true" && "$( settings ".uselocalwebsite" )" == "true" ]]; then + #shellcheck disable=SC2086 + "${ALLSKY_SCRIPTS}/upload.sh" ${ARGS} --local \ + "${UPLOAD_FILE}" "${ALLSKY_WEBSITE}/${SUBDIR}" "${DESTINATION_NAME}" + ((RET+=$?)) + fi + if [[ ${REMOTE_WEB} == "true" && "$( settings ".useremotewebsite" )" == "true" ]]; then + ROOT="$( settings ".remotewebsiteimagedir" )" + if [[ -z ${ROOT} ]]; then + REMOTE_DIR="${SUBDIR}" + else + REMOTE_DIR="${ROOT}/${SUBDIR}" + fi + #shellcheck disable=SC2086 + "${ALLSKY_SCRIPTS}/upload.sh" ${ARGS} --remote "web" \ + "${UPLOAD_FILE}" "${REMOTE_DIR}" "${DESTINATION_NAME}" "${FILE_TYPE}" + ((RET+=$?)) + fi + if [[ ${REMOTE_SERVER} == "true" && "$( settings ".useremoteserver" )" == "true" ]]; then + ROOT="$( settings ".remoteserverimagedir" )" + if [[ -z ${ROOT} ]]; then + REMOTE_DIR="${SUBDIR}" + else + REMOTE_DIR="${ROOT}/${SUBDIR}" + fi + #shellcheck disable=SC2086 + "${ALLSKY_SCRIPTS}/upload.sh" ${ARGS} --remote "server" \ + "${UPLOAD_FILE}" "${REMOTE_DIR}" "${DESTINATION_NAME}" "${FILE_TYPE}" + ((RET+=$?)) fi - sed -e '/^{/d' -e '/^}/d' \ - -e 's/^[\t ]*"//' \ - -e 's/"[\t :]*[ "]/\t/' \ - -e 's/",$//' -e 's/"$//' -e 's/,$//' \ - "${JSON_FILE}" + return "${RET}" +} + + +# Indent all lines. +function indent() +{ + echo -e "${1}" | sed 's/^/\t/' } diff --git a/scripts/generateForDay.sh b/scripts/generateForDay.sh index 4aff29e0b..87912b01d 100755 --- a/scripts/generateForDay.sh +++ b/scripts/generateForDay.sh @@ -3,15 +3,13 @@ # This script allows users to manually generate or upload keograms, startrails, and timelapses. # Allow this script to be executed manually, which requires several variables to be set. -[[ -z ${ALLSKY_HOME} ]] && export ALLSKY_HOME="$(realpath "$(dirname "${BASH_ARGV0}")/..")" -ME="$(basename "${BASH_ARGV0}")" +[[ -z ${ALLSKY_HOME} ]] && export ALLSKY_HOME="$( realpath "$( dirname "${BASH_ARGV0}" )/.." )" +ME="$( basename "${BASH_ARGV0}" )" -#shellcheck disable=SC2086 source-path=. -source "${ALLSKY_HOME}/variables.sh" || exit ${ALLSKY_ERROR_STOP} -#shellcheck disable=SC2086 source-path=scripts -source "${ALLSKY_SCRIPTS}/functions.sh" || exit ${ALLSKY_ERROR_STOP} -#shellcheck disable=SC2086,SC1091 # file doesn't exist in GitHub -source "${ALLSKY_CONFIG}/config.sh" || exit ${ALLSKY_ERROR_STOP} +#shellcheck source-path=. +source "${ALLSKY_HOME}/variables.sh" || exit "${ALLSKY_ERROR_STOP}" +#shellcheck source-path=scripts +source "${ALLSKY_SCRIPTS}/functions.sh" || exit "${ALLSKY_ERROR_STOP}" DO_HELP="false" DEBUG_ARG="" @@ -27,6 +25,7 @@ DO_STARTRAILS="false" DO_TIMELAPSE="false" THUMBNAIL_ONLY="false" THUMBNAIL_ONLY_ARG="" +IMAGES_FILE="" while [[ $# -gt 0 ]]; do ARG="${1}" @@ -56,6 +55,10 @@ while [[ $# -gt 0 ]]; do # On uploads, we should let upload.sh output messages since it has more details. UPLOAD_SILENT="" ;; + --images) + IMAGES_FILE="${2}" + shift + ;; -k | --keogram) DO_KEOGRAM="true" ((GOT++)) @@ -82,40 +85,84 @@ done usage_and_exit() { - retcode=${1} + local RET=${1} echo - [[ ${retcode} -ne 0 ]] && echo -en "${RED}" + [[ ${RET} -ne 0 ]] && echo -en "${RED}" echo "Usage: ${ME} [--help] [--silent] [--debug] [--nice n] [--upload] \\" - echo " [--thumbnail-only] [--keogram] [--startrails] [--timelapse] DATE" - [[ ${retcode} -ne 0 ]] && echo -en "${NC}" + echo " [--thumbnail-only] [--keogram] [--startrails] [--timelapse] \\" + echo " {--images file | }" + [[ ${RET} -ne 0 ]] && echo -en "${NC}" echo " where:" echo " '--help' displays this message and exits." echo " '--debug' runs upload.sh in debug mode." echo " '--nice' runs with nice level n." echo " '--upload' uploads previously-created files instead of creating them." echo " '--thumbnail-only' creates or uploads video thumbnails only." - echo " 'DATE' is the day in '${ALLSKY_IMAGES}' to process." + echo " 'INPUT_DIR' is the day in '${ALLSKY_IMAGES}' to process." echo " '--keogram' will ${MSG1} a keogram." echo " '--startrails' will ${MSG1} a startrail." echo " '--timelapse' will ${MSG1} a timelapse." echo " If you don't specify --keogram, --startrails, or --timelapse, all three will be ${MSG2}." - # shellcheck disable=SC2086 - exit ${retcode} + echo + echo "The list of images is determined in one of two ways:" + echo "1. Looking in '' for files with an extension of '${EXTENSION}'." + echo " If is NOT a full path name it is assumed to be in '${ALLSKY_IMAGES}'," + echo " which allows using images on a USB stick, for example." + echo " The output file(s) are stored in ." + echo + echo "2. Specifying '--images file' uses the images listed in 'file'; is not used." + echo " The output file is stored in the same directory as the first image." + exit "${RET}" } [[ ${DO_HELP} == "true" ]] && usage_and_exit 0 -[[ $# -eq 0 ]] && usage_and_exit 1 -if [[ ${TYPE} == "UPLOAD" ]]; then - #shellcheck disable=SC2086,SC1091 # file doesn't exist in GitHub - source "${ALLSKY_CONFIG}/ftp-settings.sh" || exit ${ALLSKY_ERROR_STOP} +if [[ -n ${IMAGES_FILE} ]]; then + # If IMAGES_FILE is specified there should be no other arguments. + [[ $# -ne 0 ]] && usage_and_exit 1 +elif [[ $# -eq 0 || $# -gt 1 ]]; then + usage_and_exit 2 fi -DATE="${1}" -OUTPUT_DIR="${ALLSKY_IMAGES}/${DATE}" -if [[ ! -d ${OUTPUT_DIR} ]]; then - echo -e "${RED}${ME}: ERROR: '${OUTPUT_DIR}' not found!${NC}" - exit 2 +if [[ -n ${IMAGES_FILE} ]]; then + if [[ ! -s ${IMAGES_FILE} ]]; then + echo -e "${RED}*** ${ME} ERROR: '${IMAGES_FILE}' does not exist or is empty!${NC}" + exit 3 + fi + INPUT_DIR="" # Not used + + # Use the directory the images are in. Only look at the first one. + I="$( head -1 "${IMAGES_FILE}" )" + OUTPUT_DIR="$( dirname "${I}" )" + + # In case the filename doesn't include a path, put in a default location. + if [[ ${OUTPUT_DIR} == "." ]]; then + OUTPUT_DIR="${ALLSKY_TMP}" + echo -en "${ME}: ${YELLOW}" + echo "Can't determine where to put files so putting in '${OUTPUT_DIR}'." + echo -e "${NC}" + fi + + # Use the basename of the directory. + DATE="$( basename "${OUTPUT_DIR}" )" + +else + INPUT_DIR="${1}" + + # If not a full pathname, ${DIRNAME} will be "." so look in ${ALLSKY_IMAGES}. + DIRNAME="$( dirname "${INPUT_DIR}" )" + if [[ ${DIRNAME} == "." ]]; then + DATE="${INPUT_DIR}" + INPUT_DIR="${ALLSKY_IMAGES}/${INPUT_DIR}" # Need full pathname for links + else + DATE="$( basename "${INPUT_DIR}" )" + fi + if [[ ! -d ${INPUT_DIR} ]]; then + echo -e "${RED}*** ${ME} ERROR: '${INPUT_DIR}' does not exist!${NC}" + exit 4 + fi + + OUTPUT_DIR="${INPUT_DIR}" # Put output file(s) in same location as input files. fi if [[ ${GOT} -eq 0 ]]; then @@ -154,17 +201,17 @@ else DIRECTORY="${3}" DESTINATION_NAME="${4}" OVERRIDE_DESTINATION_NAME="${5}" # optional - WEB_DIRECTORY="${6}" # optional if [[ -f ${UPLOAD_FILE} ]]; then # If the user specified a different name for the destination file, use it. if [[ ${OVERRIDE_DESTINATION_NAME} != "" ]]; then DESTINATION_NAME="${OVERRIDE_DESTINATION_NAME}" fi - [[ ${SILENT} == "false" ]] && echo "===== Uploading '${UPLOAD_FILE}'" + [[ ${SILENT} == "false" ]] && echo "===== Uploading '${UPLOAD_FILE}' to '${DIRECTORY}'." + # shellcheck disable=SC2086 - "${ALLSKY_SCRIPTS}/upload.sh" ${UPLOAD_SILENT} ${DEBUG_ARG} \ + upload_all ${UPLOAD_SILENT} ${DEBUG_ARG} \ "${UPLOAD_FILE}" "${DIRECTORY}" "${DESTINATION_NAME}" \ - "${FILE_TYPE}" "${WEB_DIRECTORY}" + "${FILE_TYPE}" return $? else echo -en "${YELLOW}" @@ -184,7 +231,7 @@ if [[ ${DO_KEOGRAM} == "true" || ${DO_STARTRAILS} == "true" ]]; then # a non-empty string (eg. IMGSIZE="1280x960") will be produced and later # parts of this script so startrail and keogram generation can use it # to reject incorrectly-sized images. - IMGSIZE=$(settings 'if .width != null and .height != null and .width != "0" and .height != "0" and .width != 0 and .height != 0 then "\(.width)x\(.height)" else empty end') + IMGSIZE=$( settings 'if .width != null and .height != null and .width != "0" and .height != "0" and .width != 0 and .height != 0 then "\(.width)x\(.height)" else empty end' ) if [[ ${IMGSIZE} != "" ]]; then SIZE_FILTER="-s ${IMGSIZE//\"}" else @@ -197,17 +244,29 @@ if [[ ${DO_KEOGRAM} == "true" ]]; then KEOGRAM_FILE="keogram-${DATE}.${EXTENSION}" UPLOAD_FILE="${OUTPUT_DIR}/keogram/${KEOGRAM_FILE}" if [[ ${TYPE} == "GENERATE" ]]; then - if [[ -z "${NICE}" ]]; then + if [[ -z ${NICE} ]]; then N="" else N="--nice-level ${NICE}" fi + KEOGRAM_EXTRA_PARAMETERS="$( settings ".keogramextraparameters" )" + MORE="" + EXPAND="$( settings ".keogramexpand" )" + [[ ${EXPAND} == "true" ]] && MORE="${MORE} --image-expand" + NAME="$( settings ".keogramfontname" )" + [[ ${NAME} != "" ]] && MORE="${MORE} --font-name ${NAME}" + COLOR="$( settings ".keogramfontcolor" )" + [[ ${COLOR} != "" ]] && MORE="${MORE} --font-color '${COLOR}'" + SIZE="$( settings ".keogramfontsize" )" + [[ ${SIZE} != "" ]] && MORE="${MORE} --font-size ${SIZE}" + THICKNESS="$( settings ".keogramlinethickness" )" + [[ ${THICKNESS} != "" ]] && MORE="${MORE} --font-type ${THICKNESS}" CMD="'${ALLSKY_BIN}/keogram' ${N} ${SIZE_FILTER} -d '${OUTPUT_DIR}' \ - -e ${EXTENSION} -o '${UPLOAD_FILE}' ${KEOGRAM_EXTRA_PARAMETERS}" + -e ${EXTENSION} -o '${UPLOAD_FILE}' ${MORE} ${KEOGRAM_EXTRA_PARAMETERS}" generate "Keogram" "keogram" "${CMD}" else - upload "Keogram" "${UPLOAD_FILE}" "${KEOGRAM_DIR}" "${KEOGRAM_FILE}" \ - "${KEOGRAM_DESTINATION_NAME}" "${WEB_KEOGRAM_DIR}" + upload "Keogram" "${UPLOAD_FILE}" "keograms" "${KEOGRAM_FILE}" \ + "${KEOGRAM_DESTINATION_NAME}" fi [[ $? -ne 0 ]] && ((EXIT_CODE++)) fi @@ -216,18 +275,20 @@ if [[ ${DO_STARTRAILS} == "true" ]]; then STARTRAILS_FILE="startrails-${DATE}.${EXTENSION}" UPLOAD_FILE="${OUTPUT_DIR}/startrails/${STARTRAILS_FILE}" if [[ ${TYPE} == "GENERATE" ]]; then - if [[ -z "${NICE}" ]]; then + if [[ -z ${NICE} ]]; then N="" else N="--nice ${NICE}" fi + BRIGHTNESS_THRESHOLD="$( settings ".startrailsbrightnessthreshold" )" + STARTRAILS_EXTRA_PARAMETERS="$( settings ".startrailsextraparameters" )" CMD="'${ALLSKY_BIN}/startrails' ${N} ${SIZE_FILTER} -d '${OUTPUT_DIR}' \ -e ${EXTENSION} -b ${BRIGHTNESS_THRESHOLD} -o '${UPLOAD_FILE}' \ ${STARTRAILS_EXTRA_PARAMETERS}" generate "Startrails, threshold=${BRIGHTNESS_THRESHOLD}" "startrails" "${CMD}" else - upload "Startrails" "${UPLOAD_FILE}" "${STARTRAILS_DIR}" "${STARTRAILS_FILE}" \ - "${STARTRAILS_DESTINATION_NAME}" "${WEB_STARTRAILS_DIR}" + upload "Startrails" "${UPLOAD_FILE}" "startrails" "${STARTRAILS_FILE}" \ + "${STARTRAILS_DESTINATION_NAME}" fi [[ $? -ne 0 ]] && ((EXIT_CODE++)) fi @@ -241,6 +302,7 @@ if [[ ${DO_TIMELAPSE} == "true" ]]; then UPLOAD_THUMBNAIL="${OUTPUT_DIR}/${THUMBNAIL_FILE}" UPLOAD_FILE="${OUTPUT_DIR}/${VIDEOS_FILE}" + TIMELAPSE_UPLOAD_THUMBNAIL="$( settings ".timelapseuploadthumbnail" )" if [[ ${TYPE} == "GENERATE" ]]; then if [[ ${THUMBNAIL_ONLY} == "true" ]]; then if [[ -f ${UPLOAD_FILE} ]]; then @@ -251,7 +313,7 @@ if [[ ${DO_TIMELAPSE} == "true" ]]; then RET=1 fi else - if [[ -z "${NICE}" ]]; then + if [[ -z ${NICE} ]]; then N="" else N="nice -n ${NICE}" @@ -277,18 +339,13 @@ if [[ ${DO_TIMELAPSE} == "true" ]]; then if [[ ${THUMBNAIL_ONLY} == "true" ]]; then RET=0 else - upload "Timelapse" "${UPLOAD_FILE}" "${VIDEOS_DIR}" "${VIDEOS_FILE}" \ - "${VIDEOS_DESTINATION_NAME}" "${WEB_VIDEOS_DIR}" + upload "Timelapse" "${UPLOAD_FILE}" "videos" "${VIDEOS_FILE}" \ + "${VIDEOS_DESTINATION_NAME}" RET=$? fi if [[ ${RET} -eq 0 && ${TIMELAPSE_UPLOAD_THUMBNAIL} == "true" && -f ${UPLOAD_THUMBNAIL} ]]; then - if [[ -n ${WEB_VIDEOS_DIR} ]]; then - W="${WEB_VIDEOS_DIR}/thumbnails" - else - W="" - fi - upload "TimelapseThumbnail" "${UPLOAD_THUMBNAIL}" "${VIDEOS_DIR}/thumbnails" \ - "${UPLOAD_THUMBNAIL_NAME}" "" "${W}" + upload "TimelapseThumbnail" "${UPLOAD_THUMBNAIL}" "videos/thumbnails" \ + "${UPLOAD_THUMBNAIL_NAME}" "" fi fi [[ ${RET} -ne 0 ]] && ((EXIT_CODE++)) @@ -306,5 +363,4 @@ if [[ ${TYPE} == "GENERATE" && ${SILENT} == "false" && ${EXIT_CODE} -eq 0 ]]; th echo "================" fi -#shellcheck disable=SC2086 -exit ${EXIT_CODE} +exit "${EXIT_CODE}" diff --git a/scripts/generate_notification_images.sh b/scripts/generate_notification_images.sh index c6625279d..f31c85647 100755 --- a/scripts/generate_notification_images.sh +++ b/scripts/generate_notification_images.sh @@ -6,11 +6,11 @@ # This is quick - on a Pi 4 it takes about one second per image. # Allow this script to be executed manually, which requires several variables to be set. -[[ -z ${ALLSKY_HOME} ]] && export ALLSKY_HOME="$(realpath "$(dirname "${BASH_ARGV0}")/..")" -ME="$(basename "${BASH_ARGV0}")" +[[ -z ${ALLSKY_HOME} ]] && export ALLSKY_HOME="$( realpath "$( dirname "${BASH_ARGV0}" )/.." )" +ME="$( basename "${BASH_ARGV0}" )" -#shellcheck disable=SC2086 source-path=. -source "${ALLSKY_HOME}/variables.sh" || exit ${ALLSKY_ERROR_STOP} +#shellcheck source-path=. +source "${ALLSKY_HOME}/variables.sh" || exit "${ALLSKY_ERROR_STOP}" readonly ALL_EXTS="jpg png" # all the image filename extensions we support @@ -35,8 +35,7 @@ function usage_and_exit() echo " '--size XxY' creates images that are X by Y pixels. Default: ${DEFAULT_IMAGE_SIZE} pixels." echo ) >&2 - # shellcheck disable=SC2086 - exit ${RET} + exit "${RET}" } # Check arguments @@ -159,8 +158,7 @@ function make_image() done } -which mogrify > /dev/null -if [[ $? -ne 0 ]]; then +if ! which mogrify > /dev/null ; then # Testing for mogrify which seems like a much more distinctive executable # name than "convert". I assume that if "mogrify" is in the path, then # ImageMagick is installed and "convert" will run ImageMagick and not some diff --git a/scripts/installUpgradeFunctions.sh b/scripts/installUpgradeFunctions.sh index 90a2135b8..385808627 100644 --- a/scripts/installUpgradeFunctions.sh +++ b/scripts/installUpgradeFunctions.sh @@ -37,7 +37,7 @@ function get_Git_version() { local BRANCH="${1}" local PACKAGE="${2}" local VF="$( basename "${ALLSKY_VERSION_FILE}" )" - local V="$(curl --show-error --silent "${GITHUB_RAW_ROOT}/${PACKAGE}/${BRANCH}/${VF}" | tr -d '\n\r')" + local V="$( curl --show-error --silent "${GITHUB_RAW_ROOT}/${PACKAGE}/${BRANCH}/${VF}" | tr -d '\n\r' )" # "404" means the branch isn't found since all new branches have a version file. [[ ${V} != "404: Not Found" ]] && echo -n "${V}" } @@ -55,7 +55,7 @@ function get_version() { if [[ -z ${F} ]]; then F="${ALLSKY_VERSION_FILE}" # default else - [[ ${F:1,-1} == "/" ]] && F="${F}$(basename "${ALLSKY_VERSION_FILE}")" + [[ ${F:1,-1} == "/" ]] && F="${F}$( basename "${ALLSKY_VERSION_FILE}" )" fi if [[ -f ${F} ]]; then # Sometimes the branch file will have both "master" and "dev" on two lines. @@ -73,6 +73,23 @@ function get_branch() { } +##### +# Get a shell variable's value. The variable can have optional spaces and tabs before it. +# This function is useful when we can't "source" the file. +function get_variable() { + local VARIABLE="${1}" + local FILE="${2}" + local LINE="" + local SEARCH_STRING="^[ ]*${VARIABLE}=" + if ! LINE="$( /bin/grep -E "${SEARCH_STRING}" "${FILE}" 2>/dev/null )" ; then + return 1 + fi + + echo "${LINE}" | sed -e "s/${SEARCH_STRING}//" -e 's/"//g' + return 0 +} + + ##### # Display a message of various types in appropriate colors. # Used primarily in installation scripts. @@ -186,6 +203,113 @@ function display_msg() } +# The various upload protocols need different variables defined. +# For the specified protocol, make sure the specified variable is defined. +function check_PROTOCOL() +{ + local P="${1}" # Protocol + local V="${2}" # Variable + local N="${3}" # Name + local VALUE="$( settings ".${V}" "${ALLSKY_ENV}" )" + if [[ -z ${VALUE} ]]; then + echo "${N} Protocol (${P}) set but not '${V}'." + echo "Uploads will not work until this is fixed." + return 1 + fi + return 0 +} +# Check variables are correct for a remote server. +# Return 0 for OK, 1 for warning, 2 for error. +function check_remote_server() +{ + check_for_env_file || return 1 + + local TYPE="${1}" + local TYPE_STRING + if [[ ${TYPE} == "REMOTEWEBSITE" ]]; then + TYPE_STRING="Remote Website" + else + TYPE_STRING="Remote Server" + fi + + local USE="$( settings ".use${TYPE,,}" )" + if [[ ${USE} != "true" ]]; then + # Variables should be empty. + x="$( grep -E -v "^#|^$" "${ALLSKY_ENV}" | grep "${TYPE}" | grep -E -v "${TYPE}.*=\"\"|${TYPE}.*=$" )" + if [[ -n "${x}" ]]; then + echo "${TYPE_STRING} is not being used but settings for it exist in '${ALLSKY_ENV}:" + indent "${x}" | sed "s/${TYPE}.*=.*/${TYPE}/" + return 1 + fi + return 0 + fi + + local RET=0 + local PROTOCOL="$( settings ".${TYPE,,}protocol" )" + case "${PROTOCOL}" in + "") + echo "${TYPE_STRING} is being used but has no Protocol." + echo "Uploads to it will not work." + return 2 + ;; + + ftp | ftps | sftp) + check_PROTOCOL "${PROTOCOL}" "${TYPE}_HOST" "${TYPE_STRING}" || RET=1 + check_PROTOCOL "${PROTOCOL}" "${TYPE}_USER" "${TYPE_STRING}" || RET=1 + check_PROTOCOL "${PROTOCOL}" "${TYPE}_PASSWORD" "${TYPE_STRING}" || RET=1 + if [[ ${PROTOCOL} == "ftp" ]]; then + echo "${TYPE_STRING} Protocol set to insecure 'ftp'." + echo "Try using 'ftps' or 'sftp' instead." + RET=1 + fi + ;; + + scp) + check_PROTOCOL "${PROTOCOL}" "${TYPE}_HOST" "${TYPE_STRING}" || RET=1 + check_PROTOCOL "${PROTOCOL}" "${TYPE}_USER" "${TYPE_STRING}" || RET=1 + if check_PROTOCOL "${PROTOCOL}" "${TYPE}_SSH_KEY_FILE" "${TYPE_STRING}" \ + && [[ ! -e ${SSH_KEY_FILE} ]]; then + echo "${TYPE_STRING} Protocol (${PROTOCOL}) set but '${TYPE}_SSH_KEY_FILE' (${SSH_KEY_FILE}) does not exist." + echo "Uploads will not work." + RET=1 + fi + ;; + + s3) + if check_PROTOCOL "${PROTOCOL}" "${TYPE}_AWS_CLI_DIR" "${TYPE_STRING}" \ + && [[ ! -e ${AWS_CLI_DIR} ]]; then + echo "${TYPE_STRING} Protocol (${PROTOCOL}) set but '${TYPE}_AWS_CLI_DIR' (${AWS_CLI_DIR}) does not exist." + echo "Uploads will not work." + RET=1 + fi + check_PROTOCOL "${PROTOCOL}" "${TYPE}_S3_BUCKET" "${TYPE_STRING}" || RET=1 + check_PROTOCOL "${PROTOCOL}" "${TYPE}_S3_ACL" "${TYPE_STRING}" || RET=1 + ;; + + gcs) + check_PROTOCOL "${PROTOCOL}" "${TYPE}_GCS_BUCKET" "${TYPE_STRING}" || RET=1 + check_PROTOCOL "${PROTOCOL}" "${TYPE}_GCS_ACL" "${TYPE_STRING}" || RET=1 + ;; + + *) + echo "${TYPE_STRING} Protocol (${PROTOCOL}) not blank or one of: ftp, ftps, sftp, scp, s3, gcs." + echo "Uploads will not work until this is corrected." + RET=1 + ;; + esac + + REMOTE_PORT="$( get_variable "${TYPE}_PORT" "${ALLSKY_ENV}" )" + if [[ -n ${REMOTE_PORT} ]] && ! is_number "${REMOTE_PORT}" ; then + echo "${TYPE}_PORT (${REMOTE_PORT}) must be a number." + echo "Uploads will not work until this is corrected." + RET=1 + fi + + return "${RET}" +} + + + ######################################### variables diff --git a/scripts/makeChanges.sh b/scripts/makeChanges.sh index 8f547003b..06ea85d7b 100755 --- a/scripts/makeChanges.sh +++ b/scripts/makeChanges.sh @@ -1,23 +1,13 @@ #!/bin/bash # Allow this script to be executed manually, which requires several variables to be set. -[[ -z ${ALLSKY_HOME} ]] && export ALLSKY_HOME="$(realpath "$(dirname "${BASH_ARGV0}")/..")" -ME="$(basename "${BASH_ARGV0}")" +[[ -z ${ALLSKY_HOME} ]] && export ALLSKY_HOME="$( realpath "$( dirname "${BASH_ARGV0}" )/.." )" +ME="$( basename "${BASH_ARGV0}" )" -#shellcheck disable=SC2086 source-path=. -source "${ALLSKY_HOME}/variables.sh" || exit ${ALLSKY_ERROR_STOP} -#shellcheck disable=SC2086 source-path=scripts -source "${ALLSKY_SCRIPTS}/functions.sh" || exit ${ALLSKY_ERROR_STOP} - -# This script may be called during installation BEFORE there is a settings file. -# config.sh looks for the file and produces an error if it doesn't exist, -# so only include these two files if there IS a settings file. -if [[ -f ${SETTINGS_FILE} ]]; then - #shellcheck disable=SC2086,SC1091 # file doesn't exist in GitHub - source "${ALLSKY_CONFIG}/config.sh" || exit ${ALLSKY_ERROR_STOP} - #shellcheck disable=SC2086,SC1091 # file doesn't exist in GitHub - source "${ALLSKY_CONFIG}/ftp-settings.sh" || exit ${ALLSKY_ERROR_STOP} -fi +#shellcheck source-path=. +source "${ALLSKY_HOME}/variables.sh" || exit "${ALLSKY_ERROR_STOP}" +#shellcheck source-path=scripts +source "${ALLSKY_SCRIPTS}/functions.sh" || exit "${ALLSKY_ERROR_STOP}" function usage_and_exit() { @@ -27,8 +17,7 @@ function usage_and_exit() echo -e "${wNC}" echo "There must be a multiple of 4 key/label/old_value/new_value arguments" echo "unless the --optionsOnly argument is given." - # shellcheck disable=SC2086 - exit ${1} + exit "${1}" } # Check arguments @@ -82,22 +71,12 @@ if [[ ${OPTIONS_FILE_ONLY} == "false" ]]; then [[ $(($# % 4)) -ne 0 ]] && usage_and_exit 2 fi -if [[ ${ON_TTY} -eq 0 ]]; then # called from WebUI. +if [[ ${ON_TTY} == "false" ]]; then # called from WebUI. ERROR_PREFIX="" else ERROR_PREFIX="${ME}: " fi -# This output may go to a web page, so use "w" colors. -# shell check doesn't realize there were set in variables.sh -wOK="${wOK}" -wWARNING="${wWARNING}" -wERROR="${wERROR}" -wDEBUG="${wDEBUG}" -wBOLD="${wBOLD}" -wNBOLD="${wNBOLD}" -wNC="${wNC}" - # Does the change need Allsky to be restarted in order to take affect? NEEDS_RESTART="false" @@ -111,6 +90,10 @@ SHOW_POSTDATA_MESSAGE="true" TWILIGHT_DATA_CHANGED="false" CAMERA_TYPE_CHANGED="false" GOT_WARNING="false" +CHECK_REMOTE_WEBSITE_ACCESS="false" +CHECK_REMOTE_SERVER_ACCESS="false" +USE_REMOTE_WEBSITE="" +USE_REMOTE_SERVER="" SHOW_ON_MAP="" # Several of the fields are in the Allsky Website configuration file, @@ -119,8 +102,7 @@ SHOW_ON_MAP="" # The first time we're called, set ${WEBSITES} function check_website() { - # shellcheck disable=SC2086 - [[ -n ${HAS_WEBSITE_RET} ]] && return ${HAS_WEBSITE_RET} # already checked + [[ -n ${HAS_WEBSITE_RET} ]] && return "${HAS_WEBSITE_RET}" # already checked WEBSITES="$(whatWebsites)" if [[ ${WEBSITES} == "local" || ${WEBSITES} == "both" ]]; then @@ -133,12 +115,16 @@ function check_website() WEB_CONFIG_FILE="" HAS_WEBSITE_RET=1 fi - # shellcheck disable=SC2086 - return ${HAS_WEBSITE_RET} + return "${HAS_WEBSITE_RET}" } -check_website # invoke to set variables +if [[ -f ${SETTINGS_FILE} ]]; then + # check_website requies the settings file to exist. + # If it doesn't we are likely called from the install script before the file is created. + check_website # invoke to set variables +fi CAMERA_NUMBER="" +NUM_CHANGED=0 while [[ $# -gt 0 ]] do @@ -146,30 +132,41 @@ do LABEL="${2}" OLD_VALUE="${3}" NEW_VALUE="${4}" + if [[ ${DEBUG} == "true" ]]; then MSG="${KEY}: Old=[${OLD_VALUE}], New=[${NEW_VALUE}]" echo -e "${wDEBUG}${ME}: ${MSG}${wNC}" - if [[ ${ON_TTY} -eq 0 ]]; then # called from WebUI. + if [[ ${ON_TTY} == "false" ]]; then # called from WebUI. echo -e "" fi fi + KEY="${KEY,,}" # convert to lowercase + KEY="${KEY/#_/}" # Remove any leading "_" + + # Don't skip if it's cameratype since that indicates we need to refresh. + if [[ ${KEY} != "cameratype" && ${OLD_VALUE} == "${NEW_VALUE}" ]]; then + [[ ${DEBUG} == "true" ]] && echo -e " ${wDEBUG}Skipping - old and new are equal${wNC}" + shift 4 + continue + fi + # Unfortunately, the Allsky configuration file was already updated, # so if we find a bad entry, e.g., a file doesn't exist, all we can do is warn the user. - K="${KEY,,}" # convert to lowercase - case "${K}" in + ((NUM_CHANGED++)) + case "${KEY}" in - cameranumber | cameratype) + "cameranumber" | "cameratype") - if [[ ${K} == "cameranumber" ]]; then + if [[ ${KEY} == "cameranumber" ]]; then NEW_CAMERA_NUMBER="${NEW_VALUE}" CAMERA_NUMBER=" -cameraNumber ${NEW_CAMERA_NUMBER}" # Set NEW_VALUE to the current Camera Type - NEW_VALUE="$( settings .cameraType )" + NEW_VALUE="$( settings ".cameratype" )" MSG="Re-creating files for cameraType ${NEW_VALUE}, cameraNumber ${NEW_CAMERA_NUMBER}" - if [[ ${ON_TTY} -eq 0 ]]; then # called from WebUI. + if [[ ${ON_TTY} == "false" ]]; then # called from WebUI. echo -e "" elif [[ ${DEBUG} == "true" ]]; then echo -e "${wDEBUG}${MSG}${wNC}" @@ -179,8 +176,7 @@ do if [[ ! -e "${ALLSKY_BIN}/capture_${NEW_VALUE}" ]]; then MSG="Unknown Camera Type: '${NEW_VALUE}'." echo -e "${wERROR}${ERROR_PREFIX}ERROR: ${MSG}${wNC}" - # shellcheck disable=SC2086 - exit ${EXIT_NO_CAMERA} + exit "${EXIT_NO_CAMERA}" fi # This requires Allsky to be stopped so we don't @@ -201,8 +197,7 @@ do RET=$? if [[ ${RET} -ne 0 ]] ; then echo -e "${wERROR}${ERROR_PREFIX}ERROR: ${C}.${wNC}" - # shellcheck disable=SC2086 - exit ${RET} + exit "${RET}" fi C=" -cmd ${C}" else @@ -232,8 +227,7 @@ do # Restore prior cc file if there was one. [[ -f ${CC_FILE_OLD} ]] && mv "${CC_FILE_OLD}" "${CC_FILE}" - # shellcheck disable=SC2086 - exit ${RET} # the actual exit code is important + exit "${RET}" # the actual exit code is important fi # Create a link to a file that contains the camera type and model in the name. @@ -245,10 +239,10 @@ do exit 1 fi - # ${CC_FILE} is a generic name defined in config.sh. + # ${CC_FILE} is a generic name defined in variables.sh. # ${SPECIFIC_NAME} is specific to the camera type/model. # It isn't really needed except debugging. - CC="$(basename "${CC_FILE}")" + CC="$( basename "${CC_FILE}" )" CC_EXT="${CC##*.}" # after "." CC_NAME="${CC%.*}" # before "." SPECIFIC_NAME="${ALLSKY_CONFIG}/${CC_NAME}_${CAMERA_TYPE}_${CAMERA_MODEL}.${CC_EXT}" @@ -257,12 +251,6 @@ do # adds or changes capabilities, so delete the old one just in case. ln --force "${CC_FILE}" "${SPECIFIC_NAME}" - if ! sed -i -e "s/^CAMERA_TYPE=.*$/CAMERA_TYPE=\"${NEW_VALUE}\"/" "${ALLSKY_CONFIG}/config.sh"; then - echo -e "${wERROR}ERROR updating ${wBOLD}${LABEL}${wNBOLD}.${wNC}" - [[ -f ${CC_FILE_OLD} ]] && mv "${CC_FILE_OLD}" "${CC_FILE}" - exit 1 - fi - # The old file is no longer needed. rm -f "${CC_FILE_OLD}" @@ -358,12 +346,12 @@ do NEEDS_RESTART="true" ;; - filename) + "filename") check_website && WEBSITE_CONFIG+=("config.imageName" "${LABEL}" "${NEW_VALUE}") NEEDS_RESTART="true" ;; - extratext) + "extratext") # It's possible the user will create/populate the file while Allsky is running, # so it's not an error if the file doesn't exist or is empty. if [[ -n ${NEW_VALUE} ]]; then @@ -376,9 +364,9 @@ do NEEDS_RESTART="true" ;; - latitude | longitude) + "latitude" | "longitude") # Allow either +/- decimal numbers, OR numbers with N, S, E, W, but not both. - if NEW_VALUE="$(convertLatLong "${NEW_VALUE}" "${KEY}")" ; then + if NEW_VALUE="$( convertLatLong "${NEW_VALUE}" "${KEY}" )" ; then check_website && WEBSITE_CONFIG+=(config."${KEY}" "${LABEL}" "${NEW_VALUE}") else echo -e "${wWARNING}WARNING: ${NEW_VALUE}.${wNC}" @@ -387,17 +375,17 @@ do TWILIGHT_DATA_CHANGED="true" ;; - angle) + "angle") NEEDS_RESTART="true" TWILIGHT_DATA_CHANGED="true" ;; - takedaytimeimages) + "takedaytimeimages") NEEDS_RESTART="true" TWILIGHT_DATA_CHANGED="true" ;; - config) + "config") if [[ ${NEW_VALUE} == "" ]]; then NEW_VALUE="[none]" elif [[ ${NEW_VALUE} != "[none]" ]]; then @@ -409,24 +397,20 @@ do fi ;; - daytuningfile | nighttuningfile) + "daytuningfile" | "nighttuningfile") if [[ -n ${NEW_VALUE} && ! -f ${NEW_VALUE} ]]; then echo -e "${wWARNING}WARNING: Tuning File '${NEW_VALUE}' does not exist; please change it.${wNC}" fi NEEDS_RESTART="true" ;; - displaysettings) - if [[ ${NEW_VALUE} -eq 0 ]]; then - NEW_VALUE="false" - else - NEW_VALUE="true" - fi + "displaysettings") + [[ ${NEW_VALUE} != "false" ]] && NEW_VALUE="true" if check_website; then # If there are two Websites, this gets the index in the first one. # Let's hope it's the same index in the second one... PARENT="homePage.popoutIcons" - INDEX=$(getJSONarrayIndex "${WEB_CONFIG_FILE}" "${PARENT}" "Allsky Settings") + INDEX=$( getJSONarrayIndex "${WEB_CONFIG_FILE}" "${PARENT}" "Allsky Settings" ) if [[ ${INDEX} -ge 0 ]]; then WEBSITE_CONFIG+=("${PARENT}[${INDEX}].display" "${LABEL}" "${NEW_VALUE}") else @@ -441,22 +425,53 @@ do fi ;; - showonmap) - SHOW_ON_MAP="1" - [[ ${NEW_VALUE} -eq 0 ]] && POSTTOMAP_ACTION="--delete" + "showonmap") + SHOW_ON_MAP="true" + [[ ${NEW_VALUE} == "false" ]] && POSTTOMAP_ACTION="--delete" + RUN_POSTTOMAP="true" ;; - location | owner | camera | lens | computer) + "location" | "owner" | "camera" | "lens" | "computer") RUN_POSTTOMAP="true" check_website && WEBSITE_CONFIG+=(config."${KEY}" "${LABEL}" "${NEW_VALUE}") ;; - websiteurl | imageurl) + + "remotewebsiteurl" | "remotewebsiteimageurl") + CHECK_REMOTE_WEBSITE_ACCESS="true" RUN_POSTTOMAP="true" ;; - overlaymethod) + "useremotewebsite") + CHECK_REMOTE_WEBSITE_ACCESS="true" + USE_REMOTE_WEBSITE="${NEW_VALUE}" + ;; + + "remotewebsiteprotocol" | "remotewebsiteimagedir" | \ + "remotewebsitevideodestinationname" | "remotewebsitekeogramdestinationname" | "remotewebsitestartrailsdestinationname") + CHECK_REMOTE_WEBSITE_ACCESS="true" + ;; + + remotewebsite_*) + CHECK_REMOTE_WEBSITE_ACCESS="true" + ;; + + "useremoteserver") + CHECK_REMOTE_SERVER_ACCESS="true" + USE_REMOTE_SERVER="${NEW_VALUE}" + ;; + + # We don't care about the *destination names for remote servers + "remoteserverprotocol" | "remoteserveriteimagedir") + CHECK_REMOTE_SERVER_ACCESS="true" + ;; + + remoteserver_*) + CHECK_REMOTE_SERVER_ACCESS="true" + ;; + + "overlaymethod") if [[ ${NEW_VALUE} -eq 1 ]]; then # 1 == "overlay" method echo -en "${wWARNING}" echo -en "NOTE: You must enable the ${wBOLD}Overlay Module${wNBOLD} in the" @@ -470,13 +485,25 @@ do *) - echo -e "${wWARNING}WARNING: Unknown label '${LABEL}', key='${KEY}'; ignoring.${wNC}" + echo -e "${wWARNING}WARNING: Unknown key '${KEY}'; ignoring. Old=${OLD_VALUE}, New=${NEW_VALUE}${wNC}" + ((NUM_CHANGED--)) ;; esac shift 4 done +[[ ${NUM_CHANGED} -le 0 ]] && exit 0 # Nothing changed + +USE_REMOTE_WEBSITE="$( settings ".useremotewebsite" )" +if [[ ${USE_REMOTE_WEBSITE} == "true" && ${CHECK_REMOTE_WEBSITE_ACCESS} == "true" ]]; then + : # TODO - do a test upload +fi +USE_REMOTE_SERVER="$( settings ".useremoteserver" )" +if [[ ${USE_REMOTE_SERVER} == "true" && ${CHECK_REMOTE_SERVER_ACCESS} == "true" ]]; then + : # TODO - do a test upload +fi + if check_website ; then # Anytime a setting in settings.json changed we want to # send an updated file to all Allsky Website(s). @@ -519,26 +546,26 @@ if [[ ${#WEBSITE_CONFIG[@]} -gt 0 ]]; then "${ALLSKY_SCRIPTS}/updateWebsiteConfig.sh" ${DEBUG_ARG} --remote "${WEBSITE_CONFIG[@]}" FILE_TO_UPLOAD="${ALLSKY_REMOTE_WEBSITE_CONFIGURATION_FILE}" - TO="${IMAGE_DIR}" + + IMAGE_DIR="$( settings ".remotewebsiteimagedir" )" if [[ ${DEBUG} == "true" ]]; then - echo -e "${wDEBUG}Uploading '${ALLSKY_REMOTE_WEBSITE_CONFIGURATION_FILE}' to ${TO:-root}${wNC}" + echo -e "${wDEBUG}Uploading '${FILE_TO_UPLOAD}' to remote Website.${wNC}" fi - "${ALLSKY_SCRIPTS}/upload.sh" --silent \ - "${ALLSKY_REMOTE_WEBSITE_CONFIGURATION_FILE}" \ - "${TO}" \ - "${ALLSKY_WEBSITE_CONFIGURATION_NAME}" \ - "RemoteWebsite" - R=$? - if [[ ${R} -ne 0 ]]; then - echo -e "${RED}${ERROR_PREFIX}Unable to upload '${FILE_TO_UPLOAD}'.${NC}" + if ! "${ALLSKY_SCRIPTS}/upload.sh" --silent --remote "web" \ + "${FILE_TO_UPLOAD}" \ + "${IMAGE_DIR}" \ + "${ALLSKY_WEBSITE_CONFIGURATION_NAME}" \ + "RemoteWebsite" ; then + echo -e "${RED}${ERROR_PREFIX}Unable to upload '${FILE_TO_UPLOAD}' to Website ${NUM}.${NC}" fi fi fi if [[ ${RUN_POSTTOMAP} == "true" ]]; then [[ -z ${SHOW_ON_MAP} ]] && SHOW_ON_MAP="$( settings ".showonmap" )" - if [[ ${SHOW_ON_MAP} == "1" ]]; then + if [[ ${SHOW_ON_MAP} == "true" ]]; then + [[ ${DEBUG} == "true" ]] && echo -e "${wDEBUG}Executing postToMap.sh${NC}" # shellcheck disable=SC2086 "${ALLSKY_SCRIPTS}/postToMap.sh" --whisper --force ${DEBUG_ARG} ${POSTTOMAP_ACTION} diff --git a/scripts/modules/allsky_saveimage.py b/scripts/modules/allsky_saveimage.py index 6db2db6d7..ec9ed0b1d 100644 --- a/scripts/modules/allsky_saveimage.py +++ b/scripts/modules/allsky_saveimage.py @@ -53,4 +53,4 @@ def saveimage(params, event): result = "Cannot determine the image quality. Image NOT saved" s.log(0, "ERROR: {}".format(result)) - return result \ No newline at end of file + return result diff --git a/scripts/modules/allsky_shared.py b/scripts/modules/allsky_shared.py index 2135b800b..2f9f18939 100644 --- a/scripts/modules/allsky_shared.py +++ b/scripts/modules/allsky_shared.py @@ -166,7 +166,7 @@ def setupForCommandLine(): pass proc.communicate() - readConfig() + """ XXXXX TODO: config.sh is gone. readConfig() """ readSettings() def readConfig(): @@ -195,6 +195,7 @@ def readConfig(): else: break +""" XXXXX TODO: ftp-settings.sh is gone and this function is never used. """ def readUploadConfig(): global UPLOAD @@ -224,6 +225,7 @@ def readSettings(): settingsFile = getEnvironmentVariable("SETTINGS_FILE") if settingsFile is None: + """ XXXXX TODO: it's a major error if the settings file doesn't exist. Do not continue. """ camera = getEnvironmentVariable("CAMERA_TYPE") if camera is None: camera = CONFIG["CAMERA"] @@ -254,6 +256,7 @@ def writeSettings(): settingsFile = getEnvironmentVariable("SETTINGS_FILE") if settingsFile is None: + """ XXXXX TODO: it's a major error if the settings file doesn't exist. Do not continue. """ camera = getEnvironmentVariable("CAMERA_TYPE") if camera is None: camera = CONFIG["CAMERA"] @@ -280,7 +283,7 @@ def getConfig(settingName): return result def setupParams(params, metaData): - readConfig() + """ XXXXX TODO: config.sh is gone. readConfig() """ for param in metaData["arguments"]: if param in metaData["argumentdetails"]: diff --git a/scripts/periodic.sh b/scripts/periodic.sh index bde3d3c8d..96b8ff08e 100755 --- a/scripts/periodic.sh +++ b/scripts/periodic.sh @@ -1,29 +1,28 @@ #!/bin/bash -[[ -z "${ALLSKY_HOME}" ]] && export ALLSKY_HOME="$(realpath "$(dirname "${BASH_ARGV0}")/..")" +[[ -z "${ALLSKY_HOME}" ]] && export ALLSKY_HOME="$( realpath "$( dirname "${BASH_ARGV0}" )/.." )" -#shellcheck disable=SC2086 source-path=. -source "${ALLSKY_HOME}/variables.sh" || exit ${ALLSKY_ERROR_STOP} -#shellcheck disable=SC2086 source-path=scripts -source "${ALLSKY_SCRIPTS}/functions.sh" || exit ${ALLSKY_ERROR_STOP} -#shellcheck disable=SC2086,SC1091 # file doesn't exist in GitHub -source "${ALLSKY_CONFIG}/config.sh" || exit ${ALLSKY_ERROR_STOP} +#shellcheck source-path=. +source "${ALLSKY_HOME}/variables.sh" || exit "${ALLSKY_ERROR_STOP}" +#shellcheck source-path=scripts +source "${ALLSKY_SCRIPTS}/functions.sh" || exit "${ALLSKY_ERROR_STOP}" trap "exit 0" SIGTERM SIGINT -#shellcheck disable=SC2086 -cd "${ALLSKY_SCRIPTS}" || exit ${ALLSKY_ERROR_STOP} +cd "${ALLSKY_SCRIPTS}" || exit "${ALLSKY_ERROR_STOP}" while : do "${ALLSKY_SCRIPTS}/flow-runner.py" --event periodic - DELAY=$(jq ".periodictimer" "${ALLSKY_MODULES}/module-settings.json") + DELAY=$(settings ".periodictimer" "${ALLSKY_MODULES}/module-settings.json") - if [[ ! ($DELAY =~ ^[0-9]+$) ]]; then + if [[ ! (${DELAY} =~ ^[0-9]+$) ]]; then DELAY=60 fi if [[ ${ALLSKY_DEBUG_LEVEL} -ge 4 ]]; then - echo "INFO: Sleeping for $DELAY seconds" + echo "INFO: Sleeping for ${DELAY} seconds" fi - sleep "$DELAY" + sleep "${DELAY}" done + +exit 0 diff --git a/scripts/postData.sh b/scripts/postData.sh index f09dba630..763edafad 100755 --- a/scripts/postData.sh +++ b/scripts/postData.sh @@ -6,54 +6,37 @@ # By default we upload to both local and remote Websites if they exist. # Allow this script to be executed manually or by sudo, which requires several variables to be set. -[[ -z ${ALLSKY_HOME} ]] && export ALLSKY_HOME="$(realpath "$(dirname "${BASH_ARGV0}")/..")" -ME="$(basename "${BASH_ARGV0}")" +[[ -z ${ALLSKY_HOME} ]] && export ALLSKY_HOME="$( realpath "$( dirname "${BASH_ARGV0}" )/.." )" +ME="$( basename "${BASH_ARGV0}" )" -#shellcheck disable=SC2086 source-path=. -source "${ALLSKY_HOME}/variables.sh" || exit ${ALLSKY_ERROR_STOP} -#shellcheck disable=SC2086 source-path=scripts -source "${ALLSKY_SCRIPTS}/functions.sh" || exit ${ALLSKY_ERROR_STOP} -#shellcheck disable=SC2086,SC1091 # file doesn't exist in GitHub -source "${ALLSKY_CONFIG}/config.sh" || exit ${ALLSKY_ERROR_STOP} -#shellcheck disable=SC2086,SC1091 # file doesn't exist in GitHub -source "${ALLSKY_CONFIG}/ftp-settings.sh" || exit ${ALLSKY_ERROR_STOP} +#shellcheck source-path=. +source "${ALLSKY_HOME}/variables.sh" || exit "${ALLSKY_ERROR_STOP}" +#shellcheck source-path=scripts +source "${ALLSKY_SCRIPTS}/functions.sh" || exit "${ALLSKY_ERROR_STOP}" usage_and_exit() { - retcode=${1} + RET=${1} echo - [[ ${retcode} -ne 0 ]] && echo -en "${RED}" - echo "Usage: ${ME} [--help] [--debug] [--settingsOnly] [--websites w] [--allfiles]" - [[ ${retcode} -ne 0 ]] && echo -en "${NC}" + [[ ${RET} -ne 0 ]] && echo -en "${RED}" + echo "Usage: ${ME} [--help] [--settingsOnly] [--allfiles]" + [[ ${RET} -ne 0 ]] && echo -en "${NC}" echo " where:" echo " '--allfiles' causes all 'view settings' files to be uploaded" - # shellcheck disable=SC2086 - exit ${retcode} + exit "${RET}" } HELP="false" -DEBUG="false" -DEBUG_ARG="" SETTINGS_ONLY="false" -WEBSITES_TO_DO="" ALL_FILES="false" RET=0 while [[ $# -gt 0 ]]; do ARG="${1}" case "${ARG,,}" in # lower case - --debug) - DEBUG="true" - DEBUG_ARG="--debug" - shift - ;; --help) HELP="true" shift ;; - --websites) - WEBSITES_TO_DO="${2}" - shift 2 - ;; --allfiles) ALL_FILES="true" shift @@ -75,73 +58,33 @@ done [[ ${RET} -ne 0 ]] && usage_and_exit ${RET} [[ ${HELP} = "true" ]] && usage_and_exit 0 -WEBSITES="$(whatWebsites)" -# Make sure a local or remote Allsky Website exists. -if [[ ${WEBSITES} == "none" ]]; then - echo -e "${YELLOW}${ME}: WARNING: No local or remote website found.${NC}" - exit 0 # It's not an error -fi - -[[ ${DEBUG} == "true" ]] && echo -e "${wDEBUG}WEBSITES=${WEBSITES}, WEBSITES_TO_TO=${WEBSITES_TO_DO}${NC}" -# Make sure we have the specified Website(s). -if [[ -n ${WEBSITES_TO_DO} && ${WEBSITES_TO_DO} != "${WEBSITES}" ]]; then - case "${WEBSITES_TO_DO}" in - local | remote | both) - ;; - *) - MSG="Invalid requested Website type: ${WEBSITES_TO_DO}. Must be 'local', 'remote', or 'both'" - echo -e "${RED}${ME}: ERROR: ${MSG}" - exit 1 - ;; - esac - if [[ ( ${WEBSITES_TO_DO} == "local" && ${WEBSITES} != "both") || - ( ${WEBSITES_TO_DO} == "remote" && ${WEBSITES} != "both") ]]; then - MSG="Requested Website type '${WEBSITES_TO_DO}' does not exist. Valid Websites='${WEBSITES}'." - echo -e "${RED}${ME}: ERROR: ${MSG}" - exit 1 - fi - - WEBSITES="${WEBSITES_TO_DO}" -fi - -if [[ ${WEBSITES} == "local" || ${WEBSITES} == "both" ]]; then - HAS_LOCAL_WEBSITE="true" -else - HAS_LOCAL_WEBSITE="false" -fi -if [[ ${WEBSITES} == "remote" || ${WEBSITES} == "both" ]]; then - HAS_REMOTE_WEBSITE="true" -else - HAS_REMOTE_WEBSITE="false" -fi - if [[ ${SETTINGS_ONLY} == "false" ]]; then OK="true" - if ! latitude="$(convertLatLong "$(settings ".latitude")" "latitude")" ; then + if ! latitude="$( convertLatLong "$( settings ".latitude" )" "latitude" )" ; then OK="false" echo -e "${RED}${ME}: ERROR: ${latitude}" "${ALLSKY_SCRIPTS}/addMessage.sh" "error" "${ME}: ${latitude}" fi - if ! longitude="$(convertLatLong "$(settings ".longitude")" "longitude")" ; then + if ! longitude="$( convertLatLong "$( settings ".longitude" )" "longitude" )" ; then OK="false" echo -e "${RED}${ME}: ERROR: ${longitude}" "${ALLSKY_SCRIPTS}/addMessage.sh" "error" "${ME}: ${longitude}" fi [[ ${OK} == "false" ]] && exit 1 - angle="$(settings ".angle")" - timezone="$(date "+%z")" + angle="$( settings ".angle" )" + timezone="$( date "+%z" )" # If nighttime happens after midnight, sunwait returns "--:-- (Midnight sun)" # If nighttime happens before noon, sunwait returns "--:-- (Polar night)" sunrise="$(sunwait list rise angle "${angle}" "${latitude}" "${longitude}")" sunrise_hhmm="${sunrise:0:5}" - sunset="$(sunwait list set angle "${angle}" "${latitude}" "${longitude}")" + sunset="$( sunwait list set angle "${angle}" "${latitude}" "${longitude}" )" sunset_hhmm="${sunset:0:5}" if [[ ${sunrise_hhmm} == "--:--" || ${sunset_hhmm} == "--:--" ]]; then # nighttime starts after midnight or before noon. - today="$(date --date='tomorrow' +%Y-%m-%d)" # is actually tomorrow + today="$( date --date='tomorrow' +%Y-%m-%d )" # is actually tomorrow # TODO What SHOULD *_hhmm be? sunrise_hhmm="00:00" sunset_hhmm="00:00" @@ -152,13 +95,13 @@ if [[ ${SETTINGS_ONLY} == "false" ]]; then echo "Using tomorrow at '${sunrise_hhmm}' instead." echo "***" else - today="$(date +%Y-%m-%d)" + today="$( date +%Y-%m-%d )" fi FILE="data.json" OUTPUT_FILE="${ALLSKY_TMP}/${FILE}" ( - if [[ $(settings ".takeDaytimeImages") -eq 1 ]]; then + if [[ $(settings ".takedaytimeimages") == "true" ]]; then D="true" else D="false" @@ -174,8 +117,9 @@ fi function upload_file() { - local FILE_TO_UPLOAD="${1}" - local DIRECTORY="${2}" # Directory to put file in + local WHERE="${1}" + local FILE_TO_UPLOAD="${2}" + local DIRECTORY="${3}" # Directory to put file in if [[ ! -f ${FILE_TO_UPLOAD} ]]; then MSG="File to upload '${FILE_TO_UPLOAD}' not found." echo -e "${RED}${ME}: ERROR: ${MSG}.${NC}" @@ -183,76 +127,31 @@ function upload_file() return 1 fi - local RETCODE=0 - local S="${DIRECTORY:0:1}" - - # Copy to local Allsky Website if it exists. - if [[ ${HAS_LOCAL_WEBSITE} == "true" ]]; then - - # If ${DIRECTORY} isn't "" and doesn't start with "/", add one. - if [[ -n ${S} && ${S} != "/" ]]; then - S="/" - else - S="" - fi - - TO="${ALLSKY_WEBSITE}${S}${DIRECTORY}" - [[ ${DEBUG} == "true" ]] && echo -e "${wDEBUG}cp '${FILE_TO_UPLOAD}' '${TO}'${wNC}" - - if ! cp "${FILE_TO_UPLOAD}" "${TO}" ; then - MSG="Unable to copy '${FILE_TO_UPLOAD}' to '${ALLSKY_WEBSITE}'" - echo -e "${RED}${ME}: ERROR: ${MSG}.${NC}" - "${ALLSKY_SCRIPTS}/addMessage.sh" "error" "${ME}: ${MSG}" - RETCODE=1 - fi - fi - - # Upload to remote Website if there is one. - if [[ ${HAS_REMOTE_WEBSITE} == "true" ]]; then - - # Need a "/" to separate when both variables exist. - if [[ -n ${IMAGE_DIR} ]]; then - [[ -n ${S} && ${S} != "/" ]] && S="/" - else - S="" - fi - - # Copy relative to ${IMAGE_DIR} - TO="${IMAGE_DIR}${S}${DIRECTORY}" - [[ ${DEBUG} == "true" ]] && echo -e "${wDEBUG}Uploading '${FILE_TO_UPLOAD}' to ${TO:-root}${wNC}" - - "${ALLSKY_SCRIPTS}/upload.sh" --silent ${DEBUG_ARG} \ - "${FILE_TO_UPLOAD}" \ - "${TO}" \ - "" \ - "PostData" - if [[ $? -ne 0 ]]; then - MSG="Unable to upload '${FILE_TO_UPLOAD}'" - echo -e "${RED}${ME}: ${MSG}.${NC}" - "${ALLSKY_SCRIPTS}/addMessage.sh" "error" "${ME}: ${MSG}" - RETCODE=1 - fi - fi - - # shellcheck disable=SC2086 - return ${RETCODE} + #shellcheck disable=SC2086 + upload_all ${WHERE} "${FILE_TO_UPLOAD}" "${DIRECTORY}" "" "PostData" + return $? } # These files go in ${VIEW_DIR} so the user can display their settings. # This directory is in the root of the Allsky Website. # Assume if the first upload fails they all will, so exit. -upload_file "${SETTINGS_FILE}" "${ALLSKY_WEBSITE_VIEWSETTINGS_DIRECTORY_NAME}" || exit $? +WEB_ONLY="--local-web --remote-web" +upload_file "${WEB_ONLY}" "${SETTINGS_FILE}" "${ALLSKY_WEBSITE_VIEWSETTINGS_DIRECTORY_NAME}" \ + || exit "$?" if [[ ${ALL_FILES} == "true" ]]; then - upload_file "${OPTIONS_FILE}" "${ALLSKY_WEBSITE_VIEWSETTINGS_DIRECTORY_NAME}" - upload_file "${ALLSKY_WEBUI}/includes/allskySettings.php" "${ALLSKY_WEBSITE_VIEWSETTINGS_DIRECTORY_NAME}" - upload_file "${ALLSKY_DOCUMENTATION}/css/custom.css" "${ALLSKY_WEBSITE_VIEWSETTINGS_DIRECTORY_NAME}" + upload_file "${WEB_ONLY}" "${OPTIONS_FILE}" \ + "${ALLSKY_WEBSITE_VIEWSETTINGS_DIRECTORY_NAME}" + upload_file "${WEB_ONLY}" "${ALLSKY_WEBUI}/includes/allskySettings.php" \ + "${ALLSKY_WEBSITE_VIEWSETTINGS_DIRECTORY_NAME}" + upload_file "${WEB_ONLY}" "${ALLSKY_DOCUMENTATION}/css/custom.css" \ + "${ALLSKY_WEBSITE_VIEWSETTINGS_DIRECTORY_NAME}" fi if [[ ${SETTINGS_ONLY} == "false" ]]; then - upload_file "${OUTPUT_FILE}" "" # Goes in top-level directory - # shellcheck disable=SC2086 - exit $? + # Some remote servers may want to see this file so upload everywhere. + upload_file "" "${OUTPUT_FILE}" "" # Goes in top-level directory + exit "$?" fi exit 0 diff --git a/scripts/postToMap.sh b/scripts/postToMap.sh index 1a063aa1c..17d8c6da7 100755 --- a/scripts/postToMap.sh +++ b/scripts/postToMap.sh @@ -1,25 +1,23 @@ #!/bin/bash # Allow this script to be executed manually, which requires several variables to be set. -[[ -z ${ALLSKY_HOME} ]] && export ALLSKY_HOME="$(realpath "$(dirname "${BASH_ARGV0}")/..")" -ME="$(basename "${BASH_ARGV0}")" +[[ -z ${ALLSKY_HOME} ]] && export ALLSKY_HOME="$( realpath "$(dirname "${BASH_ARGV0}")/.." )" +ME="$( basename "${BASH_ARGV0}" )" # This script uploads various information relative to the camera setup to the allsky map. # https://www.thomasjacquin.com/allsky-map/ # Information is gathered automatically from the settings file. # The script can be called manually, via endOfNight.sh, or via the WebUI. -#shellcheck disable=SC2086 source-path=. -source "${ALLSKY_HOME}/variables.sh" || exit ${ALLSKY_ERROR_STOP} -#shellcheck disable=SC2086 source-path=scripts -source "${ALLSKY_SCRIPTS}/functions.sh" || exit ${ALLSKY_ERROR_STOP} -#shellcheck disable=SC2086,SC1091 # file doesn't exist in GitHub -source "${ALLSKY_CONFIG}/config.sh" || exit ${ALLSKY_ERROR_STOP} +#shellcheck source-path=. +source "${ALLSKY_HOME}/variables.sh" || exit "${ALLSKY_ERROR_STOP}" +#shellcheck source-path=scripts +source "${ALLSKY_SCRIPTS}/functions.sh" || exit "${ALLSKY_ERROR_STOP}" function usage_and_exit() { - RET_CODE=${1} - [[ ${RET_CODE} -ne 0 ]] && echo -en "${wERROR}" + RET=${1} + [[ ${RET} -ne 0 ]] && echo -en "${wERROR}" echo echo -e "Usage: ${ME} [--help] [--whisper] [--delete] [--force] [--debug] [--machineid id] [--endofnight]" echo @@ -30,13 +28,13 @@ function usage_and_exit() echo "--debug: Output debugging statements." echo "--endofnight: Indicates how ${ME} was invoked." echo - [[ ${RET_CODE} -ne 0 ]] && echo -e "${wNC}" - # shellcheck disable=SC2086 - exit ${RET_CODE} + [[ ${RET} -ne 0 ]] && echo -e "${wNC}" + exit "${RET}" } function get_domain() { + local URL D # Get the domain name or IP address from a URL # Examples: # http://myallsky.com # Return "myallsky.com" @@ -49,15 +47,17 @@ function get_domain() echo "${D}" } -TIMEOUT=30 # seconds to wait when trying to reach a URL function check_URL() { - URL="${1}" - URL_TYPE="${2}" - FIELD_NAME="${3}" + local URL="${1}" + local URL_TYPE="${2}" + local FIELD_NAME="${3}" + + # ${E} is a global variable we may set. + local TIMEOUT=30 # seconds to wait when trying to reach a URL - D="$(get_domain "${URL}")" + local D="$( get_domain "${URL}" )" if [[ "${D:0:7}" == "192.168" || "${D:0:4}" == "10.0" || "${D:0:6}" == "172.16" || @@ -73,8 +73,9 @@ function check_URL() else # Make sure it's a valid URL - CONTENT="$(curl --head --silent --show-error --connect-timeout ${TIMEOUT} "${URL}" 2>&1)" - RET=$? + local CONTENT="$(curl --head --silent --show-error --connect-timeout ${TIMEOUT} "${URL}" 2>&1)" + local RET=$? + [[ ${DEBUG} == "true" ]] && echo -e "\n${wDEBUG}check_URL() RET=${RET}:\n${CONTENT}${wNC}.\n" if [[ ${RET} -eq 6 ]]; then E="ERROR: ${FIELD_NAME} '${URL}' not found - check spelling and network connectivity.${BR}${E}" elif [[ ${RET} -eq 28 ]]; then @@ -82,11 +83,12 @@ function check_URL() elif [[ ${RET} -ne 0 ]]; then E="ERROR: ${FIELD_NAME} '${URL}' cannot be reached (${CONTENT}).${BR}${E}" else - if [[ ${URL_TYPE} == "websiteurl" ]]; then - TYPE="$(echo "${CONTENT}" | grep -i "Content-Type: text")" + local TYPE T + if [[ ${URL_TYPE} == "remotewebsiteurl" ]]; then + TYPE="$( echo "${CONTENT}" | grep -i "Content-Type: text" )" T="web site" else - TYPE="$(echo "${CONTENT}" | grep -i "Content-Type: image")" + TYPE="$(echo "${CONTENT}" | grep -i "Content-Type: image" )" T="image" fi if [[ -z ${TYPE} ]]; then @@ -139,7 +141,7 @@ done # If not on a tty, then we're either called from the endOfNight.sh script (plain text), or the WebUI (html). -if [[ ${ON_TTY} -eq 0 && ${ENDOFNIGHT} == "false" ]]; then +if [[ ${ON_TTY} == "false" && ${ENDOFNIGHT} == "false" ]]; then BR="
      " # Line break else BR="\n" @@ -171,7 +173,7 @@ fi if [[ -z ${MACHINE_ID} ]]; then - MACHINE_ID="$(< /etc/machine-id)" + MACHINE_ID="$( < /etc/machine-id )" if [[ -z ${MACHINE_ID} ]]; then E="ERROR: Unable to get 'machine_id': check /etc/machine-id." echo -e "${ERROR_MSG_START}${E}${wNC}" @@ -181,22 +183,21 @@ if [[ -z ${MACHINE_ID} ]]; then fi OK="true" -E="" -LATITUDE="$(settings ".latitude")" -if [[ ${LATITUDE} == "" ]]; then +E="" # Global variable +LATITUDE="$( settings ".latitude" )" +if [[ -z ${LATITUDE} ]]; then E="ERROR: 'Latitude' is required.${BR}${E}" OK="false" fi -LONGITUDE="$(settings ".longitude")" -if [[ ${LONGITUDE} == "" ]]; then +LONGITUDE="$( settings ".longitude" )" +if [[ -z ${LONGITUDE} ]]; then E="ERROR: 'Longitude' is required.${BR}${E}" OK="false" fi [[ ${OK} == "false" ]] && echo -e "${ERROR_MSG_START}${E}${wNC}" && exit 1 -OK="true" -LATITUDE="$(convertLatLong "${LATITUDE}" "latitude")" || OK="false" -LONGITUDE="$(convertLatLong "${LONGITUDE}" "longitude")" || OK="false" +LATITUDE="$( convertLatLong "${LATITUDE}" "latitude" )" || OK="false" +LONGITUDE="$( convertLatLong "${LONGITUDE}" "longitude" )" || OK="false" [[ ${OK} == "false" ]] && exit 1 # convertLatLong output error message if false; then @@ -217,35 +218,35 @@ if [[ ${DELETE} == "true" ]]; then } else - LOCATION="$(settings ".location")" - OWNER="$(settings ".owner")" - WEBSITE_URL="$(settings ".websiteurl")" - IMAGE_URL="$(settings ".imageurl")" - CAMERA="$(settings ".camera")" - LENS="$(settings ".lens")" - COMPUTER="$(settings ".computer")" + LOCATION="$( settings ".location" )" + OWNER="$( settings ".owner" )" + WEBSITE_URL="$( settings ".remotewebsiteurl" )" + IMAGE_URL="$( settings ".remotewebsiteimageurl" )" + CAMERA="$( settings ".camera" )" + LENS="$( settings ".lens" )" + COMPUTER="$( settings ".computer" )" OK="true" E="" W="" # Check for required fields - if [[ ${CAMERA} == "" ]]; then + if [[ -z ${CAMERA} ]]; then E="ERROR: 'Camera' is required.${BR}${E}" OK="false" fi - if [[ ${COMPUTER} == "" ]]; then + if [[ -z ${COMPUTER} ]]; then E="ERROR: 'Computer' is required.${BR}${E}" OK="false" fi # Check for optional, but suggested fields - if [[ ${LOCATION} == "" ]]; then + if [[ -z ${LOCATION} ]]; then W="WARNING: 'Location' not set; continuing.${BR}${W}" fi - if [[ ${OWNER} == "" ]]; then + if [[ -z ${OWNER} ]]; then W="WARNING: 'Owner' not set; continuing.${BR}${W}" fi - if [[ ${LENS} == "" ]]; then + if [[ -z ${LENS} ]]; then W="WARNING: 'Lens' not set; continuing.${BR}${W}" fi @@ -256,17 +257,18 @@ else OK="false" elif [[ -n ${WEBSITE_URL} ]]; then # they specified both # The domain names (or IP addresses) must be the same. - Wurl="$(get_domain "${WEBSITE_URL}")" - Iurl="$(get_domain "${IMAGE_URL}")" + Wurl="$( get_domain "${WEBSITE_URL}" )" + Iurl="$( get_domain "${IMAGE_URL}" )" if [[ ${Wurl} != "${Iurl}" ]]; then E="ERROR: The Website and Image URLs must have the same domain name or IP address.${BR}${E}" OK="false" fi + # check_URL() may set ${E}. if [[ -n ${WEBSITE_URL} ]]; then - check_URL "${WEBSITE_URL}" websiteurl "Website URL" || OK="false" + check_URL "${WEBSITE_URL}" remotewebsiteurl "Website URL" || OK="false" fi if [[ -n ${IMAGE_URL} ]]; then - check_URL "${IMAGE_URL}" imageurl "Image URL" || OK="false" + check_URL "${IMAGE_URL}" remotewebsiteimageurl "Image URL" || OK="false" fi fi @@ -274,25 +276,25 @@ else echo -e "${WARNING_MSG_START}${W%%"${BR}"}${NC}" # Want each message to have its own addMessage.sh entry. if [[ ${ENDOFNIGHT} == "true" ]]; then - echo "${W}" | while read -r w + echo "${W}" | while read -r MSG do - "${ALLSKY_SCRIPTS}/addMessage.sh" "warning" "${ME}: ${w}" + "${ALLSKY_SCRIPTS}/addMessage.sh" "warning" "${ME}: ${MSG}" done fi fi if [[ ${OK} == "false" ]]; then echo -e "${ERROR_MSG_START}${E%%"${BR}"}${NC}" if [[ ${ENDOFNIGHT} == "true" ]]; then - echo "${E}" | while read -r e + echo "${E}" | while read -r MSG do - "${ALLSKY_SCRIPTS}/addMessage.sh" "error" "${ME}: ${e}" + "${ALLSKY_SCRIPTS}/addMessage.sh" "error" "${ME}: ${MSG}" done fi exit 2 fi if [[ -f ${ALLSKY_HOME}/version ]]; then - ALLSKY_VERSION="$(< "${ALLSKY_HOME}/version")" + ALLSKY_VERSION="$( < "${ALLSKY_HOME}/version" )" else ALLSKY_VERSION="unknown" # This really should be an error fi @@ -331,7 +333,7 @@ RETURN_CODE=0 if [[ ${UPLOAD} == "true" ]]; then if [[ ${DELETE} == "true" ]]; then [[ ${WHISPER} == "false" ]] && echo "${ME}: Deleting map data." - elif [[ ${ON_TTY} -eq 1 || ${ALLSKY_DEBUG_LEVEL} -ge 3 ]]; then + elif [[ ${ON_TTY} == "true" || ${ALLSKY_DEBUG_LEVEL} -ge 3 ]]; then [[ ${WHISPER} == "false" ]] && echo "${ME}: Uploading map data." fi # shellcheck disable=SC2089 @@ -339,8 +341,9 @@ if [[ ${UPLOAD} == "true" ]]; then # shellcheck disable=SC2089 CMD="${CMD} --data '$(generate_post_data)' 'https://www.thomasjacquin.com/allsky-map/postToMap.php'" [[ ${DEBUG} == "true" ]] && echo -e "\n${wDEBUG}Executing:\n${CMD}${wNC}\n" + # shellcheck disable=SC2090,SC2086 - RETURN="$(echo ${CMD} | bash)" + RETURN="$( echo ${CMD} | bash )" RETURN_CODE=$? [[ ${DEBUG} == "true" ]] && echo -e "\n${wDEBUG}Returned:\n${RETURN}${wNC}.\n" if [[ ${RETURN_CODE} -ne 0 ]]; then @@ -351,7 +354,7 @@ if [[ ${UPLOAD} == "true" ]]; then fi # Get the return string from the server. It's the last line of output. - RET="$(echo "${RETURN}" | tail -1)" + RET="$( echo "${RETURN}" | tail -1 )" if [[ ${RET} == "INSERTED" || ${RET} == "DELETED" ]]; then echo -e "${wOK}${MSG_START}Map data ${RET}.${wNC}" @@ -396,8 +399,8 @@ if [[ ${UPLOAD} == "true" ]]; then RETURN_CODE=2 fi -elif [[ ( ${ON_TTY} -eq 1 || ${ALLSKY_DEBUG_LEVEL} -ge 4) && ${ENDOFNIGHT} == "false" ]]; then +elif [[ ( ${ON_TTY} == "true" || ${ALLSKY_DEBUG_LEVEL} -ge 4) && ${ENDOFNIGHT} == "false" ]]; then echo "${ME}: Week day doesn't match Machine ID ending - don't upload." fi -exit ${RETURN_CODE} +exit "${RETURN_CODE}" diff --git a/scripts/removeBadImages.sh b/scripts/removeBadImages.sh index 40ef0d43a..d71a7a42a 100755 --- a/scripts/removeBadImages.sh +++ b/scripts/removeBadImages.sh @@ -12,24 +12,22 @@ # The MEAN is the only thing that should go to stdout. -ME="$(basename "${BASH_ARGV0}")" +ME="$( basename "${BASH_ARGV0}" )" -#shellcheck disable=SC2086 source-path=. -source "${ALLSKY_HOME}/variables.sh" || exit ${ALLSKY_ERROR_STOP} -#shellcheck disable=SC2086 source-path=scripts -source "${ALLSKY_SCRIPTS}/functions.sh" || exit ${ALLSKY_ERROR_STOP} -#shellcheck disable=SC2086,SC1091 # file doesn't exist in GitHub -source "${ALLSKY_CONFIG}/config.sh" || exit ${ALLSKY_ERROR_STOP} +#shellcheck source-path=. +source "${ALLSKY_HOME}/variables.sh" || exit "${ALLSKY_ERROR_STOP}" +#shellcheck source-path=scripts +source "${ALLSKY_SCRIPTS}/functions.sh" || exit "${ALLSKY_ERROR_STOP}" usage() { - retcode="${1}" + RET="${1}" ( echo echo "Remove images with corrupt data which might mess up startrails and keograms." - [ "${retcode}" -ne 0 ] && echo -en "${RED}" + [ "${RET}" -ne 0 ] && echo -en "${RED}" echo -n "Usage: ${ME} [--help] [--debug] directory [file]" - [ "${retcode}" -ne 0 ] && echo -e "${NC}" + [ "${RET}" -ne 0 ] && echo -e "${NC}" echo echo "You must enter the arguments in the above order." # TODO: use getopts to allow any order @@ -37,8 +35,7 @@ usage() echo "If 'file' is specified, only that file in 'directory' will be checked," echo "otherwise all files in 'directory' will be checked." ) >&2 - # shellcheck disable=SC2086 - exit ${retcode} + exit "${RET}" } [[ ${1} == "-h" || ${1} == "--help" ]] && usage 0 if [[ ${1} == "-d" || ${1} == "--debug" ]]; then @@ -56,7 +53,7 @@ DATE="${1}" FILE="${2}" # If we're running in debug mode don't display ${ME} since it makes the output harder to read. -if [[ ${DEBUG} == "true" || ${ON_TTY} -eq 1 ]]; then +if [[ ${DEBUG} == "true" || ${ON_TTY} == "true" ]]; then ME="" else ME="${ME}:" @@ -74,10 +71,12 @@ if [[ ${FILE} != "" && ! -f ${DATE}/${FILE} ]]; then exit 2 fi -if [[ $(settings ".takeDarkFrames") -eq 1 ]]; then +HIGH="$( settings ".imageremovebadhigh" )" +LOW="$( settings ".imageremovebadlow" )" +if [[ $(settings ".takedarkframes") == "true" ]]; then # Disable low brightness check since darks will have extremely low brightness. # But continue with the other checks in case the dark file is corrupted. - REMOVE_BAD_IMAGES_THRESHOLD_LOW=0 + LOW=0 fi # Find the full size image-*jpg and image-*png files (not the thumbnails) and @@ -117,9 +116,7 @@ num_bad=0 # If the LOW threshold is 0 it's disabled. # If the HIGH threshold is 0 or 100 (nothing can be brighter than 100) it's disabled. # Convert possibly 0.0 and 100.0 to 0 and 100 so we can check using bash. -HIGH=${REMOVE_BAD_IMAGES_THRESHOLD_HIGH} [[ $( echo "${HIGH} == 0 || ${HIGH} > 100" | bc ) -eq 1 ]] && HIGH=0 -LOW=${REMOVE_BAD_IMAGES_THRESHOLD_LOW} [[ $( echo "${LOW} <= 0" | bc ) -eq 1 ]] && LOW=0 # If we're processing a whole directory assume it's done in the background so "nice" it. @@ -136,7 +133,7 @@ for f in ${IMAGE_FILES} ; do if [[ ! -s ${f} ]]; then BAD="'${f}' (zero length)" else - if ! MEAN=$(${NICE} convert "${f}" -colorspace Gray -format "%[fx:image.mean]" info: 2>&1) ; then + if ! MEAN=$( ${NICE} convert "${f}" -colorspace Gray -format "%[fx:image.mean]" info: 2>&1 ) ; then # Do NOT set BAD since this isn't necessarily a problem with the file. echo -e "${RED}***${ME}: ERROR: 'convert ${f}' failed; leaving file.${NC}" >&2 echo -e "Message=${MEAN}" >&2 @@ -223,7 +220,7 @@ else # echo "xxxxxxxxxx ${BAD_COUNT} bad consecutive images" >&2 if [[ $((BAD_COUNT % BAD_LIMIT)) -eq 0 ]]; then MSG="Multiple bad consecutive bad images." - MSG="${MSG}\nCheck 'REMOVE_BAD_IMAGES_THRESHOLD_LOW' and 'REMOVE_BAD_IMAGES_THRESHOLD_HIGH' in config.sh" + MSG="${MSG}\nCheck 'Remove Bad Images Threshold Low' and 'Remove Bad Images Threshold High' in the WebUI." "${ALLSKY_SCRIPTS}/addMessage.sh" "warning" "${MSG}" >&2 fi if [[ ${BAD_COUNT} -ge "${BAD_LIMIT}" ]]; then diff --git a/scripts/saveImage.sh b/scripts/saveImage.sh index eec423e63..fee786b2c 100755 --- a/scripts/saveImage.sh +++ b/scripts/saveImage.sh @@ -1,25 +1,23 @@ #!/bin/bash # Script to save a DAY or NIGHT image. +# It goes in ${ALLSKY_TMP} where the WebUI and local Allsky Website can find it. -ME="$(basename "${BASH_ARGV0}")" +ME="$( basename "${BASH_ARGV0}" )" [[ ${ALLSKY_DEBUG_LEVEL} -ge 3 ]] && echo "${ME} $*" -#shellcheck disable=SC2086 source-path=. -source "${ALLSKY_HOME}/variables.sh" || exit ${ALLSKY_ERROR_STOP} -#shellcheck disable=SC2086 source-path=scripts -source "${ALLSKY_SCRIPTS}/functions.sh" || exit ${ALLSKY_ERROR_STOP} -#shellcheck disable=SC2086,SC1091 # file doesn't exist in GitHub -source "${ALLSKY_CONFIG}/config.sh" || exit ${ALLSKY_ERROR_STOP} +#shellcheck source-path=. +source "${ALLSKY_HOME}/variables.sh" || exit "${ALLSKY_ERROR_STOP}" +#shellcheck source-path=scripts +source "${ALLSKY_SCRIPTS}/functions.sh" || exit "${ALLSKY_ERROR_STOP}" usage_and_exit() { - retcode=${1} - [[ ${retcode} -ne 0 ]] && echo -ne "${RED}" + RET=${1} + [[ ${RET} -ne 0 ]] && echo -ne "${RED}" echo -n "Usage: ${ME} DAY|NIGHT full_path_to_image [variable=value [...]]" - [[ ${retcode} -ne 0 ]] && echo -e "${NC}" - # shellcheck disable=SC2086 - exit ${retcode} + [[ ${RET} -ne 0 ]] && echo -e "${NC}" + exit "${RET}" } [[ $# -lt 2 ]] && usage_and_exit 1 @@ -68,11 +66,17 @@ fi # The image may be in a memory filesystem, so do all the processing there and # leave the image used by the website(s) in that directory. -IMAGE_NAME=$(basename "${CURRENT_IMAGE}") # just the file name -WORKING_DIR=$(dirname "${CURRENT_IMAGE}") # the directory the image is currently in +IMAGE_NAME=$( basename "${CURRENT_IMAGE}" ) # just the file name +WORKING_DIR=$( dirname "${CURRENT_IMAGE}" ) # the directory the image is currently in # Optional full check for bad images. -if [[ ${REMOVE_BAD_IMAGES} == "true" ]]; then +HIGH="$( settings ".imageremovebadhigh" )" +LOW="$( settings ".imageremovebadlow" )" +# Make sure they are valid numbers. +[[ $( echo "${HIGH} == 0 || ${HIGH} > 100" | bc ) -eq 1 ]] && HIGH=0 +[[ $( echo "${LOW} <= 0" | bc ) -eq 1 ]] && LOW=0 + +if [[ ${HIGH} != "0" || ${LOW} != "0" ]]; then # If the return code is 99, the file was bad and deleted so don't continue. AS_BAD_IMAGES_MEAN="$( "${ALLSKY_SCRIPTS}/removeBadImages.sh" "${WORKING_DIR}" "${IMAGE_NAME}" )" # removeBadImages.sh displayed error message and deleted the file. @@ -87,14 +91,19 @@ fi # If we didn't execute removeBadImages.sh do a quick sanity check on the image. # OR, if we did execute removeBaImages.sh but we're cropping the image, get the image resolution. -if [[ ${REMOVE_BAD_IMAGES} != "true" || ${CROP_IMAGE} == "true" ]]; then +CROP_TOP="$( settings ".imagecroptop" )" +CROP_RIGHT="$( settings ".imagecropright" )" +CROP_BOTTOM="$( settings ".imagecropbottom" )" +CROP_LEFT="$( settings ".imagecropleft" )" +CROP_IMAGE=$(( CROP_TOP + CROP_RIGHT + CROP_BOTTOM + CROP_LEFT )) # will be > 0 if we're cropping +if [[ ${HIGH} != "0" || ${LOW} != "0" || ${CROP_IMAGE} -gt 0 ]]; then x=$(identify "${CURRENT_IMAGE}" 2>/dev/null) if [[ $? -ne 0 ]]; then echo -e "${RED}*** ${ME}: ERROR: '${CURRENT_IMAGE}' is corrupt; not saving.${NC}" exit 3 fi - if [[ ${CROP_IMAGE} == "true" ]]; then + if [[ ${CROP_IMAGE} -gt 0 ]]; then # Typical output: # image.jpg JPEG 4056x3040 4056x3040+0+0 8-bit sRGB 1.19257MiB 0.000u 0:00.000 RESOLUTION=$(echo "${x}" | awk '{ print $3 }') @@ -105,7 +114,6 @@ if [[ ${REMOVE_BAD_IMAGES} != "true" || ${CROP_IMAGE} == "true" ]]; then fi # Get passed-in variables. -# Normally at least the exposure will be passed and the sensor temp if known. while [[ $# -gt 0 ]]; do VARIABLE="AS_${1%=*}" # everything before the "=" VALUE="${1##*=}" # everything after the "=" @@ -115,13 +123,13 @@ while [[ $# -gt 0 ]]; do export ${VARIABLE}="${VALUE}" # need "export" to get indirection to work done # Export other variables so user can use them in overlays -export AS_CAMERA_TYPE="${CAMERA_TYPE}" -export AS_CAMERA_MODEL="${CAMERA_MODEL}" +export AS_CAMERA_TYPE="$( settings ".cameratype" )" +export AS_CAMERA_MODEL="$( settings ".cameramodel" )" if [[ -n ${AS_BAD_IMAGES_MEAN} ]]; then - export AS_MEAN_NORMALIZED="$( echo "${AS_BAD_IMAGES_MEAN} * 255" | bc )" # xxxx for testing + # xxxx TODO for testing. We'll eventually just use the mean passed to us. + export AS_MEAN_NORMALIZED="$( echo "${AS_BAD_IMAGES_MEAN} * 255" | bc )" fi - # If ${AS_TEMPERATURE_C} is set, use it as the sensor temperature, # otherwise use the temperature in ${TEMPERATURE_FILE}. # TODO: Currently nothing creates the TEMPERATURE_FILE. Eventually RPi cameras will. @@ -133,7 +141,7 @@ if [[ -z ${AS_TEMPERATURE_C} ]]; then fi # If taking dark frames, save the dark frame then exit. -if [[ $(settings ".takeDarkFrames") -eq 1 ]]; then +if [[ $( settings ".takedarkframes" ) == "true" ]]; then #shellcheck source-path=scripts source "${ALLSKY_SCRIPTS}/darkCapture.sh" exit 0 @@ -166,69 +174,60 @@ function display_error_and_exit() # error message, notification string # Don't let the service restart us because we will get the same error again. sudo systemctl stop allsky - # shellcheck disable=SC2086 - exit ${EXIT_ERROR_STOP} + exit "${EXIT_ERROR_STOP}" } # Resize the image if required -if [[ ${IMG_RESIZE} == "true" ]] ; then +RESIZE_W="$( settings ".imagresizewidth" )" +RESIZE_H="$( settings ".imagresizeheight" )" +if [[ ${RESIZE_W} -gt 0 && ${RESIZE_H} -gt 0 ]]; then # Make sure we were given numbers. ERROR_MSG="" - if [[ ${IMG_WIDTH} != +([+0-9]) ]]; then # no negative numbers allowed - ERROR_MSG="${ERROR_MSG}\nIMG_WIDTH (${IMG_WIDTH}) must be a number." + if [[ ${RESIZE_W} != +([+0-9]) ]]; then # no negative numbers allowed + ERROR_MSG="${ERROR_MSG}\n'Image Resize Height' (${RESIZE_W}) must be a number." fi - if [[ ${IMG_WIDTH} != +([+0-9]) ]]; then - ERROR_MSG="${ERROR_MSG}\nIMG_HEIGHT (${IMG_HEIGHT}) must be a number." + if [[ ${RESIZE_H} != +([+0-9]) ]]; then + ERROR_MSG="${ERROR_MSG}\n'Image Resize Width' (${RESIZE_H}) must be a number." fi if [[ -n ${ERROR_MSG} ]]; then echo -e "${RED}*** ${ME}: ERROR: Image resize number(s) invalid.${NC}" - display_error_and_exit "${ERROR_MSG}" "IMG_RESIZE" + display_error_and_exit "${ERROR_MSG}" "Image Resize" fi - [[ ${ALLSKY_DEBUG_LEVEL} -ge 4 ]] && echo "*** ${ME}: Resizing '${CURRENT_IMAGE}' to ${IMG_WIDTH}x${IMG_HEIGHT}" - if ! convert "${CURRENT_IMAGE}" -resize "${IMG_WIDTH}x${IMG_HEIGHT}" "${CURRENT_IMAGE}" ; then - echo -e "${RED}*** ${ME}: ERROR: IMG_RESIZE failed; not saving${NC}" - exit 4 - fi -fi - -# Crop the image if required -if [[ ${CROP_IMAGE} == "true" ]]; then - # If the image was just resized, the resolution changed, so reset the variables. - if [[ ${IMG_RESIZE} == "true" ]]; then - RESOLUTION_X=${IMG_WIDTH} - RESOLUTION_Y=${IMG_HEIGHT} - fi - - # Do some sanity checks on the CROP_* variables. - ERROR_MSG="" - # shellcheck disable=SC2153 - if ! E="$(checkPixelValue "CROP_WIDTH" "${CROP_WIDTH}" "width" "${RESOLUTION_X}")" ; then - ERROR_MSG="${ERROR_MSG}\n${E}" - fi - # shellcheck disable=SC2153 - if ! E="$(checkPixelValue "CROP_HEIGHT" "${CROP_HEIGHT}" "height" "${RESOLUTION_Y}")"; then - ERROR_MSG="${ERROR_MSG}\n${E}" + if [[ ${ALLSKY_DEBUG_LEVEL} -ge 3 ]]; then + echo "*** ${ME}: Resizing '${CURRENT_IMAGE}' to ${RESIZE_W}x${RESIZE_H}" fi - if ! E="$(checkPixelValue "CROP_OFFSET_X" "${CROP_OFFSET_X}" "width" "${RESOLUTION_X}" "any")" ; then - ERROR_MSG="${ERROR_MSG}\n${E}" - fi - if ! E="$(checkPixelValue "CROP_OFFSET_Y" "${CROP_OFFSET_Y}" "height" "${RESOLUTION_Y}" "any")" ; then - ERROR_MSG="${ERROR_MSG}\n${E}" + if ! convert "${CURRENT_IMAGE}" -resize "${RESIZE_W}x${RESIZE_H}" "${CURRENT_IMAGE}" ; then + echo -e "${RED}*** ${ME}: ERROR: image resize failed; not saving${NC}" + exit 4 fi - # Now for more intensive checks. - if [[ -z ${ERROR_MSG} ]]; then - ERROR_MSG="$(checkCropValues "${CROP_WIDTH}" "${CROP_HEIGHT}" \ - "${CROP_OFFSET_X}" "${CROP_OFFSET_Y}" \ - "${RESOLUTION_X}" "${RESOLUTION_Y}")" + if [[ ${CROP_IMAGE} -gt 0 ]]; then + # The image was just resized and the resolution changed, so reset the variables. + RESOLUTION_X=${RESIZE_W} + RESOLUTION_Y=${RESIZE_H} fi +fi +# Crop the image if required +if [[ ${CROP_IMAGE} -gt 0 ]]; then + # Perform basic checks on crop settings. + ERROR_MSG="$( checkCropValues "${CROP_TOP}" "${CROP_RIGHT}" "${CROP_BOTTOM}" "${CROP_LEFT}" \ + "${RESOLUTION_X}" "${RESOLUTION_Y}" )" if [[ -z ${ERROR_MSG} ]]; then - if [[ ${ALLSKY_DEBUG_LEVEL} -ge 4 ]]; then + if [[ ${ALLSKY_DEBUG_LEVEL} -ge 3 ]]; then + CROP_WIDTH=$(( RESOLUTION_X - CROP_RIGHT - CROP_LEFT )) + CROP_HEIGHT=$(( RESOLUTION_Y - CROP_TOP - CROP_BOTTOM )) echo -e "*** ${ME} Cropping '${CURRENT_IMAGE}' to ${CROP_WIDTH}x${CROP_HEIGHT}." fi - convert "${CURRENT_IMAGE}" -gravity Center -crop "${CROP_WIDTH}x${CROP_HEIGHT}+${CROP_OFFSET_X}+${CROP_OFFSET_Y}" +repage "${CURRENT_IMAGE}" + C="" + [[ ${CROP_TOP} -ne 0 ]] && C="${C} -gravity North -chop 0x${CROP_TOP}" + [[ ${CROP_RIGHT} -ne 0 ]] && C="${C} -gravity East -chop ${CROP_RIGHT}x0" + [[ ${CROP_BOTTOM} -ne 0 ]] && C="${C} -gravity South -chop 0x${CROP_BOTTOM}" + [[ ${CROP_LEFT} -ne 0 ]] && C="${C} -gravity West -chop ${CROP_LEFT}x0" + + # shellcheck disable=SC2086 + convert "${CURRENT_IMAGE}" ${C} "${CURRENT_IMAGE}" if [ $? -ne 0 ] ; then echo -e "${RED}*** ${ME}: ERROR: CROP_IMAGE failed; not saving${NC}" exit 4 @@ -240,11 +239,21 @@ if [[ ${CROP_IMAGE} == "true" ]]; then fi # Stretch the image if required, but only at night. -if [[ ${DAY_OR_NIGHT} == "NIGHT" && ${AUTO_STRETCH} == "true" ]]; then - if [[ ${ALLSKY_DEBUG_LEVEL} -ge 4 ]]; then - echo "*** ${ME}: Stretching '${CURRENT_IMAGE}' by ${AUTO_STRETCH_AMOUNT}" +STRETCH_AMOUNT=0 +STRETCH_MIDPOINT=0 +if [[ ${DAY_OR_NIGHT} == "NIGHT" ]]; then + STRETCH_AMOUNT="$( settings ".imagestretchamountnighttime" )" + STRETCH_MIDPOINT="$( settings ".imagestretchmidpointnighttime" )" +else # DAY + STRETCH_AMOUNT="$( settings ".imagestretchamountdaytime" )" + STRETCH_MIDPOINT="$( settings ".imagestretchmidpointdaytime" )" +fi +if [[ ${STRETCH_AMOUNT} -gt 0 ]]; then + if [[ ${ALLSKY_DEBUG_LEVEL} -ge 3 ]]; then + echo "*** ${ME}: Stretching '${CURRENT_IMAGE}' by ${STRETCH_AMOUNT}" fi - convert "${CURRENT_IMAGE}" -sigmoidal-contrast "${AUTO_STRETCH_AMOUNT}x${AUTO_STRETCH_MID_POINT}" "${CURRENT_IMAGE}" + convert "${CURRENT_IMAGE}" -sigmoidal-contrast "${STRETCH_AMOUNT}x${STRETCH_MIDPOINT}%" "${CURRENT_IMAGE}" + if [ $? -ne 0 ] ; then echo -e "${RED}*** ${ME}: ERROR: AUTO_STRETCH failed; not saving${NC}" exit 4 @@ -254,10 +263,10 @@ fi if [ "${DAY_OR_NIGHT}" = "NIGHT" ] ; then # The 12 hours ago option ensures that we're always using today's date # even at high latitudes where civil twilight can start after midnight. - export DATE_NAME="$(date -d '12 hours ago' +'%Y%m%d')" + export DATE_NAME="$( date -d '12 hours ago' +'%Y%m%d' )" else # During the daytime we alway save the file in today's directory. - export DATE_NAME="$(date +'%Y%m%d')" + export DATE_NAME="$( date +'%Y%m%d' )" fi "${ALLSKY_SCRIPTS}/flow-runner.py" @@ -270,8 +279,10 @@ rm -f "${PID_FILE}" SAVED_FILE="${CURRENT_IMAGE}" # The name of the file saved from the camera. WEBSITE_FILE="${WORKING_DIR}/${FULL_FILENAME}" # The name of the file the websites look for +TIMELAPSE_MINI_UPLOAD_VIDEO="$( settings ".minitimelapseupload" )" # If needed, save the current image in today's directory. -if [[ $(settings ".saveDaytimeImages") -eq 1 || ${DAY_OR_NIGHT} == "NIGHT" ]]; then +if [[ ( $( settings ".savedaytimeimages" ) == "true" && ${DAY_OR_NIGHT} == "DAY" ) || + ( $( settings ".savenighttimeimages" ) == "true" && ${DAY_OR_NIGHT} == "NIGHT" ) ]]; then SAVE_IMAGE="true" else SAVE_IMAGE="false" @@ -281,22 +292,23 @@ if [[ ${SAVE_IMAGE} == "true" ]]; then if [[ ${DAY_OR_NIGHT} == "NIGHT" ]]; then # The 12 hours ago option ensures that we're always using today's date # even at high latitudes where civil twilight can start after midnight. - DATE_NAME="$(date -d '12 hours ago' +'%Y%m%d')" + DATE_NAME="$( date -d '12 hours ago' +'%Y%m%d' )" else # During the daytime we alway save the file in today's directory. - DATE_NAME="$(date +'%Y%m%d')" + DATE_NAME="$( date +'%Y%m%d' )" fi DATE_DIR="${ALLSKY_IMAGES}/${DATE_NAME}" mkdir -p "${DATE_DIR}" - if [[ ${IMG_CREATE_THUMBNAILS} == "true" ]]; then + if [[ $( settings ".imagecreatethumbnails" ) == "true" ]]; then THUMBNAILS_DIR="${DATE_DIR}/thumbnails" mkdir -p "${THUMBNAILS_DIR}" # Create a thumbnail of the image for faster load in the WebUI. # If we resized above, this will be a resize of a resize, # but for thumbnails that should be ok. - convert "${CURRENT_IMAGE}" -resize "${THUMBNAIL_SIZE_X}x${THUMBNAIL_SIZE_Y}" "${THUMBNAILS_DIR}/${IMAGE_NAME}" - if [ $? -ne 0 ] ; then + X="$( settings ".thumbnailsizex" )" + Y="$( settings ".thumbnailsizey" )" + if ! convert "${CURRENT_IMAGE}" -resize "${X}x${Y}" "${THUMBNAILS_DIR}/${IMAGE_NAME}" ; then echo -e "${YELLOW}*** ${ME}: WARNING: THUMBNAIL resize failed; continuing.${NC}" fi fi @@ -306,9 +318,16 @@ if [[ ${SAVE_IMAGE} == "true" ]]; then FINAL_FILE="${DATE_DIR}/${IMAGE_NAME}" if cp "${CURRENT_IMAGE}" "${FINAL_FILE}" ; then + TIMELAPSE_MINI_IMAGES="$( settings ".minitimelapsenumimages" )" + TIMELAPSE_MINI_FREQUENCY="$( settings ".minitimelapsefrequency" )" if [[ ${TIMELAPSE_MINI_IMAGES} -ne 0 && ${TIMELAPSE_MINI_FREQUENCY} -ne 1 ]]; then + TIMELAPSE_MINI_FORCE_CREATION="$( settings ".minitimelapseforcecreation" )" # We are creating mini-timelapses; see if we should create one now. + CREATE="false" + MOD=0 + + # See how many images we have and how many are left. MINI_TIMELAPSE_FILES="${ALLSKY_TMP}/mini-timelapse_files.txt" # List of files if [[ ! -f ${MINI_TIMELAPSE_FILES} ]]; then # The file may have been deleted for an unknown reason. @@ -324,65 +343,56 @@ if [[ ${SAVE_IMAGE} == "true" ]]; then fi NUM_IMAGES=$(wc -l < "${MINI_TIMELAPSE_FILES}") LEFT=$((TIMELAPSE_MINI_IMAGES - NUM_IMAGES)) - fi - [[ ${ALLSKY_DEBUG_LEVEL} -ge 4 ]] && echo -e "NUM_IMAGES=${NUM_IMAGES}" >&2 + MOD="$( echo "${NUM_IMAGES} % ${TIMELAPSE_MINI_FREQUENCY}" | bc )" - MOD=0 - if [[ ${TIMELAPSE_MINI_FORCE_CREATION} == "true" ]]; then - # We only force creation every${TIMELAPSE_MINI_FREQUENCY} images, - # and only when we haven't reached ${TIMELAPSE_MINI_IMAGES} or we're close. - if [[ ${LEFT} -lt ${TIMELAPSE_MINI_FREQUENCY} ]]; then - TIMELAPSE_MINI_FORCE_CREATION="false" - else - MOD="$(echo "${NUM_IMAGES} % ${TIMELAPSE_MINI_FREQUENCY}" | bc)" - [[ ${MOD} -ne 0 ]] && TIMELAPSE_MINI_FORCE_CREATION="false" + # If either of the following are true we'll create the mini-timelapse: + # 1. We have ${TIMELAPSE_MINI_IMAGES} (i.e., ${LEFT} -eq 0) + # 2. ${TIMELAPSE_MINI_FORCE_CREATION} == true AND we're at a + # ${TIMELAPSE_MINI_FREQUENCY} boundary (i.e., ${MOD} -eq 0) + + if [[ ${LEFT} -le 0 || + ( ${TIMELAPSE_MINI_FORCE_CREATION} == "true" && ${MOD} -eq 0 ) ]]; then + CREATE="true" fi fi - if [[ ${TIMELAPSE_MINI_FORCE_CREATION} == "true" || ${LEFT} -le 0 ]]; then + + if [[ ${CREATE} == "true" ]]; then + [[ ${ALLSKY_DEBUG_LEVEL} -ge 3 ]] && echo -e "NUM_IMAGES=${NUM_IMAGES}" + # Create a mini-timelapse # This ALLSKY_DEBUG_LEVEL should be same as what's in upload.sh - if [[ ${ALLSKY_DEBUG_LEVEL} -ge 4 ]]; then - # timelapse.sh produces a lot of debug output - D="--debug --debug" - elif [[ ${ALLSKY_DEBUG_LEVEL} -ge 2 ]]; then + # This causes timelapse.sh to print "before" and "after" debug messages. + if [[ ${ALLSKY_DEBUG_LEVEL} -ge 2 ]]; then D="--debug" else - D="" + D="--no-debug" fi O="${ALLSKY_TMP}/mini-timelapse.mp4" - # shellcheck disable=SC2086 - "${ALLSKY_SCRIPTS}"/timelapse.sh ${D} --lock --output "${O}" \ - --mini --images "${MINI_TIMELAPSE_FILES}" - RET=$? - if [[ ${RET} -ne 0 ]]; then + + "${ALLSKY_SCRIPTS}/timelapse.sh" --Last "$( basename "${FINAL_FILE}" )" \ + "${D}" --lock --output "${O}" --mini --images "${MINI_TIMELAPSE_FILES}" + if [[ $? -eq 0 ]]; then + # Remove the oldest files if we haven't reached the limit. + if [[ ${LEFT} -le 0 ]]; then + KEEP=$((TIMELAPSE_MINI_IMAGES - TIMELAPSE_MINI_FREQUENCY)) + x="$( tail -${KEEP} "${MINI_TIMELAPSE_FILES}" )" + echo -e "${x}" > "${MINI_TIMELAPSE_FILES}" + if [[ ${ALLSKY_DEBUG_LEVEL} -ge 3 ]]; then + echo -en "${YELLOW}${ME}: Replaced ${TIMELAPSE_MINI_FREQUENCY} oldest" + echo -e " timelapse file(s).${NC}" >&2 + fi + fi + else # failed so don't try to upload TIMELAPSE_MINI_UPLOAD_VIDEO="false" fi - if [[ ${ALLSKY_DEBUG_LEVEL} -ge 2 ]]; then - if [[ ${RET} -eq 0 ]]; then - echo "${ME}: mini-timelapse created (last image: ${IMAGE_NAME})" - else - echo "${ME}: mini-timelapse creation returned with RET=${RET} (last image: ${IMAGE_NAME})" - fi - fi - # Remove the oldest files, but not if we only created - # this mini-timelapse because of a force. - if [[ ${RET} -eq 0 && (${MOD} -ne 0 || ${TIMELAPSE_MINI_FORCE_CREATION} == "false") ]]; then - KEEP=$((TIMELAPSE_MINI_IMAGES - TIMELAPSE_MINI_FREQUENCY)) - x="$(tail -${KEEP} "${MINI_TIMELAPSE_FILES}")" - echo -e "${x}" > "${MINI_TIMELAPSE_FILES}" - if [[ ${ALLSKY_DEBUG_LEVEL} -ge 4 ]]; then - echo -en "${YELLOW}${ME}: Replaced ${TIMELAPSE_MINI_FREQUENCY} oldest" - echo -e " file(s) and added current image.${NC}" >&2 - fi - fi else # Not ready to create yet - if [[ ${ALLSKY_DEBUG_LEVEL} -ge 4 ]]; then - echo -n "${ME}: Not creating mini timelapse: " + if [[ ${ALLSKY_DEBUG_LEVEL} -ge 3 ]]; then + echo -n "NUM_IMAGES=${NUM_IMAGES}: Not creating mini-timelapse: " if [[ ${MOD} -eq 0 ]]; then - echo "${LEFT} images(s) left." + echo "${LEFT} images(s) left." # haven't reached limit else echo "$((TIMELAPSE_MINI_FREQUENCY - MOD)) images(s) left in frequency." fi @@ -398,16 +408,17 @@ if [[ ${SAVE_IMAGE} == "true" ]]; then fi fi -if [[ ${IMG_UPLOAD} == "true" || (${TIMELAPSE_MINI_UPLOAD_VIDEO} == "true" && ${SAVE_IMAGE} == "true") ]]; then - #shellcheck disable=SC2086,SC1091 # file doesn't exist in GitHub - source "${ALLSKY_CONFIG}/ftp-settings.sh" || exit ${ALLSKY_ERROR_STOP} +if [[ ${TIMELAPSE_MINI_UPLOAD_VIDEO} == "false" ]]; then + # Don't deleate a lock file that belongs to another running process. + ALLSKY_TIMELAPSE_PID_FILE="" fi # If upload is true, optionally create a smaller version of the image; either way, upload it RET=0 -if [[ ${IMG_UPLOAD} == "true" ]]; then +IMG_UPLOAD_FREQUENCY="$( settings ".imageuploadfrequency" )" +if [[ ${IMG_UPLOAD_FREQUENCY} -gt 0 ]]; then # First check if we should upload this image - if [[ ${IMG_UPLOAD_FREQUENCY} != "1" ]]; then + if [[ ${IMG_UPLOAD_FREQUENCY} -ne 1 ]]; then FREQUENCY_FILE="${ALLSKY_TMP}/IMG_UPLOAD_FREQUENCY.txt" if [[ ! -f ${FREQUENCY_FILE} ]]; then # The file may have been deleted, or the user may have just changed the frequency. @@ -423,7 +434,7 @@ if [[ ${IMG_UPLOAD} == "true" ]]; then LEFT=$((LEFT - 1)) echo "${LEFT}" > "${FREQUENCY_FILE}" # This ALLSKY_DEBUG_LEVEL should be same as what's in upload.sh - [[ ${ALLSKY_DEBUG_LEVEL} -ge 4 ]] && echo "${ME}: Not uploading image: ${LEFT} images(s) left." + [[ ${ALLSKY_DEBUG_LEVEL} -ge 3 ]] && echo "${ME}: Not uploading image: ${LEFT} images(s) left." # We didn't create ${WEBSITE_FILE} yet so do that now. mv "${CURRENT_IMAGE}" "${WEBSITE_FILE}" @@ -432,16 +443,21 @@ if [[ ${IMG_UPLOAD} == "true" ]]; then fi fi - # We no longer use the "permanent" image name; instead, use the one the user specified - # in the config file (${FULL_FILENAME}). + W="$( settings ".imageresizeuploadswidth" )" + H="$( settings ".imageresizeuploadsheight" )" + if [[ ${W} -gt 0 && ${H} -gt 0 ]]; then + RESIZE_UPLOADS="true" + else + RESIZE_UPLOADS="false" + fi if [[ ${RESIZE_UPLOADS} == "true" ]]; then # Need a copy of the image since we are going to resize it. # Put the copy in ${WORKING_DIR}. FILE_TO_UPLOAD="${WORKING_DIR}/resize-${IMAGE_NAME}" - S="${RESIZE_UPLOADS_WIDTH}x${RESIZE_UPLOADS_HEIGHT}" - [ "${ALLSKY_DEBUG_LEVEL}" -ge 4 ] && echo "*** ${ME}: Resizing upload file '${FILE_TO_UPLOAD}' to ${S}" + S="${W}x${H}" + [ "${ALLSKY_DEBUG_LEVEL}" -ge 3 ] && echo "*** ${ME}: Resizing upload file '${FILE_TO_UPLOAD}' to ${S}" if ! convert "${CURRENT_IMAGE}" -resize "${S}" -gravity East -chop 2x0 "${FILE_TO_UPLOAD}" ; then - echo -e "${YELLOW}*** ${ME}: WARNING: RESIZE_UPLOADS failed; continuing with larger image.${NC}" + echo -e "${YELLOW}*** ${ME}: WARNING: Resize Uploads failed; continuing with larger image.${NC}" # We don't know the state of $FILE_TO_UPLOAD so use the larger file. FILE_TO_UPLOAD="${CURRENT_IMAGE}" fi @@ -449,50 +465,56 @@ if [[ ${IMG_UPLOAD} == "true" ]]; then FILE_TO_UPLOAD="${CURRENT_IMAGE}" fi - if [[ ${IMG_UPLOAD_ORIGINAL_NAME} == "true" ]]; then + RET=0 + if [[ $( settings ".remotewebsiteimageuploadoriginalname" ) == "true" ]]; then DESTINATION_NAME="" else DESTINATION_NAME="${FULL_FILENAME}" fi - - "${ALLSKY_SCRIPTS}/upload.sh" "${FILE_TO_UPLOAD}" "${IMAGE_DIR}" "${DESTINATION_NAME}" "SaveImage" "${WEB_IMAGE_DIR}" - RET=$? + # Goes in root of Website so second arg is "". + upload_all --remote-web "${FILE_TO_UPLOAD}" "" "${DESTINATION_NAME}" "SaveImage" + ((RET += $?)) + if [[ $( settings ".remoteserverimageuploadoriginalname" ) == "true" ]]; then + DESTINATION_NAME="" + else + DESTINATION_NAME="${FULL_FILENAME}" + fi + # Goes in root of Website so second arg is "". + upload_all --remote-server "${FILE_TO_UPLOAD}" "" "${DESTINATION_NAME}" "SaveImage" + ((RET += $?)) [[ ${RESIZE_UPLOADS} == "true" ]] && rm -f "${FILE_TO_UPLOAD}" # was a temporary file fi -# If needed, upload the mini timelapse. If upload.sh failed above, it will likely fail below. +# If needed, upload the mini timelapse. If the upload failed above, it will likely fail below. if [[ ${TIMELAPSE_MINI_UPLOAD_VIDEO} == "true" && ${SAVE_IMAGE} == "true" && ${RET} -eq 0 ]] ; then MINI="mini-timelapse.mp4" FILE_TO_UPLOAD="${ALLSKY_TMP}/${MINI}" - "${ALLSKY_SCRIPTS}/upload.sh" "${FILE_TO_UPLOAD}" "${IMAGE_DIR}" "${MINI}" "MiniTimelapse" "${WEB_IMAGE_DIR}" + upload_all --remote-web --remote-server "${FILE_TO_UPLOAD}" "" "${MINI}" "MiniTimelapse" RET=$? - if [[ ${RET} -eq 0 && ${TIMELAPSE_MINI_UPLOAD_THUMBNAIL} == "true" ]]; then + if [[ ${RET} -eq 0 && $( settings ".minitimelapseuploadthumbnail" ) == "true" ]]; then UPLOAD_THUMBNAIL_NAME="mini-timelapse.jpg" UPLOAD_THUMBNAIL="${ALLSKY_TMP}/${UPLOAD_THUMBNAIL_NAME}" # Create the thumbnail for the mini timelapse, then upload it. rm -f "${UPLOAD_THUMBNAIL}" make_thumbnail "00" "${FILE_TO_UPLOAD}" "${UPLOAD_THUMBNAIL}" if [[ ! -f ${UPLOAD_THUMBNAIL} ]]; then - echo "${ME}Mini timelapse thumbnail not created!" + echo "${ME}: Mini timelapse thumbnail not created!" else # Use --silent because we just displayed message(s) above for this image. - if [[ -n ${WEB_VIDEOS_DIR} ]]; then - x="${WEB_VIDEOS_DIR}/thumbnails" - else - x="" - fi - "${ALLSKY_SCRIPTS}/upload.sh" --silent \ + upload_all --remote-web --remote-server --silent \ "${UPLOAD_THUMBNAIL}" \ - "${IMAGE_DIR}" \ + "" \ "${UPLOAD_THUMBNAIL_NAME}" \ - "MiniThumbnail" \ - "${x}" + "MiniThumbnail" fi fi fi +# We're done so remove the lock file. +[[ -n ${ALLSKY_TIMELAPSE_PID_FILE} ]] && rm -f "${ALLSKY_TIMELAPSE_PID_FILE}" + # We create ${WEBSITE_FILE} as late as possible to avoid it being overwritten. mv "${SAVED_FILE}" "${WEBSITE_FILE}" diff --git a/scripts/testUpload.sh b/scripts/testUpload.sh new file mode 100755 index 000000000..93811f2cf --- /dev/null +++ b/scripts/testUpload.sh @@ -0,0 +1,228 @@ +#!/bin/bash + +# Run upload.sh using a test file and if it doesn't work, parse the output +# looking for errors that are easy for users to miss. + +# Allow this script to be executed manually, which requires ALLSKY_HOME to be set. +[[ -z ${ALLSKY_HOME} ]] && export ALLSKY_HOME="$( realpath "$( dirname "${BASH_ARGV0}" )/.." )" +ME="$( basename "${BASH_ARGV0}" )" + +#shellcheck source-path=. +source "${ALLSKY_HOME}/variables.sh" || exit "${ALLSKY_ERROR_STOP}" +#shellcheck source-path=scripts +source "${ALLSKY_SCRIPTS}/functions.sh" || exit "${ALLSKY_ERROR_STOP}" + +usage_and_exit() +{ + local RET=${1} + [[ ${RET} -ne 0 ]] && echo -en "${RED}" >&2 + + echo "Usage: ${ME} [--help] --website | --server ......." >&2 + + [[ ${RET} -ne 0 ]] && echo -e "${NC}" >&2 + + exit "${RET}" +} + +DEBUG="false" +DO_WEBSITE="false" +DO_SERVER="false" +while [[ $# -gt 0 ]]; do + ARG="${1}" + case "${ARG}" in + --help) + HELP="true" + ;; + --debug) + DEBUG="true" + ;; + --website) + DO_WEBSITE="true" + ;; + --server) + DO_SERVER="true" + ;; + -*) + echo -e "${RED}Unknown argument '${ARG}'.${NC}" >&2 + RET=1 + ;; + *) + break # done with arguments + ;; + esac + shift +done +[[ ${HELP} == "true" ]] && usage_and_exit 0 +[[ ${DO_WEBSITE} == "false" && ${DO_SERVER} == "false" ]] && usage_and_exit 1 + +# Parse the output and provide fixes when possible. +parse_output() +{ local OUTPUT="${1}" + local TYPE="${2}" + + local PROTOCOL DIR HOST USER S + + if [[ ${TYPE} == "REMOTEWEBSITE" ]]; then + PROTOCOL=".remotewebsiteprotocol" + DIR=".remotewebsiteimagedir" + S="Remote Website Settings" + else + PROTOCOL=".remoteserverprotocol" + DIR=".remoteserverimagedir" + S="Remote Server Settings" + fi + HOST="${TYPE}_HOST" + USER="${TYPE}_USER" + + # Parse output. + if grep -i --silent "host name resolve timeout" "${OUTPUT}" ; then + HOST="$( settings ".${HOST}" "${ENV_FILE}" )" + echo -e "\t* Host name '${HOST}' not found." >&2 + echo -e "\t FIX: Check the spelling of the server." >&2 + echo -e "\t Make sure your network is up." >&2 + echo -e "\t Make sure the network the server is on is up." >&2 + fi + + if grep -E -i --silent "User cannot log in|Login failed|Login incorrect" "${OUTPUT}" ; then + echo -e "\t* Unable to login." >&2 + echo -e "\t FIX: Make sure the username and password are correct." >&2 + fi + + if grep -i --silent "max-retries exceeded" "${OUTPUT}" ; then + echo -e "\t* Unable to login for unknown reason." >&2 + echo -e "\t FIX: Make sure the port is correct and your network is working." >&2 + PROTOCOL="$( settings "${PROTOCOL}" )" + if [[ ${PROTOCOL} == "sftp" ]]; then + HOST="$( settings ".${HOST}" "${ENV_FILE}" )" + USER="$( settings ".${USER}" "${ENV_FILE}" )" + echo -e "\t On your Pi, run: ssh ${USER}@${HOST}" >&2 + echo -e "\t When prompted to enter 'yes' or 'no', enter 'yes'." >&2 + echo -e "\t You may need to do this if the IP address of your Pi changed." >&2 + fi + fi + + if grep -i --silent "The system cannot find the file specified" "${OUTPUT}" ; then + if grep -i --silent "is current directory" "${OUTPUT}" ; then + echo -e "\t* Login succeeded but unknown location found." >&2 + else + # This should never happen. + # If we can't login we wouldn't know if the location was there. + echo -e "\t* Login failed and unknown location found." >&2 + fi + DIR="$( settings "${DIR}" )" + if [[ -n ${DIR} ]]; then + echo -e "\t The 'Image Directory' in the WebUI's '${S}' section is '${DIR}'." >&2 + echo -e "\t FIX: make sure that directory exists on the server." >&2 + else + echo -e "\t The 'Image Directory' in the WebUI's '${S}' section is empty." >&2 + # TODO: can this ever happen? + echo -e "\t FIX: unknown - not sure why this failed." >&2 + fi + fi + + # Certificate-related issues + if grep -i --silent "The authenticity of host" "${OUTPUT}" ; then + HOST="$( settings ".${HOST}" "${ENV_FILE}" )" + USER="$( settings ".${USER}" "${ENV_FILE}" )" + PROTOCOL="$( settings "${PROTOCOL}" )" + echo -e "\t* The remote machine doesn't know about your Pi." >&2 + if [[ ${PROTOCOL} == "sftp" ]]; then + echo -e "\t This happens the first time you use Protocol 'sftp' on a new Pi." >&2 + echo -e "\t FIX: On your Pi, run: ssh ${USER}@${HOST}" >&2 + echo -e "\t When prompted to enter 'yes' or 'no', enter 'yes'." >&2 + echo -e "\t You may need to do this if the IP address of your Pi changed." >&2 + else + echo -e "\t This error usually only happens when using Protocol 'sftp' on a new Pi." >&2 + echo -e "\t You are using Protocol '${PROTOCOL}', so no know fix exists." >&2 + fi + fi + + if grep -i --silent "certificate common name doesn't match requested host name" "${OUTPUT}" ; then + echo -e "\t* Certificate verification issue." >&2 + echo -e "\t FIX: Do one of the following on your Pi:" >&2 + echo -e "\t echo 'set ssl:check-hostname' > ~/.lftprc" >&2 + echo -e "\t or" >&2 + echo -e "\t In the WebUI's '${S}' section set 'FTP Commands' to 'set ssl:check-hostname'." >&2 + fi + + if grep -i --silent "Not trusted" "${OUTPUT}" ; then + echo -e "\t* Certificate verification issue." >&2 + echo -e "\t FIX: o one of the following on your Pi:" >&2 + echo -e "\t echo 'set ssl:verify-certificate no' > ~/.lftprc" >&2 + echo -e "\t or" >&2 + echo -e "\t In the WebUI's '${S}' section set 'FTP Commands' to 'set ssl:verify-certificate no'." >&2 + fi + + + if [[ -s ${OUTPUT} || ${DEBUG} == "true" ]]; then + echo -e "\n==================== OUTPUT:" >&2 + indent "${YELLOW}$( < "${OUTPUT}" )${NC}\n" + fi +} + + +# Test an upload. +do_test() +{ + local TYPE="${1}" + + local TEST_FILE bTEST_FILE OUTPUT_FILE HUMAN_TYPE PROTOCOL DIR REMOTE CMD D + + TEST_FILE="/tmp/${ME}.txt" + bTEST_FILE="$( basename "${TEST_FILE}" )" + echo "Test file for ${TYPE}" > "${TEST_FILE}" || return 1 + + OUTPUT_FILE="/tmp/${ME}-${TYPE}.txt" + + if [[ ${TYPE} == "REMOTEWEBSITE" ]]; then + HUMAN_TYPE="Remote Website" + PROTOCOL=".remotewebsiteprotocol" + DIR=".remotewebsiteimagedir" + REMOTE="web" + else + HUMAN_TYPE="Remote Server" + PROTOCOL=".remoteserverprotocol" + DIR=".remoteserverimagedir" + REMOTE="server" + fi + + PROTOCOL="$( settings "${PROTOCOL}" )" + if [[ $? -ne 0 || -z ${PROTOCOL} ]]; then + echo -e "${RED}${ME}: could not find protocol for ${HUMAN_TYPE}; unable to test.${NC}" >&2 + return 1 + fi + + DIR="$( settings "${DIR}" )" + DIR="${DIR:=null}" + + CMD="${ALLSKY_SCRIPTS}/upload.sh --debug --remote ${REMOTE} ${TEST_FILE} ${DIR} ${bTEST_FILE} ${ME}" + [[ ${DEBUG} == "true" ]] && echo -e "Executing:\n\t${CMD}" + ${CMD} > "${OUTPUT_FILE}" 2>&1 + RET=$? + if [[ ${RET} -eq 0 ]]; then + echo -e "${GREEN}Upload to ${HUMAN_TYPE} succeeded.${NC}" + if [[ -z ${DIR} || ${DIR} == "null" ]]; then + D="" + else + D="${DIR}/" + fi + echo -e "\tPlease remove '${D}${bTEST_FILE}' on your server." + if [[ -s ${OUTPUT_FILE} && ${DEBUG} == "true" ]]; then + echo -e "OUTPUT:" + echo -e "${YELLOW}$( < "${OUTPUT_FILE}" )${NC}\n" + fi + else + echo -e "${RED}Upload to ${HUMAN_TYPE} FAILED with RET=${RET}.${NC}" + parse_output "${OUTPUT_FILE}" "${TYPE}" + fi + + rm -f "${TEST_FILE}" + [[ ! -s ${OUTPUT_FILE} ]] && rm -f "${OUTPUT_FILE}" + exit "${RET}" +} + +# ========================= main body of program +[[ ${DO_WEBSITE} == "true" ]] && do_test "REMOTEWEBSITE" +[[ ${DO_SERVER} == "true" ]] && do_test "REMOTESERVER" + +exit 0 diff --git a/scripts/timelapse.sh b/scripts/timelapse.sh index f78897927..9457c5fd7 100755 --- a/scripts/timelapse.sh +++ b/scripts/timelapse.sh @@ -1,19 +1,17 @@ #!/bin/bash # Allow this script to be executed manually, which requires ALLSKY_HOME to be set. -[[ -z ${ALLSKY_HOME} ]] && export ALLSKY_HOME="$(realpath "$(dirname "${BASH_ARGV0}")/..")" -ME="$(basename "${BASH_ARGV0}")" +[[ -z ${ALLSKY_HOME} ]] && export ALLSKY_HOME="$( realpath "$( dirname "${BASH_ARGV0}" )/.." )" +ME="$( basename "${BASH_ARGV0}" )" -#shellcheck disable=SC2086 source-path=. -source "${ALLSKY_HOME}/variables.sh" || exit ${ALLSKY_ERROR_STOP} -#shellcheck disable=SC2086 source-path=scripts -source "${ALLSKY_SCRIPTS}/functions.sh" || exit ${ALLSKY_ERROR_STOP} -#shellcheck disable=SC2086,SC1091 # file doesn't exist in GitHub -source "${ALLSKY_CONFIG}/config.sh" || exit ${ALLSKY_ERROR_STOP} +#shellcheck source-path=. +source "${ALLSKY_HOME}/variables.sh" || exit "${ALLSKY_ERROR_STOP}" +#shellcheck source-path=scripts +source "${ALLSKY_SCRIPTS}/functions.sh" || exit "${ALLSKY_ERROR_STOP}" ENTERED="$*" -DEBUG=0 +DEBUG="false" HELP="false" IS_MINI="false" LOCK="false" @@ -25,7 +23,10 @@ while [[ $# -gt 0 ]]; do HELP="true" ;; -d | --debug) - ((DEBUG++)) + DEBUG="true" + ;; + --no-debug) + DEBUG="false" ;; -l | --lock) LOCK="true" @@ -38,6 +39,9 @@ while [[ $# -gt 0 ]]; do IMAGES_FILE="${2}" shift ;; + -L | --Last) # this is just so the last image name appears in "ps" output + shift + ;; -m | --mini) IS_MINI="true" ;; @@ -82,8 +86,7 @@ usage_and_exit() echo "'--mini' uses the MINI_TIMELAPSE settings and the timelapse file is" echo " called 'mini-timelapse.mp4' if '--output' isn't used." echo -en "${NC}" - # shellcheck disable=SC2086 - exit ${RET} + exit "${RET}" } if [[ -n ${IMAGES_FILE} ]]; then # If IMAGES_FILE is specified there should be no other arguments. @@ -94,11 +97,13 @@ fi [[ ${HELP} == "true" ]] && usage_and_exit 0 OUTPUT_DIR="" +LAST_IMAGE="" if [[ -n ${IMAGES_FILE} ]]; then if [[ ! -s ${IMAGES_FILE} ]]; then echo -e "${RED}*** ${ME} ERROR: '${IMAGES_FILE}' does not exist or is empty!${NC}" exit 3 fi + LAST_IMAGE="$( tail -1 "${IMAGES_FILE}" )" INPUT_DIR="" # Not used else INPUT_DIR="${1}" @@ -116,27 +121,48 @@ else fi fi +MY_PID="$$" +if [[ ${DEBUG} == "true" ]]; then + # Output one string so it's all on one line in log file. + MSG="${ME}: ${GREEN}Starting" + [[ ${IS_MINI} == "true" ]] && MSG="${MSG} mini " + MSG="${MSG}timelapse" + [[ -n ${LAST_IMAGE} ]] && MSG="${MSG}, last image = $( basename "${LAST_IMAGE}" )" + echo -e "${MSG}, my PID=${MY_PID}.${NC}" +fi + if [[ ${LOCK} == "true" ]]; then - PID_FILE="${ALLSKY_TMP}/timelapse-pid.txt" - ABORTED_MSG1="Another timelapse creation is in progress so this one was aborted." - ABORTED_FIELDS="$( basename "${OUTPUT_FILE}" )" + if [[ ${DEBUG} == "true" ]]; then + if [[ -s ${ALLSKY_TIMELAPSE_PID_FILE} ]]; then + echo " > ALLSKY_TIMELAPSE_PID_FILE contains $( < "${ALLSKY_TIMELAPSE_PID_FILE}" )" + else + echo " > No ALLSKY_TIMELAPSE_PID_FILE" + fi + fi + ABORTED_MSG1="Another timelapse creation is in progress so this one (${PPID}) was aborted." + ABORTED_FIELDS="$( basename "${OUTPUT_FILE}" )\tMY_PID=${MY_PID}\tPPID=${PPID}" ABORTED_MSG2="timelapse creations" if [[ ${IS_MINI} == "true" ]]; then CAUSED_BY="This could be caused by unreasonable TIMELAPSE_MINI_IMAGES and TIMELAPSE_MINI_FREQUENCY settings." else CAUSED_BY="Unknown cause - see /var/log/allsky.log." fi - if ! one_instance --pid-file "${PID_FILE}" \ + # We need to use the PID of our parent, not our PID, since our parent + # may also upload the timelapse file, and + if ! one_instance --pid-file "${ALLSKY_TIMELAPSE_PID_FILE}" --pid "${PPID}" \ --aborted-count-file "${ALLSKY_ABORTEDTIMELAPSE}" \ --aborted-fields "${ABORTED_FIELDS}" \ --aborted-msg1 "${ABORTED_MSG1}" --aborted-msg2 "${ABORTED_MSG2}" \ --caused-by "${CAUSED_BY}" ; then exit 5 fi - SEQUENCE_DIR="${ALLSKY_TMP}/sequence-lock-timelapse-$$" + if [[ ${DEBUG} == "true" ]]; then + echo " > Got lock, new PID=$( < "${ALLSKY_TIMELAPSE_PID_FILE}" )" + fi + SEQUENCE_DIR="${ALLSKY_TMP}/sequence-lock-timelapse" else - SEQUENCE_DIR="${ALLSKY_TMP}/sequence-timelapse-$$" - PID_FILE="" + SEQUENCE_DIR="${ALLSKY_TMP}/sequence-timelapse" + ALLSKY_TIMELAPSE_PID_FILE="" fi if [[ -z ${OUTPUT_FILE} ]]; then @@ -167,7 +193,8 @@ fi TMP="${ALLSKY_TMP}/timelapseTMP.txt" [[ ${IS_MINI} == "false" ]] && : > "${TMP}" # Only create when NOT doing mini-timelapses -if [[ ${KEEP_SEQUENCE} == "false" ]]; then +KEEP_SEQUENCE="$( settings ".timelapsekeepsequence" )" +if [[ ${KEEP_SEQUENCE} == "false" || ! -d ${SEQUENCE_DIR} ]]; then rm -fr "${SEQUENCE_DIR}" mkdir -p "${SEQUENCE_DIR}" @@ -197,8 +224,8 @@ if [[ ${KEEP_SEQUENCE} == "false" ]]; then # This user or something else may have removed it. if [[ ! -s ${IMAGE} ]]; then if [[ ! -f ${IMAGE} ]]; then -# TODO: would be nice to remove from the file, -# but we don't create/update the file so any change we make may be overwritten. + # It would be nice to remove from the file, but we don't + # create/update the file so any change we make may be overwritten. MSG="not found" else MSG="has nothing in it" @@ -212,49 +239,54 @@ if [[ ${KEEP_SEQUENCE} == "false" ]]; then NUM="$( printf "%04d" "${NUM_IMAGES}" )" ln -s "${IMAGE}" "${SEQUENCE_DIR}/${NUM}.${EXTENSION}" fi - done + done if [[ $? -ne 0 ]]; then echo -e "${RED}*** ${ME} ERROR: No images found in '${INPUT_DIR}'!${NC}" rm -fr "${SEQUENCE_DIR}" - [[ -n ${PID_FILE} ]] && rm -f "${PID_FILE}" + [[ -n ${ALLSKY_TIMELAPSE_PID_FILE} ]] && rm -f "${ALLSKY_TIMELAPSE_PID_FILE}" exit 1 fi else - echo -e "${ME} ${YELLOW}" - echo "Not regenerating sequence because KEEP_SEQUENCE is enabled." + echo -en "${ME} ${YELLOW}" + echo -n "Not regenerating sequence because 'Keep Timelapse Sequence' is enabled." echo -e "${NC}" fi -SCALE="" - # "-loglevel warning" gets rid of the dozens of lines of garbage output # but doesn't get rid of "deprecated pixel format" message when -pix_ftm is "yuv420p". -# set FFLOG=info in config.sh if you want to see what's going on for debugging. if [[ ${IS_MINI} == "true" ]]; then - FPS="${TIMELAPSE_MINI_FPS}" - TIMELAPSE_BITRATE="${TIMELAPSE_MINI_BITRATE}" - if [[ ${TIMELAPSE_MINI_WIDTH} != "0" ]]; then - SCALE="-filter:v scale=${TIMELAPSE_MINI_WIDTH}:${TIMELAPSE_MINI_HEIGHT}" - fi -elif [[ ${TIMELAPSEWIDTH} != "0" ]]; then - SCALE="-filter:v scale=${TIMELAPSEWIDTH}:${TIMELAPSEHEIGHT}" + FPS="$( settings ".minitimelapsefps" )" + TIMELAPSE_BITRATE="$( settings ".minitimelapsebitrate" )" + W="$( settings ".minitimelapsewidth" )" + H="$( settings ".minitimelapseheight" )" +else + FPS="$( settings ".timelapsefps" )" + TIMELAPSE_BITRATE="$( settings ".timelapsebitrate" )" + W="$( settings ".timelapsewidth" )" + H="$( settings ".timelapseheight" )" +fi +if [[ ${W} -gt 0 ]]; then + SCALE="-filter:v scale=${W}:${H}" +else + SCALE="" fi -# shellcheck disable=SC2086 +FFLOG="$( settings ".timelapsefflog" )" +# shellcheck disable=SC2086,SC2046 X="$(ffmpeg -y -f image2 \ -loglevel "${FFLOG}" \ -r "${FPS}" \ -i "${SEQUENCE_DIR}/%04d.${EXTENSION}" \ - -vcodec "${VCODEC}" \ + -vcodec "$( settings ".timelapsevcodec" )" \ -b:v "${TIMELAPSE_BITRATE}" \ - -pix_fmt "${PIX_FMT}" \ + -pix_fmt "$( settings ".timelapsepixfmt" )" \ -movflags +faststart \ $SCALE \ - ${TIMELAPSE_EXTRA_PARAMETERS} \ + $( settings ".timelapseextraparameters" ) \ "${OUTPUT_FILE}" 2>&1)" RET=$? # The "deprecated..." message is useless and only confuses users, so hide it. -X="$(echo "${X}" | grep -v "deprecated pixel format used")" +X="$( echo "${X}" | grep -v "deprecated pixel format used" )" [ "${X}" != "" ] && echo "${X}" >> "${TMP}" # a warning/error message if [[ ${RET} -ne -0 ]]; then @@ -264,6 +296,7 @@ if [[ ${RET} -ne -0 ]]; then echo "Links in '${SEQUENCE_DIR}' left for debugging." echo -e "Remove them when the problem is fixed.${NC}\n" rm -f "${OUTPUT_FILE}" # don't leave around to confuse user + [[ -n ${ALLSKY_TIMELAPSE_PID_FILE} ]] && rm -f "${ALLSKY_TIMELAPSE_PID_FILE}" exit 1 fi @@ -278,16 +311,15 @@ fi # timelapse is uploaded via generateForDay.sh (usually via endOfNight.sh), which called us. -if [[ ${DEBUG} -ge 2 ]]; then - if [[ ${IS_MINI} == "true" ]]; then - M="Mini t" - else - M="T" - fi - echo -e "${ME}: ${GREEN}${M}imelapse in ${OUTPUT_FILE}${NC}" +if [[ ${DEBUG} == "true" ]]; then + # Output one string so it's all on one line in log file. + MSG="${ME}: ${GREEN}" + [[ ${IS_MINI} == "true" ]] && MSG="${MSG}mini " + MSG="${MSG}timelapse creation finished" + [[ -n ${LAST_IMAGE} ]] && MSG="${MSG}, last image = $( basename "${LAST_IMAGE}" )" + echo -e "${MSG}, my PID=${MY_PID}.${NC}" fi -[[ -n ${PID_FILE} ]] && rm -f "${PID_FILE}" - -exit 0 +# Let our parent remove ${ALLSKY_TIMELAPSE_PID_FILE} when done. +exit 0 \ No newline at end of file diff --git a/scripts/updateWebsiteConfig.sh b/scripts/updateWebsiteConfig.sh index ffcafc814..8c2a5a0a2 100755 --- a/scripts/updateWebsiteConfig.sh +++ b/scripts/updateWebsiteConfig.sh @@ -4,15 +4,13 @@ # If no file is specified, use the local one if it exists, else use the remote one. # Allow this script to be executed manually, which requires several variables to be set. -[[ -z ${ALLSKY_HOME} ]] && export ALLSKY_HOME="$(realpath "$(dirname "${BASH_ARGV0}")/..")" -ME="$(basename "${BASH_ARGV0}")" +[[ -z ${ALLSKY_HOME} ]] && export ALLSKY_HOME="$( realpath "$( dirname "${BASH_ARGV0}" )/.." )" +ME="$( basename "${BASH_ARGV0}" )" -#shellcheck disable=SC2086 source-path=. -source "${ALLSKY_HOME}/variables.sh" || exit ${ALLSKY_ERROR_STOP} -#shellcheck disable=SC2086 source-path=scripts -source "${ALLSKY_SCRIPTS}/functions.sh" || exit ${ALLSKY_ERROR_STOP} -#shellcheck disable=SC2086,SC1091 # file doesn't exist in GitHub -source "${ALLSKY_CONFIG}/config.sh" || exit ${ALLSKY_ERROR_STOP} +#shellcheck source-path=. +source "${ALLSKY_HOME}/variables.sh" || exit "${ALLSKY_ERROR_STOP}" +#shellcheck source-path=scripts +source "${ALLSKY_SCRIPTS}/functions.sh" || exit "${ALLSKY_ERROR_STOP}" function usage_and_exit() { @@ -24,9 +22,9 @@ function usage_and_exit() fi echo -e "${C}Usage: ${ME} [--help] [--debug] [--verbosity silent|summary|verbose] [--local | --remote | --config file] key label new_value [...]${wNC}" >&2 echo "There must be a multiple of 3 arguments." >&2 - # shellcheck disable=SC2086 - exit ${RET} + exit "${RET}" } + # Check arguments OK="true" HELP="false" @@ -120,7 +118,8 @@ while [[ $# -gt 0 ]]; do # Only put quotes around ${NEW_VALUE} if it's a string, # i.e., not a number or a special name. - if [[ ! (${NEW_VALUE} =~ ${NUMRE}) && ${NEW_VALUE} != "true" && ${NEW_VALUE} != "false" && ${NEW_VALUE} != "null" ]]; then + if [[ ! (${NEW_VALUE} =~ ${NUMRE}) && ${NEW_VALUE} != "true" && + ${NEW_VALUE} != "false" && ${NEW_VALUE} != "null" ]]; then Q='"' NEW_VALUE="${Q}${NEW_VALUE}${Q}" fi @@ -142,7 +141,7 @@ if [[ ${DEBUG} == "true" ]]; then echo -e "${wNC}" fi -if OUTPUT="$(jq "${S}" "${CONFIG_FILE}" 2>&1 > /tmp/x && mv /tmp/x "${CONFIG_FILE}")"; then +if OUTPUT="$( settings "${S}" "${CONFIG_FILE}" 2>&1 > /tmp/x && mv /tmp/x "${CONFIG_FILE}" )"; then if [[ ${VERBOSITY} == "verbose" ]]; then echo -e "${wOK}${OUTPUT_MESSAGE}${wNC}" elif [[ ${VERBOSITY} == "summary" ]]; then diff --git a/scripts/upload.sh b/scripts/upload.sh index acc6ee31a..ded62e360 100755 --- a/scripts/upload.sh +++ b/scripts/upload.sh @@ -4,25 +4,22 @@ # This is a separate script so it can also be used manually to test uploads. # Allow this script to be executed manually, which requires ALLSKY_HOME to be set. -[[ -z ${ALLSKY_HOME} ]] && export ALLSKY_HOME="$(realpath "$(dirname "${BASH_ARGV0}")/..")" -ME="$(basename "${BASH_ARGV0}")" +[[ -z ${ALLSKY_HOME} ]] && export ALLSKY_HOME="$( realpath "$( dirname "${BASH_ARGV0}" )/.." )" +ME="$( basename "${BASH_ARGV0}" )" -#shellcheck disable=SC2086 source-path=. -source "${ALLSKY_HOME}/variables.sh" || exit ${ALLSKY_ERROR_STOP} -#shellcheck disable=SC2086 source-path=scripts -source "${ALLSKY_SCRIPTS}/functions.sh" || exit ${ALLSKY_ERROR_STOP} -#shellcheck disable=SC2086,SC1091 # file doesn't exist in GitHub -source "${ALLSKY_CONFIG}/config.sh" || exit ${ALLSKY_ERROR_STOP} -#shellcheck disable=SC2086,SC1091 # file doesn't exist in GitHub -source "${ALLSKY_CONFIG}/ftp-settings.sh" || exit ${ALLSKY_ERROR_STOP} +#shellcheck source-path=. +source "${ALLSKY_HOME}/variables.sh" || exit "${ALLSKY_ERROR_STOP}" +#shellcheck source-path=scripts +source "${ALLSKY_SCRIPTS}/functions.sh" || exit "${ALLSKY_ERROR_STOP}" usage_and_exit() { RET=$1 [[ ${RET} -ne 0 ]] && echo -en "${RED}" echo "*** Usage: ${ME} [--help] [--wait] [--silent] [--debug] \\" + echo " { --local | --remote type } \\" echo " file_to_upload directory destination_file_name \\" - echo " [file_type] [local_directory]" + echo " [file_type]" [[ ${RET} -ne 0 ]] && echo -e "${NC}" echo @@ -30,17 +27,16 @@ usage_and_exit() { echo " '--help' displays this message and exits." echo " '--wait' waits for any upload of the same type to finish." echo " '--silent' doesn't display any status messages." + echo " '--local' copy to local Website." + echo " '--remote type' upload to remote 'web' or 'server'." echo " 'file_to_upload' is the path name of the file to upload." echo " 'directory' is the directory ON THE SERVER the file should be uploaded to." echo " 'destination_file_name' is the name the file should be called ON THE SERVER." echo " 'file_type' is an optional, temporary name to use when uploading the file." - echo " 'local_directory' is the name of an optional local directory the file should be" - echo " copied to IN ADDITION TO being uploaded to a remote server." echo echo "For example: ${ME} keogram-20230710.jpg /keograms keogram.jpg" - # shellcheck disable=SC2086 - exit ${RET} + exit "${RET}" } @@ -48,37 +44,53 @@ HELP="false" WAIT="false" SILENT="false" DEBUG="false" +LOCAL="false" +REMOTE_TYPE="" RET=0 while [[ $# -gt 0 ]]; do - case "${1}" in + ARG="${1}" + case "${ARG}" in --help) HELP="true" - shift ;; --wait) WAIT="true" - shift ;; --silent) SILENT="true" - shift ;; --debug) DEBUG="true" + ;; + --local) + LOCAL="true" + ;; + --remote) + REMOTE_TYPE="${2}" shift ;; -*) - echo -e "${RED}Unknown argument '${1}'.${NC}" >&2 - shift + echo -e "${RED}Unknown argument '${ARG}'.${NC}" >&2 RET=1 ;; *) break # done with arguments ;; esac + shift done -[[ $# -lt 3 || ${RET} -ne 0 ]] && usage_and_exit ${RET} +[[ $# -lt 3 || ${RET} -ne 0 ]] && usage_and_exit 1 [[ ${HELP} == "true" ]] && usage_and_exit 0 +[[ ${LOCAL} == "false" && -z ${REMOTE_TYPE} ]] && usage_and_exit 1 +[[ ${LOCAL} == "true" && -n ${REMOTE_TYPE} ]] && usage_and_exit 1 +if [[ -n ${REMOTE_TYPE} ]]; then + if [[ ${REMOTE_TYPE} != "web" && ${REMOTE_TYPE} != "server" ]]; then + echo -en "${RED}" >&2 + echo -n "*** ${ME}: ERROR: Unknown remote type: '${REMOTE_TYPE}'." >&2 + echo -e "${NC}" >&2 + exit 2 + fi +fi FILE_TO_UPLOAD="${1}" if [[ ! -f ${FILE_TO_UPLOAD} ]]; then @@ -88,7 +100,9 @@ if [[ ! -f ${FILE_TO_UPLOAD} ]]; then exit 2 fi -REMOTE_DIR="${2}" +DIRECTORY="${2}" +# Allow explicit empty directory. +[[ ${DIRECTORY} == "null" ]] && DIRECTORY="" DESTINATION_NAME="${3}" [[ -z ${DESTINATION_NAME} ]] && DESTINATION_NAME="$(basename "${FILE_TO_UPLOAD}")" # When run manually, the FILE_TYPE normally won't be given. @@ -101,16 +115,50 @@ if [[ -n ${COPY_TO} && ! -d ${COPY_TO} ]]; then exit 2 fi -# "put" to a temp name, then move the temp name to the final name. -# This is useful with slow uplinks where multiple lftp requests can be running at once, -# and only one lftp can upload the file at once, otherwise we get this error: -# put: Access failed: 550 The process cannot access the file because it is being used by -# another process. (image.jpg) -# Slow uplinks also cause problems with web servers that read the file as it's being uploaded. +PID_FILE="" + +function check_for_error_messages() +{ + local ERROR_MESSAGES="${1}" + # Output any error messages + if [[ -n ${ERROR_MESSAGES} ]]; then + echo -e "Upload output from '${FILE_TO_UPLOAD}:\n ${ERROR_MESSAGES}\n" >&2 + echo -e "${ERROR_MESSAGES}" > "${LOG}" + fi + [[ -n ${PID_FILE} ]] && rm -f "${PID_FILE}" +} # To save a write to the SD card, only save output to ${LOG} on error. LOG="${ALLSKY_TMP}/upload_errors.txt" +if [[ ${LOCAL} == "true" ]]; then + if [[ -z ${DIRECTORY} ]]; then + DIRECTORY="${ALLSKY_WEBSITE}" + elif [[ ${DIRECTORY:0:1} != "/" ]]; then + DIRECTORY="${ALLSKY_WEBSITE}/${DIRECTORY}" + fi + # No need to set the lock for local copies - they are fast. + if [[ ${SILENT} == "false" && ${ALLSKY_DEBUG_LEVEL} -ge 3 ]]; then + echo "${ME}: Copying ${FILE_TO_UPLOAD} to ${DIRECTORY}/${DESTINATION_NAME}" + fi + OUTPUT="$(cp "${FILE_TO_UPLOAD}" "${DIRECTORY}/${DESTINATION_NAME}" 2>&1)" + RET=$? + check_for_error_messages "${OUTPUT}" + exit "${RET}" +fi + + +############## Remote Website or server + + +# For uploads to a remote server, "put" to a temp name, then move the temp name to the final name. +# This is useful with slow uplinks where multiple upload requests can be running at once, +# and only one upload can upload the file at once. +# For lftp we get this error: +# put: Access failed: 550 The process cannot access the file because it is being used by +# another process. (image.jpg) +# Slow uplinks also cause problems with web servers that read the file as it's being uploaded. + # Make sure only one upload of this file type happens at once. # Multiple concurrent uploads (which can happen if the system and/or network is slow can # cause errors and files left on the server. @@ -134,48 +182,60 @@ if ! one_instance --sleep "${SLEEP}" --max-checks "${MAX_CHECKS}" --pid-file "${ exit 1 fi -# Convert to lowercase so we don't care if user specified upper or lowercase. -PROTOCOL="${PROTOCOL,,}" +if [[ ${REMOTE_TYPE} == "web" ]]; then + prefix="remotewebsite" + PREFIX="REMOTEWEBSITE" +else # "server" + prefix="remoteserver" + PREFIX="REMOTESERVER" +fi +PROTOCOL="$( settings ".${prefix}protocol" )" # SIGTERM is sent by systemctl to stop Allsky. -# SIGHUP is sent to have the capture program reload their arguments. -# Ignore them so we don't leave a temporary or partially uploaded file if the service is stopped -# in the middle of an upload. +# SIGHUP is sent to have the capture program reload its arguments. +# Ignore them so we don't leave a temporary or partially uploaded file if the service +# is stopped in the middle of an upload. trap "" SIGTERM trap "" SIGHUP if [[ ${PROTOCOL} == "s3" ]] ; then + AWS_CLI_DIR="$( settings ".${PREFIX}_AWS_CLI_DIR" "${ALLSKY_ENV}" )" + S3_BUCKET="$( settings ".${PREFIX}_S3_BUCKET" "${ALLSKY_ENV}" )" + S3_ACL="$( settings ".${PREFIX}_S3_ACL" "${ALLSKY_ENV}" )" if [[ ${SILENT} == "false" && ${ALLSKY_DEBUG_LEVEL} -ge 3 ]]; then - echo "${ME}: Uploading ${FILE_TO_UPLOAD} to aws ${S3_BUCKET}${REMOTE_DIR}/${DESTINATION_NAME}" - fi - OUTPUT="$("${AWS_CLI_DIR}/aws" s3 cp "${FILE_TO_UPLOAD}" "s3://${S3_BUCKET}${REMOTE_DIR}/${DESTINATION_NAME}" --acl "${S3_ACL}" 2>&1)" - RET=$? - - -elif [[ ${PROTOCOL} == "local" ]] ; then - if [[ ${SILENT} == "false" && ${ALLSKY_DEBUG_LEVEL} -ge 3 ]]; then - echo "${ME}: Copying ${FILE_TO_UPLOAD} to ${REMOTE_DIR}/${DESTINATION_NAME}" + MSG="${ME}: Uploading ${FILE_TO_UPLOAD} to" + MSG="${MSG} aws ${S3_BUCKET}${DIRECTORY}/${DESTINATION_NAME}" + echo "${MSG}" fi - OUTPUT="$(cp "${FILE_TO_UPLOAD}" "${REMOTE_DIR}/${DESTINATION_NAME}" 2>&1)" + OUTPUT="$( "${AWS_CLI_DIR}/aws" s3 cp "${FILE_TO_UPLOAD}" \ + "s3://${S3_BUCKET}${DIRECTORY}/${DESTINATION_NAME}" --acl "${S3_ACL}" 2>&1 )" RET=$? elif [[ "${PROTOCOL}" == "scp" ]] ; then + REMOTE_USER="$( settings ".${PREFIX}_USER" "${ALLSKY_ENV}" )" + REMOTE_HOST="$( settings ".${PREFIX}_HOST" "${ALLSKY_ENV}" )" + REMOTE_PORT="$( settings ".${PREFIX}_PORT" "${ALLSKY_ENV}" )" + SSH_KEY_FILE="$( settings ".${PREFIX}_SSH_KEY_FILE" "${ALLSKY_ENV}" )" if [[ ${SILENT} == "false" && ${ALLSKY_DEBUG_LEVEL} -ge 3 ]]; then - # shellcheck disable=SC2153 - echo "${ME}: Copying ${FILE_TO_UPLOAD} to ${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_DIR}/${DESTINATION_NAME}" + MSG="${ME}: Copying ${FILE_TO_UPLOAD} to" + MSG="${MSG} ${REMOTE_USER}@${REMOTE_HOST}:${DIRECTORY}/${DESTINATION_NAME}" + echo "${MSG}" fi [[ -n ${REMOTE_PORT} ]] && REMOTE_PORT="-P ${REMOTE_PORT}" # shellcheck disable=SC2086 - OUTPUT="$(scp -i "${SSH_KEY_FILE}" ${REMOTE_PORT} "${FILE_TO_UPLOAD}" "${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_DIR}/${DESTINATION_NAME}" 2>&1)" + OUTPUT="$( scp -i "${SSH_KEY_FILE}" ${REMOTE_PORT} "${FILE_TO_UPLOAD}" \ + "${REMOTE_USER}@${REMOTE_HOST}:${DIRECTORY}/${DESTINATION_NAME}" 2>&1 )" RET=$? elif [[ ${PROTOCOL} == "gcs" ]] ; then + GCS_BUCKET="$( settings ".${PREFIX}_GCS_BUCKET" "${ALLSKY_ENV}" )" + GCS_ACL="$( settings ".${PREFIX}_GCS_ACL" "${ALLSKY_ENV}" )" if [[ ${SILENT} == "false" && ${ALLSKY_DEBUG_LEVEL} -ge 3 ]]; then - echo "${ME}: Uploading ${FILE_TO_UPLOAD} to gcs ${GCS_BUCKET}${REMOTE_DIR}" + echo "${ME}: Uploading ${FILE_TO_UPLOAD} to gcs ${GCS_BUCKET}${DIRECTORY}" fi - OUTPUT="$(gsutil cp -a "${GCS_ACL}" "${FILE_TO_UPLOAD}" "gs://${GCS_BUCKET}${REMOTE_DIR}" 2>&1)" + OUTPUT="$(gsutil cp -a "${GCS_ACL}" "${FILE_TO_UPLOAD}" "gs://${GCS_BUCKET}${DIRECTORY}" 2>&1)" RET=$? @@ -185,20 +245,46 @@ else # sftp/ftp/ftps TEMP_NAME="${FILE_TYPE}-${RANDOM}" - # If REMOTE_DIR isn't null (which it can be) and doesn't already have a trailing "/", append one. - [[ -n ${REMOTE_DIR} && ${REMOTE_DIR: -1:1} != "/" ]] && REMOTE_DIR="${REMOTE_DIR}/" + # If directory is null (which it can be) put the file in the image directory + # which is the root. + if [[ -z ${DIRECTORY} ]]; then + IMAGE_DIR="$( settings ".${prefix}imagedir" )" + if [[ -n ${IMAGE_DIR} ]]; then + [[ ${IMAGE_DIR: -1:1} != "/" ]] && IMAGE_DIR="${IMAGE_DIR}/" + DIRECTORY="${IMAGE_DIR}" + fi + + elif [[ ${DIRECTORY: -1:1} != "/" ]]; then + # If DIRECTORY doesn't already have a trailing "/", append one. + DIRECTORY="${DIRECTORY}/" + fi if [[ ${SILENT} == "false" && ${ALLSKY_DEBUG_LEVEL} -ge 3 ]]; then - echo "${ME}: FTP '${FILE_TO_UPLOAD}' to '${REMOTE_DIR}${DESTINATION_NAME}', TEMP_NAME=${TEMP_NAME}" + MSG="${ME}: FTP '${FILE_TO_UPLOAD}' to" + MSG="${MSG} '${DIRECTORY}${DESTINATION_NAME}', TEMP_NAME=${TEMP_NAME}" + echo "${MSG}" fi # LFTP_CMDS needs to be unique per file type so we don't overwrite a different upload type. DIR="${ALLSKY_TMP}/lftp_cmds" - [[ ! -d ${DIR} ]] && mkdir "${DIR}" + if [[ ! -d ${DIR} ]]; then + if ! mkdir "${DIR}" ; then + echo -e "${RED}" + echo -e "*** ERROR: Unable to create '${DIR}'." + echo -e "${NC}" + ls -ld "${ALLSKY_TMP}" + exit 1 + fi + fi + LFTP_CMDS="${DIR}/${FILE_TYPE}.txt" set +H # This keeps "!!" from being processed in REMOTE_PASSWORD + REMOTE_USER="$( settings ".${PREFIX}_USER" "${ALLSKY_ENV}" )" + REMOTE_HOST="$( settings ".${PREFIX}_HOST" "${ALLSKY_ENV}" )" + REMOTE_PORT="$( settings ".${PREFIX}_PORT" "${ALLSKY_ENV}" )" # The export LFTP_PASSWORD has to be OUTSIDE the ( ) below. + REMOTE_PASSWORD="$( settings ".${PREFIX}_PASSWORD" "${ALLSKY_ENV}" )" if [[ ${DEBUG} == "true" ]]; then # In debug mode, include the password on the command line so it's easier # for the user to run "lftp -f ${LFT_CMDS}" @@ -211,6 +297,7 @@ else # sftp/ftp/ftps fi ( + LFTP_COMMANDS="$( settings ".${PREFIX}_LFTP_COMMANDS" "${ALLSKY_ENV}" )" [[ -n ${LFTP_COMMANDS} ]] && echo "${LFTP_COMMANDS}" # Sometimes have problems with "max-reties 1", so make it 2 @@ -235,11 +322,11 @@ else # sftp/ftp/ftps echo "ls" echo "debug 5" fi - if [[ -n ${REMOTE_DIR} ]]; then + if [[ -n ${DIRECTORY} ]]; then # lftp outputs error message so we don't have to. - echo "cd '${REMOTE_DIR}' || exit 1" + echo "cd '${DIRECTORY}' || exit 1" if [[ ${DEBUG} == "true" ]]; then - echo "echo 'In REMOTE_DIR=${REMOTE_DIR}:'" + echo "echo 'In DIRECTORY=${DIRECTORY}:'" echo "ls" fi fi @@ -271,6 +358,16 @@ else # sftp/ftp/ftps echo exit 0 ) > "${LFTP_CMDS}" + if [[ $? -ne 0 ]]; then + echo -e "${RED}" + echo -e "*** ERROR: Unable to create '${LFTP_CMDS}'." + echo -e "${NC}" + # Do ls of parent and grandparent. + PARENT="$( dirname "${LFTP_CMDS}" )" + GRANDPARENT="$( dirname "${PARENT}" )" + ls -ld "${PARENT}" "${GRANDPARENT}" + exit 1 + fi OUTPUT="$(lftp -f "${LFTP_CMDS}" 2>&1)" RET=$? @@ -288,7 +385,7 @@ else # sftp/ftp/ftps echo "FILE_TO_UPLOAD='${FILE_TO_UPLOAD}'" # shellcheck disable=SC2153 echo "REMOTE_HOST='${REMOTE_HOST}'" - echo "REMOTE_DIR='${REMOTE_DIR}'" + echo "DIRECTORY='${DIRECTORY}'" echo "TEMP_NAME='${TEMP_NAME}'" echo "DESTINATION_NAME='${DESTINATION_NAME}'" echo -en "${NC}" @@ -299,29 +396,12 @@ else # sftp/ftp/ftps echo -e "\n${YELLOW}Commands used${NC} are in: ${GREEN}${LFTP_CMDS}${NC}" else - if [[ ${SILENT} == "false" && ${ALLSKY_DEBUG_LEVEL} -ge 4 && ${ON_TTY} -eq 0 ]]; then + if [[ ${SILENT} == "false" && ${ALLSKY_DEBUG_LEVEL} -ge 3 && ${ON_TTY} == "false" ]]; then echo "${ME}: FTP '${FILE_TO_UPLOAD}' finished" fi fi fi -# Output any error messages -if [[ -n ${OUTPUT} ]]; then - echo -e "Upload output from '${FILE_TO_UPLOAD}:\n ${OUTPUT}\n" >&2 - echo -e "${OUTPUT}" > "${LOG}" -fi - -# If a local directory was also specified, copy the file there. -if [[ ${RET} -eq 0 && -n ${COPY_TO} ]]; then - if [[ ${SILENT} == "false" && ${ALLSKY_DEBUG_LEVEL} -ge 3 ]]; then - # No need to specify the file being copied again since we did so above. - echo "${ME}: Also copying to ${COPY_TO}/${DESTINATION_NAME}" - fi - cp "${FILE_TO_UPLOAD}" "${COPY_TO}/${DESTINATION_NAME}" - RET=$? -fi - -rm -f "${PID_FILE}" +check_for_error_messages "${OUTPUT}" -# shellcheck disable=SC2086 -exit ${RET} +exit "${RET}" diff --git a/src/ASI_functions.cpp b/src/ASI_functions.cpp index c735ee3fd..d3a321627 100644 --- a/src/ASI_functions.cpp +++ b/src/ASI_functions.cpp @@ -128,14 +128,19 @@ ASI_CAMERA_INFO ASICameraInfoArray[] = { "imx477", 0, "RPi HQ", 0, 3040, 4056, ASI_TRUE, // Need ASI_IMG_END so we know where the end of the list is. BAYER_RG, {1, 2, 0}, {ASI_IMG_RGB24, ASI_IMG_END}, 1.55, ASI_FALSE, - 12, ASI_FALSE, ASI_FALSE + 12, ASI_TRUE, ASI_FALSE }, // There are many versions of the imx708 (_wide, _noir, _wide_noir, etc.) // so just check for "imx708" (6 characters. { "imx708", 6, "RPi Module 3", 0, 2592, 4608, ASI_TRUE, BAYER_RG, {1, 2, 0}, {ASI_IMG_RGB24, ASI_IMG_END}, 1.4, ASI_FALSE, - 10, ASI_FALSE, ASI_TRUE + 10, ASI_TRUE, ASI_TRUE + }, + + { "ov5647", 0, "RPi Version 1", 0, 2592, 1944, ASI_TRUE, + BAYER_RG, {1, 2, 0}, {ASI_IMG_RGB24, ASI_IMG_END}, 1.4, ASI_FALSE, + 10, ASI_FALSE, ASI_FALSE }, { "ov5647", 0, "RPi Version 1", 0, 2592, 1944, ASI_TRUE, @@ -210,33 +215,34 @@ ASI_CONTROL_CAPS ControlCapsArray[][MAX_NUM_CONTROL_CAPS] = // Name, Description, MaxValue, MinValue, DefaultValue, CurrentValue, IsAutoSupported, IsWritable, ControlType { // imx477, libcamera THIS MUST BE THE FIRST CAMERA - { "Gain", "Gain", 16.0, 1.0, 1.0, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_GAIN }, - { "Exposure", "Exposure Time (us)", 230 * US_IN_SEC, 1, 10000, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_EXPOSURE }, - { "WB_R", "White balance: Red component", 10.0, 0.1, 2.5, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_WB_R }, - { "WB_B", "White balance: Blue component", 10.0, 0.1, 2.0, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_WB_B }, + { "Gain", "Gain", 16.0, 1.0, NO_DEFAULT, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_GAIN }, + { "Exposure", "Exposure Time (us)", 230 * US_IN_SEC, 1, NOT_SET, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_EXPOSURE }, + { "WB_R", "White balance: Red component", 10.0, 0.1, NO_DEFAULT, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_WB_R }, + { "WB_B", "White balance: Blue component", 10.0, 0.1, NO_DEFAULT, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_WB_B }, + { "Temperature", "Sensor Temperature", 80, -20, NOT_SET, NOT_SET, ASI_FALSE, ASI_FALSE, ASI_TEMPERATURE }, { "Flip", "Flip: 0->None, 1->Horiz, 2->Vert, 3->Both", 3, 0, 0, NOT_SET, ASI_FALSE, ASI_TRUE, ASI_FLIP }, { "AutoExpMaxGain", "Auto exposure maximum gain value", 16.0, 1.0, 16.0, NOT_SET, ASI_FALSE, ASI_TRUE, ASI_AUTO_MAX_GAIN }, { "AutoExpMaxExpMS", "Auto exposure maximum exposure value (ms)", 230 * MS_IN_SEC, 1, 60 * MS_IN_SEC, NOT_SET, ASI_FALSE, ASI_TRUE, ASI_AUTO_MAX_EXP }, - { "ExposureCompensation", "Exposure Compensation", 10.0, -10.0, 0, NOT_SET, ASI_FALSE, ASI_TRUE, EV }, + { "ExposureCompensation", "Exposure Compensation", 10.0, -10.0, 0.0, NOT_SET, ASI_FALSE, ASI_TRUE, EV }, // These are the same for all libcamera cameras. - { "Brightness", "Brightness", 1.0, -1.0, 0, NOT_SET, ASI_FALSE, ASI_TRUE, ASI_AUTO_TARGET_BRIGHTNESS }, - { "Saturation", "Saturation", 16.0, 0.0, 1.0, NOT_SET, ASI_FALSE, ASI_TRUE, SATURATION }, - { "Contrast", "Contrast", 16.0, 0.0, 1.0, NOT_SET, ASI_FALSE, ASI_TRUE, CONTRAST }, - { "Sharpness", "Sharpness", 16.0, 0.0, 1.0, NOT_SET, ASI_FALSE, ASI_TRUE, SHARPNESS }, + { "Brightness", "Brightness", 1.0, -1.0, 0.0, NOT_SET, ASI_FALSE, ASI_TRUE, ASI_AUTO_TARGET_BRIGHTNESS }, + { "Saturation", "Saturation", 15.99, 0.0, 1.0, NOT_SET, ASI_FALSE, ASI_TRUE, SATURATION }, + { "Contrast", "Contrast", 15.99, 0.0, 1.0, NOT_SET, ASI_FALSE, ASI_TRUE, CONTRAST }, + { "Sharpness", "Sharpness", 15.99, 0.0, 1.0, NOT_SET, ASI_FALSE, ASI_TRUE, SHARPNESS }, { "End", "End", 0.0, 0.0, 0.0, 0.0, ASI_FALSE, ASI_FALSE, CONTROL_TYPE_END }, // Signals end of list }, { // imx477, raspistill. Minimum width and height are 64. - { "Gain", "Gain", 16.0, 1.0, 1.0, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_GAIN }, - { "Exposure", "Exposure Time (us)", 230 * US_IN_SEC, 1, 10000, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_EXPOSURE }, - { "WB_R", "White balance: Red component", 10.0, 0.1, 2.5, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_WB_R }, - { "WB_B", "White balance: Blue component", 10.0, 0.1, 2.0, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_WB_B }, + { "Gain", "Gain", 16.0, 1.0, NO_DEFAULT, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_GAIN }, + { "Exposure", "Exposure Time (us)", 230 * US_IN_SEC, 1, NOT_SET, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_EXPOSURE }, + { "WB_R", "White balance: Red component", 10.0, 0.1, NO_DEFAULT, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_WB_R }, + { "WB_B", "White balance: Blue component", 10.0, 0.1, NO_DEFAULT, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_WB_B }, { "Temperature", "Temperature, not supported", NOT_SET, NOT_SET, NOT_SET, NOT_SET, ASI_FALSE, ASI_FALSE, ASI_TEMPERATURE }, { "Flip", "Flip: 0->None, 1->Horiz, 2->Vert, 3->Both", 3, 0, 0, NOT_SET, ASI_FALSE, ASI_TRUE, ASI_FLIP }, { "AutoExpMaxGain", "Auto exposure maximum gain value", 16.0, 1.0, 16.0, NOT_SET, ASI_FALSE, ASI_TRUE, ASI_AUTO_MAX_GAIN }, { "AutoExpMaxExpMS", "Auto exposure maximum exposure value (ms)", 230 * MS_IN_SEC, 1, 60 * MS_IN_SEC, NOT_SET, ASI_FALSE, ASI_TRUE, ASI_AUTO_MAX_EXP }, - { "ExposureCompensation", "Exposure Compensation", 10, -10, 0, NOT_SET, ASI_FALSE, ASI_TRUE, EV }, + { "ExposureCompensation", "Exposure Compensation", 10, -10, 0.0, NOT_SET, ASI_FALSE, ASI_TRUE, EV }, { "Brightness", "Brightness", 100, 0, 50, NOT_SET, ASI_FALSE, ASI_TRUE, ASI_AUTO_TARGET_BRIGHTNESS }, { "Saturation", "Saturation", 100, -100, 0, NOT_SET, ASI_FALSE, ASI_TRUE, SATURATION }, { "Contrast", "Contrast", 100, -100, 0, NOT_SET, ASI_FALSE, ASI_TRUE, CONTRAST }, @@ -246,19 +252,19 @@ ASI_CONTROL_CAPS ControlCapsArray[][MAX_NUM_CONTROL_CAPS] = }, { // imx708*, libcamera - { "Gain", "Gain", 16.0, 1.122807, 1.122807, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_GAIN }, - { "Exposure", "Exposure Time (us)", 112015553, 26, 10000, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_EXPOSURE }, - { "WB_R", "White balance: Red component", 32.0, 0.0, 2.5, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_WB_R }, - { "WB_B", "White balance: Blue component", 32.0, 0.0, 2.0, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_WB_B }, + { "Gain", "Gain", 16.0, 1.122807, NO_DEFAULT, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_GAIN }, + { "Exposure", "Exposure Time (us)", 112015553, 26, NOT_SET, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_EXPOSURE }, + { "WB_R", "White balance: Red component", 32.0, 0.0, NO_DEFAULT, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_WB_R }, + { "WB_B", "White balance: Blue component", 32.0, 0.0, NO_DEFAULT, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_WB_B }, { "Temperature", "Sensor Temperature", 80, -20, NOT_SET, NOT_SET, ASI_FALSE, ASI_FALSE, ASI_TEMPERATURE }, { "Flip", "Flip: 0->None, 1->Horiz, 2->Vert, 3->Both", 3, 0, 0, NOT_SET, ASI_FALSE, ASI_TRUE, ASI_FLIP }, { "AutoExpMaxGain", "Auto exposure maximum gain value", 16.0, 1.122807, 16.0, NOT_SET, ASI_FALSE, ASI_TRUE, ASI_AUTO_MAX_GAIN }, { "AutoExpMaxExpMS", "Auto exposure maximum exposure value (ms)", 112015553 / US_IN_MS, 26.0, 60 * MS_IN_SEC, NOT_SET, ASI_FALSE, ASI_TRUE, ASI_AUTO_MAX_EXP }, - { "ExposureCompensation", "Exposure Compensation", 8.0, -8.0, 0, NOT_SET, ASI_FALSE, ASI_TRUE, EV }, - { "Brightness", "Brightness", 1.0, -1.0, 0.0, NOT_SET, ASI_FALSE, ASI_TRUE, ASI_AUTO_TARGET_BRIGHTNESS }, + { "ExposureCompensation", "Exposure Compensation", 8.0, -8.0, 0.0, NOT_SET, ASI_FALSE, ASI_TRUE, EV }, + { "Brightness", "Brightness", 1.0, -1.0, 0, NOT_SET, ASI_FALSE, ASI_TRUE, ASI_AUTO_TARGET_BRIGHTNESS }, { "Saturation", "Saturation", 32.0, 0.0, 1.0, NOT_SET, ASI_FALSE, ASI_TRUE, SATURATION }, { "Contrast", "Contrast", 32.0, 0.0, 1.0, NOT_SET, ASI_FALSE, ASI_TRUE, CONTRAST }, - { "Sharpness", "Sharpness", 16.0, 0.0, 1.0, NOT_SET, ASI_FALSE, ASI_TRUE, SHARPNESS }, + { "Sharpness", "Sharpness", 32.0, 0.0, 1.0, NOT_SET, ASI_FALSE, ASI_TRUE, SHARPNESS }, { "End", "End", 0.0, 0.0, 0.0, 0.0, ASI_FALSE, ASI_FALSE, CONTROL_TYPE_END }, }, @@ -267,16 +273,16 @@ ASI_CONTROL_CAPS ControlCapsArray[][MAX_NUM_CONTROL_CAPS] = }, { // ov5647, libcamera - { "Gain", "Gain", 63.9375, 1.0, 1.0, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_GAIN }, - { "Exposure", "Exposure Time (us)", 969249, 130, 9000, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_EXPOSURE }, - { "WB_R", "White balance: Red component", 32.0, 0, 0, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_WB_R }, - { "WB_B", "White balance: Blue component", 32.0, 0, 0, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_WB_B }, + { "Gain", "Gain", 63.9375, 1.0, NO_DEFAULT, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_GAIN }, + { "Exposure", "Exposure Time (us)", 969249, 130, NOT_SET, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_EXPOSURE }, + { "WB_R", "White balance: Red component", 32.0, 0.0, NO_DEFAULT, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_WB_R }, + { "WB_B", "White balance: Blue component", 32.0, 0.0, NO_DEFAULT, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_WB_B }, { "Temperature", "Temperature, not supported", NOT_SET, NOT_SET, NOT_SET, NOT_SET, ASI_FALSE, ASI_FALSE, ASI_TEMPERATURE }, { "Flip", "Flip: 0->None, 1->Horiz, 2->Vert, 3->Both", 3, 0, 0, NOT_SET, ASI_FALSE, ASI_TRUE, ASI_FLIP }, { "AutoExpMaxGain", "Auto exposure maximum gain value", 63.9375, 1.0, 63.9375, NOT_SET, ASI_FALSE, ASI_TRUE, ASI_AUTO_MAX_GAIN }, { "AutoExpMaxExpMS", "Auto exposure maximum exposure value (ms)", 969249 / US_IN_MS, 1.0, 9 * MS_IN_SEC, NOT_SET, ASI_FALSE, ASI_TRUE, ASI_AUTO_MAX_EXP }, - { "ExposureCompensation", "Exposure Compensation", 8.0, -8.0, 0, NOT_SET, ASI_FALSE, ASI_TRUE, EV }, - { "Brightness", "Brightness", 1.0, -1.0, 0, NOT_SET, ASI_FALSE, ASI_TRUE, ASI_AUTO_TARGET_BRIGHTNESS }, + { "ExposureCompensation", "Exposure Compensation", 8.0, -8.0, 0.0, NOT_SET, ASI_FALSE, ASI_TRUE, EV }, + { "Brightness", "Brightness", 1.0, -1.0, 0.0, NOT_SET, ASI_FALSE, ASI_TRUE, ASI_AUTO_TARGET_BRIGHTNESS }, { "Saturation", "Saturation", 32, 0.0, 1.0, NOT_SET, ASI_FALSE, ASI_TRUE, SATURATION }, { "Contrast", "Contrast", 32.0, 0.0, 1.0, NOT_SET, ASI_FALSE, ASI_TRUE, CONTRAST }, { "Sharpness", "Sharpness", 16.0, 0.0, 1.0, NOT_SET, ASI_FALSE, ASI_TRUE, SHARPNESS }, @@ -288,19 +294,19 @@ ASI_CONTROL_CAPS ControlCapsArray[][MAX_NUM_CONTROL_CAPS] = }, { // imx290, libcamera - { "Gain", "Gain", 16.0, 1.0, 1.0, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_GAIN }, + { "Gain", "Gain", 16.0, 1.0, NO_DEFAULT, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_GAIN }, { "Exposure", "Exposure Time (us)", 200 * US_IN_SEC, 1, 10000, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_EXPOSURE }, - { "WB_R", "White balance: Red component", 10.0, 0.1, 2.5, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_WB_R }, - { "WB_B", "White balance: Blue component", 10.0, 0.1, 2.0, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_WB_B }, + { "WB_R", "White balance: Red component", 10.0, 0.1, NO_DEFAULT, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_WB_R }, + { "WB_B", "White balance: Blue component", 10.0, 0.1, NO_DEFAULT, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_WB_B }, { "Temperature", "Sensor Temperature", 80, -20, NOT_SET, NOT_SET, ASI_FALSE, ASI_FALSE, ASI_TEMPERATURE }, { "Flip", "Flip: 0->None, 1->Horiz, 2->Vert, 3->Both", 3, 0, 0, NOT_SET, ASI_FALSE, ASI_TRUE, ASI_FLIP }, - { "AutoExpMaxGain", "Auto exposure maximum gain value", 16.0, 1, 16.0, NOT_SET, ASI_FALSE, ASI_TRUE, ASI_AUTO_MAX_GAIN }, + { "AutoExpMaxGain", "Auto exposure maximum gain value", 16.0, 1.0, 16.0, NOT_SET, ASI_FALSE, ASI_TRUE, ASI_AUTO_MAX_GAIN }, { "AutoExpMaxExpMS", "Auto exposure maximum exposure value (ms)", 200 * MS_IN_SEC, 1, 60 * MS_IN_SEC, NOT_SET, ASI_FALSE, ASI_TRUE, ASI_AUTO_MAX_EXP }, - { "ExposureCompensation", "Exposure Compensation", 10.0, -10.0, 0, NOT_SET, ASI_FALSE, ASI_TRUE, EV }, - { "Brightness", "Brightness", 1.0, -1.0, 0, NOT_SET, ASI_FALSE, ASI_TRUE, ASI_AUTO_TARGET_BRIGHTNESS }, - { "Saturation", "Saturation", 16.0, 0.0, 1.0, NOT_SET, ASI_FALSE, ASI_TRUE, SATURATION }, - { "Contrast", "Contrast", 16.0, 0.0, 1.0, NOT_SET, ASI_FALSE, ASI_TRUE, CONTRAST }, - { "Sharpness", "Sharpness", 16.0, 0.0, 1.0, NOT_SET, ASI_FALSE, ASI_TRUE, SHARPNESS }, + { "ExposureCompensation", "Exposure Compensation", 10.0, -10.0, 0.0, NOT_SET, ASI_FALSE, ASI_TRUE, EV }, + { "Brightness", "Brightness", 1.0, -1.0, 0.0, NOT_SET, ASI_FALSE, ASI_TRUE, ASI_AUTO_TARGET_BRIGHTNESS }, + { "Saturation", "Saturation", 15.99, 0.0, 1.0, NOT_SET, ASI_FALSE, ASI_TRUE, SATURATION }, + { "Contrast", "Contrast", 15.99, 0.0, 1.0, NOT_SET, ASI_FALSE, ASI_TRUE, CONTRAST }, + { "Sharpness", "Sharpness", 15.99, 0.0, 1.0, NOT_SET, ASI_FALSE, ASI_TRUE, SHARPNESS }, { "End", "End", 0.0, 0.0, 0.0, 0.0, ASI_FALSE, ASI_FALSE, CONTROL_TYPE_END }, }, @@ -309,19 +315,19 @@ ASI_CONTROL_CAPS ControlCapsArray[][MAX_NUM_CONTROL_CAPS] = }, { // imx519, libcamera - { "Gain", "Gain", 16.0, 1.0, 1.0, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_GAIN }, + { "Gain", "Gain", 16.0, 1.0, NO_DEFAULT, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_GAIN }, { "Exposure", "Exposure Time (us)", 200 * US_IN_SEC, 1, 10000, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_EXPOSURE }, - { "WB_R", "White balance: Red component", 10.0, 0.1, 2.5, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_WB_R }, - { "WB_B", "White balance: Blue component", 10.0, 0.1, 2.0, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_WB_B }, + { "WB_R", "White balance: Red component", 10.0, 0.1, NO_DEFAULT, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_WB_R }, + { "WB_B", "White balance: Blue component", 10.0, 0.1, NO_DEFAULT, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_WB_B }, { "Temperature", "Sensor Temperature", 80, -20, NOT_SET, NOT_SET, ASI_FALSE, ASI_FALSE, ASI_TEMPERATURE }, { "Flip", "Flip: 0->None, 1->Horiz, 2->Vert, 3->Both", 3, 0, 0, NOT_SET, ASI_FALSE, ASI_TRUE, ASI_FLIP }, - { "AutoExpMaxGain", "Auto exposure maximum gain value", 16.0, 1, 16.0, NOT_SET, ASI_FALSE, ASI_TRUE, ASI_AUTO_MAX_GAIN }, + { "AutoExpMaxGain", "Auto exposure maximum gain value", 16.0, 1.0, 16.0, NOT_SET, ASI_FALSE, ASI_TRUE, ASI_AUTO_MAX_GAIN }, { "AutoExpMaxExpMS", "Auto exposure maximum exposure value (ms)", 200 * MS_IN_SEC, 1, 60 * MS_IN_SEC, NOT_SET, ASI_FALSE, ASI_TRUE, ASI_AUTO_MAX_EXP }, - { "ExposureCompensation", "Exposure Compensation", 10.0, -10.0, 0, NOT_SET, ASI_FALSE, ASI_TRUE, EV }, - { "Brightness", "Brightness", 1.0, -1.0, 0, NOT_SET, ASI_FALSE, ASI_TRUE, ASI_AUTO_TARGET_BRIGHTNESS }, - { "Saturation", "Saturation", 16.0, 0.0, 1.0, NOT_SET, ASI_FALSE, ASI_TRUE, SATURATION }, - { "Contrast", "Contrast", 16.0, 0.0, 1.0, NOT_SET, ASI_FALSE, ASI_TRUE, CONTRAST }, - { "Sharpness", "Sharpness", 16.0, 0.0, 1.0, NOT_SET, ASI_FALSE, ASI_TRUE, SHARPNESS }, + { "ExposureCompensation", "Exposure Compensation", 10.0, -10.0, 0.0, NOT_SET, ASI_FALSE, ASI_TRUE, EV }, + { "Brightness", "Brightness", 1.0, -1.0, 0.0, NOT_SET, ASI_FALSE, ASI_TRUE, ASI_AUTO_TARGET_BRIGHTNESS }, + { "Saturation", "Saturation", 15.99, 0.0, 1.0, NOT_SET, ASI_FALSE, ASI_TRUE, SATURATION }, + { "Contrast", "Contrast", 15.99, 0.0, 1.0, NOT_SET, ASI_FALSE, ASI_TRUE, CONTRAST }, + { "Sharpness", "Sharpness", 15.99, 0.0, 1.0, NOT_SET, ASI_FALSE, ASI_TRUE, SHARPNESS }, { "End", "End", 0.0, 0.0, 0.0, 0.0, ASI_FALSE, ASI_FALSE, CONTROL_TYPE_END }, }, @@ -331,19 +337,19 @@ ASI_CONTROL_CAPS ControlCapsArray[][MAX_NUM_CONTROL_CAPS] = { // arducam_64mp, libcamera - { "Gain", "Gain", 16.0, 1.0, 1.0, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_GAIN }, + { "Gain", "Gain", 16.0, 1.0, NO_DEFAULT, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_GAIN }, { "Exposure", "Exposure Time (us)", 200 * US_IN_SEC, 1, 10000, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_EXPOSURE }, - { "WB_R", "White balance: Red component", 10.0, 0.1, 2.5, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_WB_R }, - { "WB_B", "White balance: Blue component", 10.0, 0.1, 2.0, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_WB_B }, + { "WB_R", "White balance: Red component", 10.0, 0.1, NO_DEFAULT, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_WB_R }, + { "WB_B", "White balance: Blue component", 10.0, 0.1, NO_DEFAULT, NOT_SET, ASI_TRUE, ASI_TRUE, ASI_WB_B }, { "Temperature", "Sensor Temperature", 80, -20, NOT_SET, NOT_SET, ASI_FALSE, ASI_FALSE, ASI_TEMPERATURE }, { "Flip", "Flip: 0->None, 1->Horiz, 2->Vert, 3->Both", 3, 0, 0, NOT_SET, ASI_FALSE, ASI_TRUE, ASI_FLIP }, - { "AutoExpMaxGain", "Auto exposure maximum gain value", 16.0, 1, 16.0, NOT_SET, ASI_FALSE, ASI_TRUE, ASI_AUTO_MAX_GAIN }, + { "AutoExpMaxGain", "Auto exposure maximum gain value", 16.0, 1.0, 16.0, NOT_SET, ASI_FALSE, ASI_TRUE, ASI_AUTO_MAX_GAIN }, { "AutoExpMaxExpMS", "Auto exposure maximum exposure value (ms)", 200 * MS_IN_SEC, 1, 60 * MS_IN_SEC, NOT_SET, ASI_FALSE, ASI_TRUE, ASI_AUTO_MAX_EXP }, - { "ExposureCompensation", "Exposure Compensation", 10.0, -10.0, 0, NOT_SET, ASI_FALSE, ASI_TRUE, EV }, - { "Brightness", "Brightness", 1.0, -1.0, 0, NOT_SET, ASI_FALSE, ASI_TRUE, ASI_AUTO_TARGET_BRIGHTNESS }, - { "Saturation", "Saturation", 16.0, 0.0, 1.0, NOT_SET, ASI_FALSE, ASI_TRUE, SATURATION }, - { "Contrast", "Contrast", 16.0, 0.0, 1.0, NOT_SET, ASI_FALSE, ASI_TRUE, CONTRAST }, - { "Sharpness", "Sharpness", 16.0, 0.0, 1.0, NOT_SET, ASI_FALSE, ASI_TRUE, SHARPNESS }, + { "ExposureCompensation", "Exposure Compensation", 10.0, -10.0, 0.0, NOT_SET, ASI_FALSE, ASI_TRUE, EV }, + { "Brightness", "Brightness", 1.0, -1.0, 0.0, NOT_SET, ASI_FALSE, ASI_TRUE, ASI_AUTO_TARGET_BRIGHTNESS }, + { "Saturation", "Saturation", 15.99, 0.0, 1.0, NOT_SET, ASI_FALSE, ASI_TRUE, SATURATION }, + { "Contrast", "Contrast", 15.99, 0.0, 1.0, NOT_SET, ASI_FALSE, ASI_TRUE, CONTRAST }, + { "Sharpness", "Sharpness", 15.99, 0.0, 1.0, NOT_SET, ASI_FALSE, ASI_TRUE, SHARPNESS }, { "End", "End", 0.0, 0.0, 0.0, 0.0, ASI_FALSE, ASI_FALSE, CONTROL_TYPE_END }, }, @@ -576,6 +582,7 @@ ASI_ERROR_CODE ASIGetSerialNumber(int iCameraIndex, ASI_SN *pSN) } + #else ////////////////////// ZWO // To keep the compiler quiet, we need to define these - they are RPi only. @@ -844,21 +851,21 @@ void saveCameraInfo(ASI_CAMERA_INFO cameraInfo, char const *file, int width, int fprintf(f, "\t\"sensorHeight\" : %d,\n", height); fprintf(f, "\t\"pixelSize\" : %1.2f,\n", pixelSize); fprintf(f, "\t\"supportedBins\" : [\n"); - for (unsigned int i = 0; i < sizeof(cameraInfo.SupportedBins); ++i) - { - int b = cameraInfo.SupportedBins[i]; - if (b == 0) - { - fprintf(f, "\n"); - break; - } - if (i > 0) + for (unsigned int i = 0; i < sizeof(cameraInfo.SupportedBins); ++i) { - fprintf(f, ","); // comma on all but last one - fprintf(f, "\n"); + int b = cameraInfo.SupportedBins[i]; + if (b == 0) + { + fprintf(f, "\n"); + break; + } + if (i > 0) + { + fprintf(f, ","); // comma on all but last one + fprintf(f, "\n"); + } + fprintf(f, "\t\t{ \"value\" : %d, \"label\" : \"%dx%d\" }", b, b, b); } - fprintf(f, "\t\t{ \"value\" : %d, \"label\" : \"%dx%d\" }", b, b, b); - } fprintf(f, "\t],\n"); // RPi only supports sensor temp with libcamera. @@ -874,66 +881,65 @@ void saveCameraInfo(ASI_CAMERA_INFO cameraInfo, char const *file, int width, int #ifdef IS_RPi fprintf(f, "\t\"autoFocus\" : %s,\n", cameraInfo.SupportsAutoFocus ? "true" : "false"); fprintf(f, "\t\"supportedRotations\": [\n"); - fprintf(f, "\t\t{ \"value\" : 0, \"label\" : \"None\" },\n"); - if (CG.ct == ctRPi && CG.isLibcamera) - { - // libcamera only supports 0 and 180 degree rotation - fprintf(f, "\t\t{ \"value\" : 180, \"label\" : \"180 degrees\" }\n"); - } - else - { - fprintf(f, "\t\t{ \"value\" : 90, \"label\" : \"90 degrees\" },\n"); - fprintf(f, "\t\t{ \"value\" : 180, \"label\" : \"180 degrees\" },\n"); - fprintf(f, "\t\t{ \"value\" : 270, \"label\" : \"270 degrees\" }\n"); - } + fprintf(f, "\t\t{ \"value\" : 0, \"label\" : \"None\" },\n"); + if (CG.ct == ctRPi && CG.isLibcamera) + { + // libcamera only supports 0 and 180 degree rotation + fprintf(f, "\t\t{ \"value\" : 180, \"label\" : \"180 degrees\" }\n"); + } + else + { + fprintf(f, "\t\t{ \"value\" : 90, \"label\" : \"90 degrees\" },\n"); + fprintf(f, "\t\t{ \"value\" : 180, \"label\" : \"180 degrees\" },\n"); + fprintf(f, "\t\t{ \"value\" : 270, \"label\" : \"270 degrees\" }\n"); + } fprintf(f, "\t],\n"); #endif fprintf(f, "\t\"supportedImageFormats\": [\n"); - fprintf(f, "\t\t{ "); - fprintf(f, "\"value\" : %d, ", AUTO_IMAGE_TYPE); - fprintf(f, "\"label\" : \"%s\"", "auto"); - fprintf(f, " },\n"); - for (unsigned int i = 0; i < sizeof(cameraInfo.SupportedVideoFormat); i++) - { - ASI_IMG_TYPE it = cameraInfo.SupportedVideoFormat[i]; - if (it == ASI_IMG_END) - { - fprintf(f, "\n"); - break; - } - if (i > 0) + fprintf(f, "\t\t{ "); + fprintf(f, "\"value\" : %d, ", AUTO_IMAGE_TYPE); + fprintf(f, "\"label\" : \"%s\"", "auto"); + fprintf(f, " },\n"); + for (unsigned int i = 0; i < sizeof(cameraInfo.SupportedVideoFormat); i++) { - fprintf(f, ","); // comma on all but last one - fprintf(f, "\n"); + ASI_IMG_TYPE it = cameraInfo.SupportedVideoFormat[i]; + if (it == ASI_IMG_END) + { + fprintf(f, "\n"); + break; + } + if (i > 0) + { + fprintf(f, ","); // comma on all but last one + fprintf(f, "\n"); + } + fprintf(f, "\t\t{ "); + fprintf(f, "\"value\" : %d, ", (int) it); + fprintf(f, "\"label\" : \"%s\"", + it == ASI_IMG_RAW8 ? "RAW8" : + it == ASI_IMG_RGB24 ? "RGB24" : + it == ASI_IMG_RAW16 ? "RAW16" : + it == ASI_IMG_Y8 ? "Y8" : + "unknown format"); + fprintf(f, " }"); } - fprintf(f, "\t\t{ "); - fprintf(f, "\"value\" : %d, ", (int) it); - fprintf(f, "\"label\" : \"%s\"", - it == ASI_IMG_RAW8 ? "RAW8" : - it == ASI_IMG_RGB24 ? "RGB24" : - it == ASI_IMG_RAW16 ? "RAW16" : - it == ASI_IMG_Y8 ? "Y8" : - "unknown format"); - fprintf(f, " }"); - } - fprintf(f, "\t],\n");; + fprintf(f, "\t],\n"); // Add some other things the camera supports, or the software supports for this camera. // Adding it to the "controls" array makes the code that checks what's available easier. fprintf(f, "\t\"controls\": [\n"); - // sensor size was also saved above, but save here with min/max/default + // sensor size was also saved above, but this is the size the user can change. fprintf(f, "\t\t{\n"); - fprintf(f, "\t\t\t\"Name\" : \"sensorWidth\",\n"); + fprintf(f, "\t\t\t\"Name\" : \"Width\",\n"); fprintf(f, "\t\t\t\"argumentName\" : \"width\",\n"); fprintf(f, "\t\t\t\"MinValue\" : 0,\n"); // TODO: I some ZWO cameras have a min fprintf(f, "\t\t\t\"MaxValue\" : %d,\n", width); fprintf(f, "\t\t\t\"DefaultValue\" : 0\n"); fprintf(f, "\t\t},\n"); - fprintf(f, "\t\t{\n"); - fprintf(f, "\t\t\t\"Name\" : \"sensorHeight\",\n"); + fprintf(f, "\t\t\t\"Name\" : \"Height\",\n"); fprintf(f, "\t\t\t\"argumentName\" : \"height\",\n"); fprintf(f, "\t\t\t\"MinValue\" : 0,\n"); // TODO: I some ZWO cameras have a min fprintf(f, "\t\t\t\"MaxValue\" : %d,\n", height); @@ -952,7 +958,7 @@ void saveCameraInfo(ASI_CAMERA_INFO cameraInfo, char const *file, int width, int fprintf(f, "\t\t\t\"argumentName\" : \"%s\",\n", "daymean"); fprintf(f, "\t\t\t\"MinValue\" : 0.0,\n"); fprintf(f, "\t\t\t\"MaxValue\" : 1.0,\n"); - fprintf(f, "\t\t\t\"DefaultValue\" : %.2f\n", CG.myModeMeanSetting.dayMean); + fprintf(f, "\t\t\t\"DefaultValue\" : %.3f\n", CG.myModeMeanSetting.dayMean); fprintf(f, "\t\t},\n"); fprintf(f, "\t\t{\n"); @@ -968,7 +974,7 @@ void saveCameraInfo(ASI_CAMERA_INFO cameraInfo, char const *file, int width, int fprintf(f, "\t\t\t\"argumentName\" : \"%s\",\n", "nightmean"); fprintf(f, "\t\t\t\"MinValue\" : 0.0,\n"); fprintf(f, "\t\t\t\"MaxValue\" : 1.0,\n"); - fprintf(f, "\t\t\t\"DefaultValue\" : %.2f\n", CG.myModeMeanSetting.nightMean); + fprintf(f, "\t\t\t\"DefaultValue\" : %.3f\n", CG.myModeMeanSetting.nightMean); fprintf(f, "\t\t},\n"); fprintf(f, "\t\t{\n"); @@ -983,21 +989,21 @@ void saveCameraInfo(ASI_CAMERA_INFO cameraInfo, char const *file, int width, int fprintf(f, "\t\t{\n"); fprintf(f, "\t\t\t\"Name\" : \"%s\",\n", "AutoWhiteBalance"); fprintf(f, "\t\t\t\"argumentName\" : \"%s\",\n", "awb"); - fprintf(f, "\t\t\t\"DefaultValue\" : 0\n"); + fprintf(f, "\t\t\t\"DefaultValue\" : false\n"); fprintf(f, "\t\t},\n"); } if (CG.isCooledCamera) { fprintf(f, "\t\t{\n"); fprintf(f, "\t\t\t\"Name\" : \"%s\",\n", "EnableCooler"); - fprintf(f, "\t\t\t\"argumentName\" : \"%s\",\n", "EnableCooler"); - fprintf(f, "\t\t\t\"DefaultValue\" : 0\n"); + fprintf(f, "\t\t\t\"argumentName\" : \"%s\",\n", "enablecooler"); + fprintf(f, "\t\t\t\"DefaultValue\" : false\n"); fprintf(f, "\t\t},\n"); } if (CG.supportsTemperature) { - fprintf(f, "\t\t{\n"); + fprintf(f, "\t\t{\n"); // TODO This will go away when the legacy overlay is removed fprintf(f, "\t\t\t\"Name\" : \"%s\",\n", "showTemp"); - fprintf(f, "\t\t\t\"argumentName\" : \"%s\",\n", "showTemp"); - fprintf(f, "\t\t\t\"DefaultValue\" : %d\n", CG.overlay.showTemp ? 1 : 0); + fprintf(f, "\t\t\t\"argumentName\" : \"%s\",\n", "showtemp"); + fprintf(f, "\t\t\t\"DefaultValue\" : %s\n", CG.overlay.showTemp ? "true" : "false"); fprintf(f, "\t\t},\n"); } if (CG.supportsAggression) { @@ -1022,19 +1028,19 @@ void saveCameraInfo(ASI_CAMERA_INFO cameraInfo, char const *file, int width, int fprintf(f, "\t\t{\n"); fprintf(f, "\t\t\t\"Name\" : \"%s\",\n", "autousb"); fprintf(f, "\t\t\t\"argumentName\" : \"%s\",\n", "autousb"); - fprintf(f, "\t\t\t\"DefaultValue\" : 1\n"); + fprintf(f, "\t\t\t\"DefaultValue\" : true\n"); fprintf(f, "\t\t},\n"); fprintf(f, "\t\t{\n"); fprintf(f, "\t\t\t\"Name\" : \"%s\",\n", "showUSB"); - fprintf(f, "\t\t\t\"argumentName\" : \"%s\",\n", "showUSB"); - fprintf(f, "\t\t\t\"DefaultValue\" : %d\n", CG.overlay.showUSB ? 1 : 0); + fprintf(f, "\t\t\t\"argumentName\" : \"%s\",\n", "showusb"); + fprintf(f, "\t\t\t\"DefaultValue\" : %s\n", CG.overlay.showUSB ? "true" : "false"); fprintf(f, "\t\t},\n"); fprintf(f, "\t\t{\n"); fprintf(f, "\t\t\t\"Name\" : \"%s\",\n", "experimentalExposure"); - fprintf(f, "\t\t\t\"argumentName\" : \"%s\",\n", "experimentalExposure"); - fprintf(f, "\t\t\t\"DefaultValue\" : \"%d\"\n", CG.HB.useExperimentalExposure ? 1 : 0); + fprintf(f, "\t\t\t\"argumentName\" : \"%s\",\n", "experimentalexposure"); + fprintf(f, "\t\t\t\"DefaultValue\" : %s\n", CG.HB.useExperimentalExposure ? "true" : "false"); fprintf(f, "\t\t},\n"); fprintf(f, "\t\t{\n"); @@ -1046,18 +1052,18 @@ void saveCameraInfo(ASI_CAMERA_INFO cameraInfo, char const *file, int width, int fprintf(f, "\t\t{\n"); fprintf(f, "\t\t\t\"Name\" : \"%s\",\n", "showhistogrambox"); fprintf(f, "\t\t\t\"argumentName\" : \"%s\",\n", "showhistogrambox"); - fprintf(f, "\t\t\t\"DefaultValue\" : %d\n", CG.overlay.showHistogramBox ? 1 : 0); + fprintf(f, "\t\t\t\"DefaultValue\" : %s\n", CG.overlay.showHistogramBox ? "true" : "false"); fprintf(f, "\t\t},\n"); fprintf(f, "\t\t{\n"); fprintf(f, "\t\t\t\"Name\" : \"%s\",\n", "newexposure"); fprintf(f, "\t\t\t\"argumentName\" : \"%s\",\n", "newexposure"); - fprintf(f, "\t\t\t\"DefaultValue\" : %d\n", CG.videoOffBetweenImages ? 1 : 0); + fprintf(f, "\t\t\t\"DefaultValue\" : %s\n", CG.videoOffBetweenImages ? "true" : "false"); fprintf(f, "\t\t},\n"); fprintf(f, "\t\t{\n"); fprintf(f, "\t\t\t\"Name\" : \"%s\",\n", "CameraNumber"); - fprintf(f, "\t\t\t\"argumentName\" : \"%s\",\n", "cameraNumber"); + fprintf(f, "\t\t\t\"argumentName\" : \"%s\",\n", "cameranumber"); fprintf(f, "\t\t\t\"DefaultValue\" : %d\n", CG.cameraNumber); fprintf(f, "\t\t},\n"); #endif @@ -1065,15 +1071,20 @@ void saveCameraInfo(ASI_CAMERA_INFO cameraInfo, char const *file, int width, int #ifdef IS_RPi fprintf(f, "\t\t{\n"); fprintf(f, "\t\t\t\"Name\" : \"%s\",\n", "ExtraArguments"); - fprintf(f, "\t\t\t\"argumentName\" : \"%s\"\n", "extraArgs"); + fprintf(f, "\t\t\t\"argumentName\" : \"%s\"\n", "extraargs"); fprintf(f, "\t\t},\n"); - if (CG.ct == ctRPi && CG.isLibcamera) { fprintf(f, "\t\t{\n"); fprintf(f, "\t\t\t\"Name\" : \"%s\",\n", "TuningFile"); - fprintf(f, "\t\t\t\"argumentName\" : \"%s\",\n", "TuningFile"); + fprintf(f, "\t\t\t\"argumentName\" : \"%s\",\n", "tuningfile"); fprintf(f, "\t\t\t\"DefaultValue\" : \"\"\n"); fprintf(f, "\t\t},\n"); + + if (CG.ct == ctRPi && CG.isLibcamera) { + fprintf(f, "\t\t{\n"); + fprintf(f, "\t\t\t\"Name\" : \"%s\",\n", "Rotation"); + fprintf(f, "\t\t\t\"argumentName\" : \"%s\"\n", "rotation"); + fprintf(f, "\t\t},\n"); } #endif @@ -1107,7 +1118,10 @@ void saveCameraInfo(ASI_CAMERA_INFO cameraInfo, char const *file, int width, int fprintf(f, "\t\t\t\"argumentName\" : \"%s\",\n", a); fprintf(f, "\t\t\t\"MinValue\" : %s,\n", LorF(min, "%ld", "%.3f")); fprintf(f, "\t\t\t\"MaxValue\" : %s,\n", LorF(max, "%ld", "%.3f")); - fprintf(f, "\t\t\t\"DefaultValue\" : %s,\n", LorF(def, "%ld", "%.3f")); + if (def == NO_DEFAULT) + fprintf(f, "\t\t\t\"DefaultValue\" : \"none\",\n"); + else + fprintf(f, "\t\t\t\"DefaultValue\" : %s,\n", LorF(def, "%ld", "%.3f")); fprintf(f, "\t\t\t\"IsAutoSupported\" : %s,\n", cc.IsAutoSupported == ASI_TRUE ? "true" : "false"); fprintf(f, "\t\t\t\"IsWritable\" : %s,\n", cc.IsWritable == ASI_TRUE ? "true" : "false"); fprintf(f, "\t\t\t\"ControlType\" : %d\n", cc.ControlType); @@ -1132,17 +1146,6 @@ void saveCameraInfo(ASI_CAMERA_INFO cameraInfo, char const *file, int width, int fprintf(f, "\t\t\t\"DefaultValue\" : %d\n", 10 * MS_IN_SEC); fprintf(f, "\t\t},\n"); - fprintf(f, "\t\t{\n"); - fprintf(f, "\t\t\t\"Name\" : \"%s\",\n", "daymean"); - fprintf(f, "\t\t\t\"argumentName\" : \"%s\",\n", "daymean"); - fprintf(f, "\t\t\t\"DefaultValue\" : %f\n", CG.myModeMeanSetting.dayMean); - fprintf(f, "\t\t},\n"); - fprintf(f, "\t\t{\n"); - fprintf(f, "\t\t\t\"Name\" : \"%s\",\n", "nightmean"); - fprintf(f, "\t\t\t\"argumentName\" : \"%s\",\n", "nightmean"); - fprintf(f, "\t\t\t\"DefaultValue\" : %f\n", CG.myModeMeanSetting.nightMean); - fprintf(f, "\t\t},\n"); - // Set the day gain to the minimum possible. fprintf(f, "\t\t{\n"); fprintf(f, "\t\t\t\"Name\" : \"%s\",\n", "daygain"); @@ -1772,4 +1775,4 @@ bool validateSettings(config *cg, ASI_CAMERA_INFO ci) } return(ok); -} +} \ No newline at end of file diff --git a/src/allsky_common.cpp b/src/allsky_common.cpp index 72ae73321..42b84e5b6 100644 --- a/src/allsky_common.cpp +++ b/src/allsky_common.cpp @@ -908,7 +908,7 @@ bool validateLong(long *num, long min, long max, char const *name, bool invalidI bool validateFloat(double *num, double min, double max, char const *name, bool invalidIsOK) { if (*num < min) { - fprintf(stderr, "*** %s: '%s' (%'.1f) is less than the minimum of %'.1f", + fprintf(stderr, "*** %s: '%s' (%'.3f) is less than the minimum of %'.3f", invalidIsOK ? "WARNING" : "ERROR", name, *num, min); if (invalidIsOK == true) { @@ -919,7 +919,7 @@ bool validateFloat(double *num, double min, double max, char const *name, bool i return invalidIsOK; } else if (*num > max) { - fprintf(stderr, "*** %s: '%s' (%'.1f) is greater than the maximum of %'.1f", + fprintf(stderr, "*** %s: '%s' (%'.3f) is greater than the maximum of %'.3f", invalidIsOK ? "WARNING" : "ERROR", name, *num, max); if (invalidIsOK == true) { @@ -952,7 +952,7 @@ void displayHeader(config cg) printf("Capture images of the sky with a Raspberry Pi and an RPi camera\n"); printf("%s\n", c(KNRM)); - if (! cg.help) printf("%sAdd -h or --help for available options%s\n\n", c(KYEL), c(KNRM)); + if (! cg.help) printf("%sAdd --help for available options%s\n\n", c(KYEL), c(KNRM)); printf("Author: Thomas Jacquin - \n\n"); printf("Contributors:\n"); printf(" -Knut Olav Klo\n"); @@ -1550,7 +1550,7 @@ bool getCommandLineArguments(config *cg, int argc, char *argv[]) return(false); } } - else if (strcmp(a, "h") == 0 || strcmp(a, "-help") == 0) + else if (strcmp(a, "-help") == 0) { cg->help = true; cg->quietExit = true; // we display the help message and quit @@ -1672,6 +1672,11 @@ bool getCommandLineArguments(config *cg, int argc, char *argv[]) } // nighttime settings + else if (strcmp(a, "takenighttimeimages") == 0) + { +++i; +//xxx cg->daytimeCapture = getBoolean(argv[++i]); + } else if (strcmp(a, "nightautoexposure") == 0) { cg->nightAutoExposure = getBoolean(argv[++i]); @@ -1969,43 +1974,6 @@ bool getCommandLineArguments(config *cg, int argc, char *argv[]) cg->overlay.outlinefont = getBoolean(argv[++i]); } - // Arguments that may be passed to us but we don't use. - else if ( - strcmp(a, "xx_end_xx") == 0 || - strcmp(a, "lastchanged") == 0 || - strcmp(a, "uselocalwebsite") == 0 || -#define temp1 "useremote" - strncmp(a, temp1, sizeof(temp1)-1) == 0 || -#define temp2 "protocol" - strncmp(a, temp2, sizeof(temp2)-1) == 0 || -#define temp3 "imagedir" - strncmp(a, temp3, sizeof(temp3)-1) == 0 || -#define temp4 "videodestinationname" - strncmp(a, temp4, sizeof(temp4)-1) == 0 || -#define temp5 "keogramdeodestinationname" - strncmp(a, temp5, sizeof(temp5)-1) == 0 || -#define temp6 "startrailsdeodestinationname" - strncmp(a, temp6, sizeof(temp6)-1) == 0 || - strcmp(a, "displaysettings") == 0 || - strcmp(a, "showonmap") == 0 || - strcmp(a, "websiteurl") == 0 || - strcmp(a, "imageurl") == 0 || - strcmp(a, "location") == 0 || - strcmp(a, "owner") == 0 || - strcmp(a, "camera") == 0 || - strcmp(a, "lens") == 0 || - strcmp(a, "computer") == 0 || - strcmp(a, "usedarkframes") == 0 || - strcmp(a, "uselogin") == 0 || - strcmp(a, "cameratype") == 0 || - strcmp(a, "cameramodel") == 0 || - strcmp(a, "showusb") == 0 || - strcmp(a, "alwaysshowadvanced") == 0 - ) - { - i++; - } - else Log(-1, "*** %s: WARNING: Unknown argument: [%s]. Ignored.\n", cg->ME, a); } diff --git a/src/capture_ZWO.cpp b/src/capture_ZWO.cpp index c46525110..68f95e835 100644 --- a/src/capture_ZWO.cpp +++ b/src/capture_ZWO.cpp @@ -108,7 +108,7 @@ ASI_ERROR_CODE setControl(int camNum, ASI_CONTROL_TYPE control, long value, ASI_ ret = ASISetControlValue(camNum, control, value, makeAuto); if (ret != ASI_SUCCESS) { - Log(-1, "*** %s: WARNING: ASISetControlCaps() for control %d, value=%ld failed: %s\n", + Log(-1, "*** %s: WARNING: ASISetControlValue() for control %d, value=%ld failed: %s\n", CG.ME, control, value, getRetCode(ret)); return(ret); } @@ -323,12 +323,17 @@ int computeHistogram(unsigned char *imageBuffer, config cg, bool useHistogramBox // Camera has internal frame buffers we need to clear. // The camera and/or driver will buffer frames and return the oldest one which // could be very old. Read out all the buffered frames so the frame we get is current. -void flushBufferedImages(config *cg, void *buf, size_t size) +ASI_ERROR_CODE flushBufferedImages(config *cg, void *buf, size_t size) { enum { NUM_IMAGE_BUFFERS = 2 }; ASI_ERROR_CODE status; - setControl(cg->cameraNumber, ASI_EXPOSURE, cg->cameraMinExposure_us, ASI_FALSE); + status = setControl(cg->cameraNumber, ASI_EXPOSURE, cg->cameraMinExposure_us, ASI_FALSE); + if (status != ASI_SUCCESS) + { + Log(0, "*** %s: ERROR: flushBufferedImages() setControl() returned %s\n", cg->ME, getRetCode(status)); + return(status); + } for (int i = 0; i < NUM_IMAGE_BUFFERS; i++) { @@ -341,8 +346,14 @@ void flushBufferedImages(config *cg, void *buf, size_t size) { Log(0, "*** %s: ERROR: flushBufferedImages() got %s\n", cg->ME, getRetCode(status)); } - // TODO: in theory if status == ASI_ERROR_TIMEOUT we could stop. + else + { + // ASI_ERROR_TIMEOUT. No more left. + return(status); + } } + + return(ASI_SUCCESS); } @@ -1599,8 +1610,8 @@ if (saved_newExposure_us != newExposure_us) if (CG.lastMean >= minAcceptableMean && CG.lastMean <= maxAcceptableMean) { // +++ at end makes it easier to see in log file - Log(2, " > Good image: mean within range of %d to %d ++++++++++, mean %d\n", - minAcceptableMean, maxAcceptableMean, (int)CG.lastMean); + Log(2, " > Good image: mean %d within range of %d to %d ++++++++++\n", + (int)CG.lastMean, minAcceptableMean, maxAcceptableMean); } else if (attempts > maxHistogramAttempts) { diff --git a/src/include/allsky_common.h b/src/include/allsky_common.h index d15d8dcbb..5eafbd8cf 100644 --- a/src/include/allsky_common.h +++ b/src/include/allsky_common.h @@ -26,11 +26,13 @@ // NOT_SET are items that aren't set yet and will be calculated at run time. // NOT_CHANGED items are command-line arguments where the default is camera-dependent, +// NO_DEFAULT items don't have a default value. // and we can't use NOT_SET because -1 may be a legal value. // IS_DEFAULT means the value is the same as the camera default, so don't pass to camera program // since it'll use the default anyway. #define NOT_SET -1 #define NOT_CHANGED -999999 +#define NO_DEFAULT -999998 #define IS_DEFAULT NOT_CHANGED // Defaults diff --git a/uninstall.sh b/uninstall.sh index a7aa54e3e..ff737f70f 100755 --- a/uninstall.sh +++ b/uninstall.sh @@ -3,11 +3,10 @@ [[ -z "${ALLSKY_HOME}" ]] && export ALLSKY_HOME="$(realpath "$(dirname "${BASH_ARGV0}")")" # ME="$(basename "${BASH_ARGV0}")" -#shellcheck disable=SC2086 source-path=. -source "${ALLSKY_HOME}/variables.sh" || exit ${ALLSKY_ERROR_STOP} +#shellcheck source-path=. +source "${ALLSKY_HOME}/variables.sh" || exit "${ALLSKY_ERROR_STOP}" -#shellcheck disable=SC2086 -cd "${ALLSKY_HOME}" || exit ${ALLSKY_ERROR_STOP} +cd "${ALLSKY_HOME}" || exit "${ALLSKY_ERROR_STOP}" MSG="This will remove all non-config, system files from your computer.\n" MSG="${MSG}Note: This only removes files in their default location.\n" diff --git a/upgrade.sh b/upgrade.sh index 0c507ad94..f069ed0ed 100755 --- a/upgrade.sh +++ b/upgrade.sh @@ -29,7 +29,7 @@ if [[ ${EUID} -eq 0 ]]; then fi #shellcheck disable=SC2086 -cd "${ALLSKY_HOME}" || exit ${ALLSKY_ERROR_STOP} +cd "${ALLSKY_HOME}" || exit ${ALLSKY_ERROR_STOP} #### @@ -97,9 +97,6 @@ while [ $# -gt 0 ]; do --no-check) FORCE_CHECK="false" ;; - --force-check) - FORCE_CHECK="true" - ;; *) display_msg error "Unknown argument: '${ARG}'." OK="false" @@ -128,7 +125,6 @@ if [[ ${FORCE_CHECK} == "true" || ${BRANCH} == "${GITHUB_MAIN_BRANCH}" ]]; then else # See if there's a newer version of this script; if so, download it and execute it. - BRANCH="$(getBranch)" || exit 2 NEWER_SCRIPT="/tmp/${ME}" checkAndGetNewerFile --branch "${BRANCH}" "${CURRENT_SCRIPT}" "${ME}" "${NEWER_SCRIPT}" RET=$? @@ -140,17 +136,12 @@ if [[ ${FORCE_CHECK} == "true" || ${BRANCH} == "${GITHUB_MAIN_BRANCH}" ]]; then fi fi -# TODO: these are here to keep shellcheck quiet. +# TODO: these are here to keep shellcheck quiet while this script is incomplete. DEBUG="${DEBUG}" DEBUG_ARG="${DEBUG_ARG}" FUNCTION="${FUNCTION}" WORD="${WORD}" -#shellcheck disable=SC2086,SC1091 # file doesn't exist in GitHub -source "${ALLSKY_CONFIG}/config.sh" || exit ${ALLSKY_ERROR_STOP} -#shellcheck disable=SC2086,SC1091 # file doesn't exist in GitHub -source "${ALLSKY_CONFIG}/ftp-settings.sh" || exit ${ALLSKY_ERROR_STOP} - if [[ ${ACTION} == "upgrade" ]]; then : @@ -179,7 +170,7 @@ if [[ ${ACTION} == "upgrade" ]]; then # cd # Git new code into ${ALLSKY_HOME} # cd ${ALLSKY_HOME} - # Run: ./install.sh $DEBUG_ARG .... --doUpgrade + # Run: ./install.sh ${DEBUG_ARG} .... --doUpgrade # --doUpgrade tells it to use prior version without asking and to # not display header, change messages to say "upgrade", not "install", etc. # ?? anything else? @@ -198,28 +189,19 @@ if [[ ${ACTION} == "upgrade" ]]; then elif [[ ${ACTION} == "restore" ]]; then : - # If running in $ALLSKY_HOME # us 1st time through + # If running in ${ALLSKY_HOME} # us 1st time through # Make sure ${ALLSKY_HOME}-OLD exists # If not, warn user and exit: # "No prior version to restore from: ${ALLSKY_HOME}-OLD does not exist". # cp ${ME} /tmp # chmod 775 /tmp/${ME} - # exec /tmp/${ME} --restore ${ALL_ARGS} $ALLSKY_HOME + # exec /tmp/${ME} --restore ${ALL_ARGS} ${ALLSKY_HOME} # Else # running from /tmp - do the actual work # Stop allsky # mv $ALLSKY_HOME} ${ALLSKY_HOME}-new_tmp - # mv ${ALLSKY_HOME}-OLD $ALLSKY_HOME - # move images from ${ALLSKY_HOME}-new_tmp to $ALLSKY_HOME - # move darks from ${ALLSKY_HOME}-new_tmp to $ALLSKY_HOME - # copy scripts/endOfNight_additionalSteps.sh from ${ALLSKY_HOME}-new_tmp to $ALLSKY_HOME - - # Prompt the user if they want to: - # restore their old "images" folder (if there's anything in it) - # restore their old "darks" folder (if there's anything in it) - # restore their old configuration settings - # (config.sh, ftp-settings.sh, scripts/endOfNight_additionalSteps.sh) - # upgrade their WebUI (if installed) - # upgrade their Website (if installed) + # mv ${ALLSKY_HOME}-OLD ${ALLSKY_HOME} + # move images from ${ALLSKY_HOME}-new_tmp to ${ALLSKY_HOME} + # move darks from ${ALLSKY_HOME}-new_tmp to ${ALLSKY_HOME} fi diff --git a/variables.sh b/variables.sh index 9c632eed0..f1e3412ee 100644 --- a/variables.sh +++ b/variables.sh @@ -16,7 +16,7 @@ if [[ -z "${ALLSKY_VARIABLE_SET}" ]]; then # If we're not on a tty output is likely being written to a file, so don't use colors. # The "w" colors are for when output may go to a web page. if tty --silent ; then - ON_TTY=1 + ON_TTY="true" GREEN="\033[0;32m"; wOK="${GREEN}" YELLOW="\033[0;33m"; wWARNING="${YELLOW}" RED="\033[0;31m"; wERROR="${RED}" @@ -26,7 +26,7 @@ if [[ -z "${ALLSKY_VARIABLE_SET}" ]]; then wBOLD="["; wNBOLD="]" wBR="\n" else - ON_TTY=0 + ON_TTY="false" GREEN=""; wOK="" YELLOW=""; wWARNING="" RED=""; wERROR="" @@ -78,6 +78,9 @@ if [[ -z "${ALLSKY_VARIABLE_SET}" ]]; then # Holds a count of continuous "bad" images ALLSKY_BAD_IMAGE_COUNT="${ALLSKY_TMP}/bad_image_count.txt" + # Holds the PID of the process that called timelapse.sh. + ALLSKY_TIMELAPSE_PID_FILE="${ALLSKY_TMP}/timelapse-pid.txt" + # Holds information on what the user needs to do after an installation. ALLSKY_INSTALLATION_LOGS="${ALLSKY_CONFIG}/installation_logs" POST_INSTALLATION_ACTIONS="${ALLSKY_INSTALLATION_LOGS}/post-installation_actions.txt" @@ -144,7 +147,28 @@ if [[ -z "${ALLSKY_VARIABLE_SET}" ]]; then # They are configuration files so go in ${ALLSKY_CONFIG) like all the other config files. CC_FILE="${ALLSKY_CONFIG}/cc.json" SETTINGS_FILE="${ALLSKY_CONFIG}/settings.json" + if [[ -s ${SETTINGS_FILE} ]]; then + # Get the name of the file the websites will look for, and split into name and extension. + FULL_FILENAME="$( jq -r ".filename" "${SETTINGS_FILE}" )" + EXTENSION="${FULL_FILENAME##*.}" + FILENAME="${FULL_FILENAME%.*}" + + CAMERA_TYPE="$( jq -r '.cameratype' "${SETTINGS_FILE}" )" + CAMERA_MODEL="$( jq -r '.cameramodel' "${SETTINGS_FILE}" )" + + # So scripts can conditionally output messages. + ALLSKY_DEBUG_LEVEL="$( jq -r '.debuglevel' "${SETTINGS_FILE}" )" + + # ALLSKY_VERSION is updated during installation + ALLSKY_VERSION="v2023.05.01_02" + else + ALLSKY_DEBUG_LEVEL=1 + fi OPTIONS_FILE="${ALLSKY_CONFIG}/options.json" + ALLSKY_ENV="${ALLSKY_HOME}/env.json" + + IMG_DIR="current/tmp" + CAPTURE_SAVE_DIR="${ALLSKY_TMP}" # These EXIT codes from the capture programs must match what's in src/include/allsky_common.h # Anything at or above EXIT_ERROR_STOP is unrecoverable and the service must be stopped diff --git a/version b/version index d310619c7..c685078eb 100644 --- a/version +++ b/version @@ -1 +1 @@ -v2023.05.01_03dev +v2023.90.90 \ No newline at end of file diff --git a/website/install.sh b/website/install.sh index f87fe8af0..b722fde04 100755 --- a/website/install.sh +++ b/website/install.sh @@ -3,17 +3,12 @@ [[ -z ${ALLSKY_HOME} ]] && export ALLSKY_HOME="$(realpath "$(dirname "${BASH_ARGV0}")"/..)" ME="$(basename "${BASH_ARGV0}")" -#shellcheck disable=SC2086 source-path=. -source "${ALLSKY_HOME}/variables.sh" || exit ${ALLSKY_ERROR_STOP} -#shellcheck disable=SC2086 source-path=scripts -source "${ALLSKY_SCRIPTS}/functions.sh" || exit ${ALLSKY_ERROR_STOP} -#shellcheck disable=SC2086 source-path=scripts -source "${ALLSKY_SCRIPTS}/installUpgradeFunctions.sh" || exit ${ALLSKY_ERROR_STOP} - -#shellcheck disable=SC2086,SC1091 # file doesn't exist in GitHub -source "${ALLSKY_CONFIG}/config.sh" || exit_installation ${ALLSKY_ERROR_STOP} -#shellcheck disable=SC2086,SC1091 # file doesn't exist in GitHub -source "${ALLSKY_CONFIG}/ftp-settings.sh" || exit_installation ${ALLSKY_ERROR_STOP} +#shellcheck source-path=. +source "${ALLSKY_HOME}/variables.sh" || exit "${ALLSKY_ERROR_STOP}" +#shellcheck source-path=scripts +source "${ALLSKY_SCRIPTS}/functions.sh" || exit "${ALLSKY_ERROR_STOP}" +#shellcheck source-path=scripts +source "${ALLSKY_SCRIPTS}/installUpgradeFunctions.sh" || exit "${ALLSKY_ERROR_STOP}" TITLE="Allsky Website Installer" @@ -95,8 +90,7 @@ usage_and_exit() echo echo "'--function' executes the specified function and quits." echo - # shellcheck disable=SC2086 - exit_installation ${RET} + exit_installation "${RET}" } @@ -373,10 +367,10 @@ modify_locations() { ##### Create and upload a new data.json file and files needed to display settings. upload_data_json_file() { - LOCAL_or_REMOTE="${1}" # is this for a local or remote Website, or both? - display_msg --log progress "Uploading initial files to ${LOCAL_or_REMOTE} Website(s)." + LOCAL_or_REMOTE="${1}" # is this for a local or remote Website? + display_msg --log progress "Uploading initial files to ${LOCAL_or_REMOTE} Websites." - OUTPUT="$( "${ALLSKY_SCRIPTS}/postData.sh" --websites "${LOCAL_or_REMOTE}" --allFiles 2>&1 )" + OUTPUT="$( "${ALLSKY_SCRIPTS}/postData.sh" "--${LOCAL_or_REMOTE}-web" --allFiles 2>&1 )" if [[ $? -ne 0 || ! -f ${ALLSKY_TMP}/data.json ]]; then MSG="Unable to upload initial files:" if echo "${OUTPUT}" | grep --silent "${ALLSKY_WEBSITE_VIEWSETTINGS_DIRECTORY_NAME}: No such file or" ; then @@ -386,9 +380,12 @@ upload_data_json_file() { MSG="${MSG}\nThe '${ALLSKY_WEBSITE_VIEWSETTINGS_DIRECTORY_NAME}' directory does not exist." fi MSG="${MSG}\n${OUTPUT}" - MSG="${MSG}\nMake sure 'REMOTE_HOST' is set to a valid server or to '' in 'ftp-settings.sh'," - MSG="${MSG}\n then run: ${ALLSKY_SCRIPTS}/postData.sh" - MSG="${MSG}\nto create and upload a 'data.json' file." + if [[ ${LOCAL_or_REMOTE} == "remote" ]]; then + MSG="${MSG}\nMake sure you have defined all necessary variables in" + MSG="${MSG}\nthe 'Remote Website Settings' section of the WebUI's settings," + MSG="${MSG}\n then run: ${ALLSKY_SCRIPTS}/postData.sh --remote-web" + MSG="${MSG}\nto create and upload a 'data.json' file." + fi display_msg --log error "${MSG}" return 1 fi @@ -477,6 +474,7 @@ create_website_configuration_file() { if [[ ${INDEX} -ge 0 ]]; then MINI_TLAPSE_DISPLAY="${PARENT}[${INDEX}].display" MINI_TLAPSE_URL="${PARENT}[${INDEX}].url" + TIMELAPSE_MINI_IMAGES="$( settings ".minitimelapsenumimages" )" if [[ ${TIMELAPSE_MINI_IMAGES:-0} -eq 0 ]]; then MINI_TLAPSE_DISPLAY_VALUE="false" MINI_TLAPSE_URL_VALUE="" @@ -500,9 +498,9 @@ create_website_configuration_file() { fi # Convert latitude and longitude to use N, S, E, W. - LATITUDE="$(convertLatLong "${LATITUDE}" "latitude")" + LATITUDE="$( convertLatLong "${LATITUDE}" "latitude" )" [[ -z ${LATITUDE} ]] && display_msg --log warning "latitude is empty" - LONGITUDE="$(convertLatLong "${LONGITUDE}" "longitude")" + LONGITUDE="$( convertLatLong "${LONGITUDE}" "longitude" )" [[ -z ${LONGITUDE} ]] && display_msg --log warning "longitude is empty" if [[ ${LATITUDE:1,-1} == "S" ]]; then # last character @@ -513,7 +511,8 @@ create_website_configuration_file() { LOCATION="$(settings ".location")" OWNER="$(settings ".owner")" - CAMERA_MODEL="$(settings ".cameraModel")" + CAMERA_MODEL="$(settings ".cameramodel")" + CAMERA="${CAMERA_TYPE}${CAMERA_MODEL}" LENS="$(settings ".lens")" COMPUTER="$(sed --quiet -e 's/Raspberry Pi/RPi/' -e '/^Model/ s/.*: // p' /proc/cpuinfo)" @@ -548,12 +547,12 @@ create_website_configuration_file() { ##### Update the Website version in the config file update_version_in_config_file() { - local CONFIG_FILE="${1}" + local FILE="${1}" if [[ ${PRIOR_WEBSITE_VERSION} != "${NEW_WEBSITE_VERSION}" ]]; then - display_msg --log progress "Updating Website version in '${CONFIG_FILE}' to ${NEW_WEBSITE_VERSION}" + display_msg --log progress "Updating Website version in '${FILE}' to ${NEW_WEBSITE_VERSION}" # shellcheck disable=SC2086 "${ALLSKY_SCRIPTS}/updateWebsiteConfig.sh" --verbosity silent ${DEBUG_ARG} \ - --config "${CONFIG_FILE}" \ + --config "${FILE}" \ config.AllskyWebsiteVersion "AllskyWebsiteVersion" "${NEW_WEBSITE_VERSION}" else display_msg "${LOG_TYPE}" progress "Website version already at ${NEW_WEBSITE_VERSION} - no need to update." @@ -565,7 +564,6 @@ update_version_in_config_file() NEEDS_NEW_CONFIGURATION_FILE="true" modify_configuration_variables() { - if [[ ${DEBUG} == "true" ]];then display_msg --log debug "modify_configuration_variables(): PRIOR_WEBSITE_TYPE=${PRIOR_WEBSITE_TYPE}" fi @@ -621,10 +619,10 @@ check_if_remote_website_ready() MSG="Setting up a remote Allsky Website requires that you first:\n" MSG="${MSG}\n 1. Have Allsky configured and running the way you want it.\n" MSG="${MSG}\n 2. Upload the Allsky Website files to your remote server.\n" - MSG="${MSG}\n 3. Update 'ftp-settings.sh' using the WebUI's 'Editor' page" - MSG="${MSG}\n to point to the remote server.\n" + MSG="${MSG}\n 3. Update the 'Remote Website Settings' section of the WebUI's" + MSG="${MSG}\n 'Allsky Settings' page to point to the remote webserver.\n" MSG="${MSG}\n 4. Enter the URL of the remote Website into the 'Website URL'" - MSG="${MSG}\n field in the WebUI's 'Allsky Settings' page," + MSG="${MSG}\n field in the same WebUI page," MSG="${MSG}\n even if you are not displaying your Website on the Allsky Map." MSG="${MSG}\n\n\nHave you completed these steps?" if ! whiptail --title "${TITLE}" --yesno "${MSG}" 22 80 3>&1 1>&2 2>&3; then @@ -644,17 +642,30 @@ check_if_remote_website_ready() do_remote_website() { # Make sure things really are set up, despite what the user said. -# TODO: not all protocols require REMOTE_HOST +# TODO: at end, enable "useremotewebsite" + + PROTOCOL="$( settings ".remotewebsiteprotocol" )" + if [[ -z ${PROTOCOL} ]]; then + MSG="No protocol is specified for the Remote Allsky Website" + MSG="${MSG} in the 'Allsky Settings' page of the WebUI." + display_msg --log error "${MSG}" + exit_installation 1 + fi + OK="true" - if [[ -z ${REMOTE_HOST} ]]; then - MSG="The 'REMOTE_HOST' must be set in 'ftp-settings.sh'\n" - MSG="${MSG}in order to do a remote Website installation.\n" - MSG="${MSG}Please set it, the password, and other information, then re-run this installation." + + if ! X="$( check_remote_server "REMOTEWEBSITE" )" ; then + MSG="The fields in the 'Remote Website Settings' section of the WebUI's" + MSG="${MSG} 'Allsky Settings' page must be set" + MSG="${MSG} in order to do a remote Website installation.\n" + MSG="${MSG}When done, re-run this installation." + MSG="${MSG}\nIssues are:\n${X}" display_msg error "${MSG}" - display_msg --logonly error "REMOTE_HOST not set." + display_msg --logonly error "${X}" OK="false" fi - WEBURL="$( settings ".websiteurl" )" + WEBURL="$( settings ".remotewebsiteurl" )" + if [[ -z ${WEBURL} ]]; then MSG="The 'Website URL' setting must be defined in the WebUI\n" MSG="${MSG}in order to do a remote Website installation.\n" @@ -671,12 +682,13 @@ do_remote_website() { display_msg --log progress "Testing upload to remote Website." display_msg info " When done you can remove '${TEST_FILE_NAME}' from your remote server." echo "This is a test file and can be removed." > "${TEST_FILE}" - if ! RET="$("${ALLSKY_SCRIPTS}/upload.sh" \ + if ! RET="$( "${ALLSKY_SCRIPTS}/upload.sh" -num 1 \ "${TEST_FILE}" \ "${IMAGE_DIR}" \ "${TEST_FILE_NAME}" \ - "UploadTest")" ; then + "UploadTest" )" ; then MSG="Unable to upload a test file.\n" + MSG="${MSG}\nSee the 'Troubleshooting -> Uploads' documentation to fix the problem." display_msg --log error "${MSG}" display_msg --log info "${RET}" OK="false" @@ -689,7 +701,7 @@ do_remote_website() { # TODO: Will also need to change the messages above. # Tell the remote server to check the sanity of the Website. - # This also creates some necessary directories. + # This can create missing directories that initially have no content. display_msg --log progress "Sanity checking remote Website." [[ ${WEBURL: -1} != "/" ]] && WEBURL="${WEBURL}/" if [[ ${DEBUG} == "true" ]]; then @@ -698,17 +710,20 @@ do_remote_website() { D="" fi X="$( curl --show-error --silent "${WEBURL}?check=1${D}" )" - if ! echo "${X}" | grep --silent "^SUCCESS$" ; then + if ! echo "${X}" | grep --silent "^SUCCESS" ; then MSG="Sanity check of remote Website (${WEBURL}) failed." - MSG2="${MSG}\nYou will need to manually fix." - display_msg warning "${MSG}${MSG2}" - display_msg --logonly warning "${MSG}" - echo -e "${X}" + display_msg --log warning "${MSG}\n${X}" + MSG="\nPlease fix and run the installation again." + display_msg warning "${MSG}" + OK="false" fi + [[ ${OK} == "false" ]] && exit_installation 1 + if [[ -f ${ALLSKY_REMOTE_WEBSITE_CONFIGURATION_FILE} ]]; then # The user is upgrading a new-style remote Website. - display_msg --log progress "You should continue to configure your remote Allsky Website via the WebUI." + MSG="You should continue to configure your remote Allsky Website via the WebUI." + display_msg --log progress "${MSG}" update_version_in_config_file "${ALLSKY_REMOTE_WEBSITE_CONFIGURATION_FILE}" @@ -718,32 +733,13 @@ do_remote_website() { # Don't know if the user is upgrading an old-style remote website, # or they don't even have a remote website. - MSG="You can keep a copy of your remote Website's configuration file on your Pi" - MSG="${MSG}\nso you can easily edit it in the WebUI and have it automatically uploaded." - MSG="${MSG}\n** This is the recommended way of making changes to the configuration **." - MSG="${MSG}\n\nWould you like to do that?" - if (whiptail --title "${TITLE}" --yesno "${MSG}" 15 60 3>&1 1>&2 2>&3); then - create_website_configuration_file - - MSG="\nTo edit the remote configuration file, go to the 'Editor' page in the WebUI\n" - MSG="${MSG}and select '${ALLSKY_REMOTE_WEBSITE_CONFIGURATION_NAME} (remote Allsky Website)'.\n" - display_msg info "${MSG}" - display_msg --logonly info "User will use local copy of remote Website config file." - else - # upload_data_json_file needs the remote configuration file to exist or else it thinks - # there isn't a remote site. - ( - echo "Do NOT remove or change this file." - echo "It indicates there is a remote Allsky Website although it's not configured from the Pi." - ) > "${ALLSKY_REMOTE_WEBSITE_CONFIGURATION_FILE}" - - MSG="You need to manually copy '${REPO_WEBCONFIG_FILE}'" - # ALLSKY_WEBSITE_CONFIGURATION_NAME is what it's called on the remote server - MSG="${MSG}to your remote server and rename it to '${ALLSKY_WEBSITE_CONFIGURATION_NAME}'," - MSG="${MSG}then modify it on yuor server." - display_msg warning "${MSG}" - display_msg --logonly info "User will NOT use local copy of remote Website config file." - fi + MSG="The master copy of your remote Website's configuration file will be on your Pi." + MSG="${MSG}\nYou must edit it in the WebUI; it will then be automatically uploaded." + MSG="${MSG}\nTo edit the remote configuration file, go to the WebUI's 'Editor' page and\n" + MSG="${MSG}select '${ALLSKY_REMOTE_WEBSITE_CONFIGURATION_NAME} (remote Allsky Website)'.\n" + display_msg info "${MSG}" + + create_website_configuration_file fi upload_data_json_file "remote" || exit_installation 2 @@ -845,8 +841,7 @@ download_Allsky_Website() { display_msg --log progress "Downloading Allsky Website files${B} into ${ALLSKY_WEBSITE}." TMP="/tmp/git.install.tmp" - # shellcheck disable=SC2086 - git clone -b ${BRANCH} "${GITHUB_ROOT}/allsky-website.git" "${ALLSKY_WEBSITE}" > "${TMP}" 2>&1 + git clone -b "${BRANCH}" "${GITHUB_ROOT}/allsky-website.git" "${ALLSKY_WEBSITE}" > "${TMP}" 2>&1 if [[ $? -ne 0 ]]; then display_msg --log error "Unable to get Allsky Website files from git." display_msg --logonly info "$( cat "${TMP}" )" @@ -896,13 +891,25 @@ restore_prior_files() { display_msg "${LOG_TYPE}" info "No prior startrails to restore." fi - D="${PRIOR_WEBSITE}/myImages" + D="${PRIOR_WEBSITE}/myFiles" if [[ -d ${D} ]]; then count=$(find "${D}" | wc -l) if [[ ${count} -gt 1 ]]; then - display_msg --log progress "Restoring prior 'myImages' directory." + display_msg --log progress "Restoring prior 'myFiles' directory." mv "${D}" "${ALLSKY_WEBSITE}" fi + else + display_msg "${LOG_TYPE}" info "No prior 'myFiles' to restore." + fi + + # This is the old name. + D="${PRIOR_WEBSITE}/myImages" + if [[ -d ${D} ]]; then + count=$(find "${D}" | wc -l) + if [[ ${count} -gt 1 ]]; then + display_msg --log progress "Moving prior 'myImages' contents to 'myFiles'." + mv "${D}"* "${ALLSKY_WEBSITE}/myFiles" + fi else display_msg "${LOG_TYPE}" info "No prior 'myImages' to restore." fi @@ -987,7 +994,9 @@ while [[ $# -gt 0 ]]; do shift done -[[ -z ${FUNCTION} && ${UPDATE} == "false" ]] && display_msg "${LOG_TYPE}" info "STARTING INSTALLATON AT $(date).\n" +if [[ -z ${FUNCTION} && ${UPDATE} == "false" ]]; then + display_msg "${LOG_TYPE}" info "STARTING INSTALLATON AT $(date).\n" +fi [[ ${HELP} == "true" ]] && usage_and_exit 0 [[ ${OK} == "false" ]] && usage_and_exit 1 @@ -995,12 +1004,13 @@ done # Make sure the settings file isn't corrupted. if ! json_pp < "${SETTINGS_FILE}" > /dev/null; then - display_msg --log error "Settings file '${SETTINGS_FILE} is corrupted.\nFix, then re-run this installation." + MSG="Settings file '${SETTINGS_FILE} is corrupted.\nFix, then re-run this installation." + display_msg --log error "${MSG}" exit_installation 1 fi -LATITUDE="$(settings ".latitude")" -LONGITUDE="$(settings ".longitude")" +LATITUDE="$( settings ".latitude" )" +LONGITUDE="$( settings ".longitude" )" if [[ -z ${LATITUDE} || -z ${LONGITUDE} ]]; then MSG="Latitude and Longitude must be set in the WebUI before the Allsky Website\ncan be installed." display_msg --log error "${MSG}" @@ -1059,7 +1069,7 @@ mkdir -p \ "${ALLSKY_WEBSITE}/startrails/thumbnails" \ "${ALLSKY_WEBSITE}/keograms/thumbnails" \ "${ALLSKY_WEBSITE}/videos/thumbnails" \ - "${ALLSKY_WEBSITE}/myImages" + "${ALLSKY_WEBSITE}/myFiles" ##### Set permissions on files and directories set_permissions @@ -1087,4 +1097,4 @@ if [[ ${NEEDS_NEW_CONFIGURATION_FILE} == "true" ]]; then fi echo -exit_installation 0 +exit_installation 0 \ No newline at end of file