From fc7f467539851adde52ab2f96305427ad771c194 Mon Sep 17 00:00:00 2001 From: Christian Kvasny Date: Mon, 23 Oct 2023 13:37:00 +0200 Subject: [PATCH 01/59] Create main_versioning.yml --- .github/workflows/main_versioning.yml | 38 +++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 .github/workflows/main_versioning.yml diff --git a/.github/workflows/main_versioning.yml b/.github/workflows/main_versioning.yml new file mode 100644 index 0000000..5b5cae8 --- /dev/null +++ b/.github/workflows/main_versioning.yml @@ -0,0 +1,38 @@ +name: Main Versioning + +# This workflow triggers when a pull request to the 'main' branch is closed +on: + pull_request: + types: [closed] + branches: + - main + +jobs: + update_version: + # Ensure this job only runs if the PR was merged, not just closed without merging + if: github.event.pull_request.merged == true + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Update VERSION in main + run: | + # Remove the -DEV suffix from the version number + NEW_VERSION=$(sed -n 's/VERSION=//p' scripts/controller.sh | sed 's/-DEV//') + sed -i "s/VERSION=.*/VERSION=$NEW_VERSION/" scripts/controller.sh + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + git commit -am "Update version to $NEW_VERSION" + git push origin main + + - name: Merge changes back to dev and append -DEV + run: | + git checkout dev + git merge main --no-ff -m "Merge main changes back to dev" + # Append -DEV back to the version number for the dev branch + NEW_DEV_VERSION="$NEW_VERSION-DEV" + sed -i "s/VERSION=.*/VERSION=$NEW_DEV_VERSION/" scripts/controller.sh + git commit -am "Update version to $NEW_DEV_VERSION in dev" + git push origin dev From e08056a5dcbee3b00e7663fbe317f84f9c03ebbd Mon Sep 17 00:00:00 2001 From: Christian Kvasny Date: Mon, 23 Oct 2023 20:32:47 +0200 Subject: [PATCH 02/59] Update main_versioning.yml fix missing " --- .github/workflows/main_versioning.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/main_versioning.yml b/.github/workflows/main_versioning.yml index 5b5cae8..dc54a01 100644 --- a/.github/workflows/main_versioning.yml +++ b/.github/workflows/main_versioning.yml @@ -21,10 +21,10 @@ jobs: run: | # Remove the -DEV suffix from the version number NEW_VERSION=$(sed -n 's/VERSION=//p' scripts/controller.sh | sed 's/-DEV//') - sed -i "s/VERSION=.*/VERSION=$NEW_VERSION/" scripts/controller.sh + sed -i "s/VERSION=.*/VERSION=${NEW_VERSION}/" scripts/controller.sh git config --local user.email "action@github.com" git config --local user.name "GitHub Action" - git commit -am "Update version to $NEW_VERSION" + git commit -am "Update version to ${NEW_VERSION}" git push origin main - name: Merge changes back to dev and append -DEV @@ -32,7 +32,7 @@ jobs: git checkout dev git merge main --no-ff -m "Merge main changes back to dev" # Append -DEV back to the version number for the dev branch - NEW_DEV_VERSION="$NEW_VERSION-DEV" - sed -i "s/VERSION=.*/VERSION=$NEW_DEV_VERSION/" scripts/controller.sh - git commit -am "Update version to $NEW_DEV_VERSION in dev" + NEW_DEV_VERSION="${NEW_VERSION}-DEV" + sed -i "s/VERSION=.*/VERSION=${NEW_DEV_VERSION}/" scripts/controller.sh + git commit -am "Update version to ${NEW_DEV_VERSION} in dev" git push origin dev From b9abf287e63d42ac11c998ddec335c995d27724b Mon Sep 17 00:00:00 2001 From: Christian Kvasny Date: Mon, 23 Oct 2023 20:49:50 +0200 Subject: [PATCH 03/59] Update main_versioning.yml --- .github/workflows/main_versioning.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main_versioning.yml b/.github/workflows/main_versioning.yml index dc54a01..32fb500 100644 --- a/.github/workflows/main_versioning.yml +++ b/.github/workflows/main_versioning.yml @@ -33,6 +33,6 @@ jobs: git merge main --no-ff -m "Merge main changes back to dev" # Append -DEV back to the version number for the dev branch NEW_DEV_VERSION="${NEW_VERSION}-DEV" - sed -i "s/VERSION=.*/VERSION=${NEW_DEV_VERSION}/" scripts/controller.sh + sed -i "s/^VERSION=.*$/VERSION=\"${NEW_VERSION}\"/" scripts/controller.sh git commit -am "Update version to ${NEW_DEV_VERSION} in dev" git push origin dev From 746c8f8275548cb8f3916cf8c04ba3afc148db84 Mon Sep 17 00:00:00 2001 From: Christian Kvasny Date: Mon, 23 Oct 2023 20:59:22 +0200 Subject: [PATCH 04/59] Update main_versioning.yml --- .github/workflows/main_versioning.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main_versioning.yml b/.github/workflows/main_versioning.yml index 32fb500..12053ce 100644 --- a/.github/workflows/main_versioning.yml +++ b/.github/workflows/main_versioning.yml @@ -20,8 +20,8 @@ jobs: - name: Update VERSION in main run: | # Remove the -DEV suffix from the version number - NEW_VERSION=$(sed -n 's/VERSION=//p' scripts/controller.sh | sed 's/-DEV//') - sed -i "s/VERSION=.*/VERSION=${NEW_VERSION}/" scripts/controller.sh + NEW_VERSION=$(sed -n 's/^VERSION=\"\(.*\)-DEV\"$/\1/p' scripts/controller.sh) + sed -i "s/^VERSION=\".*\"$/VERSION=\"${NEW_VERSION}\"/" scripts/controller.sh git config --local user.email "action@github.com" git config --local user.name "GitHub Action" git commit -am "Update version to ${NEW_VERSION}" @@ -33,6 +33,6 @@ jobs: git merge main --no-ff -m "Merge main changes back to dev" # Append -DEV back to the version number for the dev branch NEW_DEV_VERSION="${NEW_VERSION}-DEV" - sed -i "s/^VERSION=.*$/VERSION=\"${NEW_VERSION}\"/" scripts/controller.sh + sed -i "s/^VERSION=\".*\"$/VERSION=\"${NEW_DEV_VERSION}\"/" scripts/controller.sh git commit -am "Update version to ${NEW_DEV_VERSION} in dev" git push origin dev From 4e7abab312a961948d06c665c24a2c9d9f5257d1 Mon Sep 17 00:00:00 2001 From: Christian Kvasny Date: Mon, 23 Oct 2023 23:49:15 +0200 Subject: [PATCH 05/59] Update controller.sh add version number add bash version info add skip info for not activated options fix Venus OS run failure remove bc and some stuff i can't remember --- scripts/controller.sh | 96 +++++++++++++++++++++++++------------------ 1 file changed, 56 insertions(+), 40 deletions(-) diff --git a/scripts/controller.sh b/scripts/controller.sh index dea098d..f887516 100755 --- a/scripts/controller.sh +++ b/scripts/controller.sh @@ -28,6 +28,8 @@ License=$( EOLICENSE ) +VERSION="2.3.0-DEV" + set -e if [ -z "$LANG" ]; then @@ -132,7 +134,7 @@ if [ -f "$DIR/config.txt" ]; then # Include the configuration file source "$DIR/config.txt" else - log_info "E: The file $DIR/config.txt was not found! Configure the existing sample.config.txt file and then save it as config.txt in the same directory." false + echo "E: The file $DIR/config.txt was not found! Configure the existing sample.config.txt file and then save it as config.txt in the same directory." false exit 127 fi @@ -237,7 +239,6 @@ fi unset num_tools_missing - ####################################### ### Begin of the functions... ### ####################################### @@ -301,7 +302,7 @@ parse_and_validate_config() { local file="$1" local errors="" - rotating_spinner & # Start the spinner in the background + rotating_spinner & # Start the spinner in the background local spinner_pid=$! # Get the PID of the spinner # Step 1: Parse @@ -315,7 +316,7 @@ parse_and_validate_config() { # Set the value in the associative array config_values["$key"]="$value" - done < "$file" + done <"$file" # Step 2: Validation for var_name in "${!valid_vars[@]}"; do @@ -479,14 +480,14 @@ download_entsoe_prices() { exit_with_cleanup 1 fi - if [ -n "$DEBUG" ]; then log_info "D: No delay of download of entsoe data since DEBUG variable set." >&2 "D: Entsoe file '$file' with price data downloaded" >&2; fi + if [ -n "$DEBUG" ]; then log_info "D: No delay of download of entsoe data since DEBUG variable set." "D: Entsoe file '$file' with price data downloaded" >&2 >&2; fi if [ ! -s "$file" ]; then log_info "E: Entsoe file '$file' is empty, please check your entsoe API Key." exit_with_cleanup 1 fi - if [ -n "$DEBUG" ]; then log_info "D: No delay of download of entsoe data since DEBUG variable set." >&2 "D: Entsoe file '$file' with price data downloaded"; fi + if [ -n "$DEBUG" ]; then log_info "D: No delay of download of entsoe data since DEBUG variable set." "D: Entsoe file '$file' with price data downloaded" >&2; fi awk ' # Capture content inside the tag @@ -545,13 +546,13 @@ download_entsoe_prices() { log_info "E: No prices found in the tomorrow XML data." } ' "$file" - + if [ -f "$output_file" ]; then - sort -g "$output_file" > "${output_file%.*}_sorted.${output_file##*.}" + sort -g "$output_file" >"${output_file%.*}_sorted.${output_file##*.}" timestamp=$(TZ=$TZ date +%d) - echo "date_now_day: $timestamp" >> "$output_file" + echo "date_now_day: $timestamp" >>"$output_file" fi - + # Check if tomorrow file contains next day prices if [ "$include_second_day" = 1 ] && grep -q "PT60M" "$file" && [ "$(wc -l <"$output_file")" -gt 3 ]; then cat $file10 >$file8 @@ -616,15 +617,15 @@ get_awattar_prices() { } get_tibber_prices() { - current_price=$(sed -n "${now_linenumber}s/.*\"${tibber_prices}\":\([^,]*\),.*/\1/p" "$file15") - lowest_price=$(sed -n "1s/.*\"${tibber_prices}\":\([^,]*\),.*/\1/p" "$file12") - second_lowest_price=$(sed -n "2s/.*\"${tibber_prices}\":\([^,]*\),.*/\1/p" "$file12") - third_lowest_price=$(sed -n "3s/.*\"${tibber_prices}\":\([^,]*\),.*/\1/p" "$file12") - fourth_lowest_price=$(sed -n "4s/.*\"${tibber_prices}\":\([^,]*\),.*/\1/p" "$file12") - fifth_lowest_price=$(sed -n "5s/.*\"${tibber_prices}\":\([^,]*\),.*/\1/p" "$file12") - sixth_lowest_price=$(sed -n "6s/.*\"${tibber_prices}\":\([^,]*\),.*/\1/p" "$file12") - highest_price=$(sed -n "s/.*\"${tibber_prices}\":\([^,]*\),.*/\1/p" "$file12" | awk 'BEGIN {max = 0} {if ($1 > max) max = $1} END {print max}') - average_price=$(sed -n "s/.*\"${tibber_prices}\":\([^,]*\),.*/\1/p" "$file12" | awk '{sum += $1} END {print sum/NR}') + current_price=$(sed -n "${now_linenumber}s/.*\"${tibber_prices}\":([^,]*),.*/\1/p" "$file15") + lowest_price=$(sed -n "1s/.*\"${tibber_prices}\":([^,]*),.*/\1/p" "$file12") + second_lowest_price=$(sed -n "2s/.*\"${tibber_prices}\":([^,]*),.*/\1/p" "$file12") + third_lowest_price=$(sed -n "3s/.*\"${tibber_prices}\":([^,]*),.*/\1/p" "$file12") + fourth_lowest_price=$(sed -n "4s/.*\"${tibber_prices}\":([^,]*),.*/\1/p" "$file12") + fifth_lowest_price=$(sed -n "5s/.*\"${tibber_prices}\":([^,]*),.*/\1/p" "$file12") + sixth_lowest_price=$(sed -n "6s/.*\"${tibber_prices}\":([^,]*),.*/\1/p" "$file12") + highest_price=$(sed -n "s/.*\"${tibber_prices}\":([^,]*),.*/\1/p" "$file12" | awk 'BEGIN {max = 0} {if ($1 > max) max = $1} END {print max}') + average_price=$(sed -n "s/.*\"${tibber_prices}\":([^,]*),.*/\1/p" "$file12" | awk '{sum += $1} END {print sum/NR}') } get_current_entsoe_day() { current_entsoe_day=$(sed -n 25p "$file10" | grep -Eo '[0-9]+'); } @@ -712,10 +713,12 @@ evaluate_conditions() { for condition in "${!conditions_ref[@]}"; do if [ -n "$DEBUG" ]; then - result="( ${descriptions_ref[$condition]} ) evaluates to $([ "${conditions_ref[$condition]}" -eq 1 ] && echo true || echo false)" + description_value="${descriptions_ref[$condition]}" + condition_evaluation=$([ "${conditions_ref[$condition]}" -eq 1 ] && echo true || echo false) + result="($description_value) evaluates to $condition_evaluation" log_info "D: condition_evaluation [ $result ]." >&2 fi - + if ((conditions_ref[$condition])) && [[ $condition_met -eq 0 ]]; then execute_ref=1 condition_met_ref="$condition" @@ -747,7 +750,10 @@ is_charging_economical() { if [ -n "$DEBUG" ]; then log_info "D: is_charging_economical [ $is_economical - $([ "$is_economical" -eq 1 ] && echo "false" || echo "true") ]." >&2 - log_info "D: if [ reference_price ($(millicentToEuro $reference_price)) > total_cost ($(millicentToEuro $total_cost)) ] result is $([ "$is_economical" -eq 1 ] && echo "false" || echo "true")." >&2 + reference_price_euro=$(millicentToEuro $reference_price) + total_cost_euro=$(millicentToEuro $total_cost) + is_economical_str=$([ "$is_economical" -eq 1 ] && echo "false" || echo "true") + log_info "D: if [ reference_price $reference_price_euro > total_cost $total_cost_euro ] result is $is_economical_str." >&2 fi return $is_economical @@ -872,12 +878,8 @@ euroToMillicent() { # Replace each comma with a period, fixme if this is wrong euro=$(echo "$euro" | sed 's/,/./g') - if which bc >/dev/null 2>&1; then - # Using bc to multiply the euro number and convert it to an integer - v=$(echo "scale=0; $euro * 10^$potency / 1" | bc) - else - v=$(awk "BEGIN {print int($euro * (10 ^ $potency))}") - fi + # v=$(awk "BEGIN {print int($euro * (10 ^ $potency))}") + v=$(awk -v euro="$euro" -v potency="$potency" 'BEGIN {printf "%.0f", euro * (10 ^ potency)}') if [ -z "$v" ]; then log_info "E: Could not translate '$euro' to an integer." @@ -898,26 +900,28 @@ euroToMillicent_test() { log_info() { local msg="$1" - local prefix=$(echo "$msg" | head -n 1 | cut -d' ' -f1) # Extract the first word from the first line - local color="\033[1m" # Default color - local writeToLog=true # Default is true + local prefix=$(echo "$msg" | head -n 1 | cut -d' ' -f1) # Extract the first word from the first line + local color="\033[1m" # Default color + local writeToLog=true # Default is true case "$prefix" in - "E:") color="\033[1;31m" ;; # Bright Red - "D:") color="\033[1;34m" # Bright Blue - writeToLog=false ;; # Default to not log debug messages - "W:") color="\033[1;33m" ;; # Bright Yellow - "I:") color="\033[1;32m" ;; # Bright Green + "E:") color="\033[1;31m" ;; # Bright Red + "D:") + color="\033[1;34m" # Bright Blue + writeToLog=false + ;; # Default to not log debug messages + "W:") color="\033[1;33m" ;; # Bright Yellow + "I:") color="\033[1;32m" ;; # Bright Green esac - writeToLog="${2:-$writeToLog}" # Override default if second parameter is provided + writeToLog="${2:-$writeToLog}" # Override default if second parameter is provided # Print to console with color codes printf "${color}%b\033[0m\n" "$msg" # If we should write to the log, write without color codes if [ "$writeToLog" == "true" ]; then - echo -e "$msg" | sed 's/\x1b\[[0-9;]*m//g' >> "$LOG_FILE" + echo -e "$msg" | sed 's/\x1b\[[0-9;]*m//g' >>"$LOG_FILE" fi } @@ -935,9 +939,12 @@ exit_with_cleanup() { echo >>"$LOG_FILE" +log_info "I: Bash Version: $(bash --version | head -n 1)" +log_info "I: Spotmarket-Switcher - Version $VERSION" + parse_and_validate_config "$DIR/config.txt" # if [ $? -eq 1 ]; then - # Handle error +# Handle error # fi # An independent segment to test the conversion of floats to integers @@ -1087,6 +1094,8 @@ if ((use_solarweather_api_to_abort == 1)); then log_info "E: File '$file3' is empty, please check your API Key if download is still not possible tomorrow." fi find "$file3" -size 0 -delete # FIXME - looks wrong and complicated - simple RM included in prior if clause? +else + log_info "W: skip Solarweather. not activated" fi charging_condition_met="" @@ -1173,15 +1182,21 @@ if ((execute_charging == 1 && use_victron_charger == 1)); then fi elif ((execute_charging != 1 && use_victron_charger == 1)); then manage_charging "off" "Charging was not executed." +else + log_info "W: skip Victron Charger. not activated" fi # Execute Fritz DECT on command if ((use_fritz_dect_sockets == 1)); then manage_fritz_sockets +else + log_info "W: skip Fritz DECT. not activated" fi if ((use_shelly_wlan_sockets == 1)); then manage_shelly_sockets +else + log_info "W: skip Shelly Api. not activated" fi echo >>"$LOG_FILE" @@ -1189,6 +1204,7 @@ echo >>"$LOG_FILE" # Rotating log files if [ -f "$LOG_FILE" ]; then if [ "$(du -k "$LOG_FILE" | awk '{print $1}')" -gt "$LOG_MAX_SIZE" ]; then + log_info "I: Rotating log files" mv "$LOG_FILE" "${LOG_FILE}.$(date +%Y%m%d%H%M%S)" touch "$LOG_FILE" find . -maxdepth 1 -name "${LOG_FILE}*" -type f -exec ls -1t {} + | @@ -1199,5 +1215,5 @@ if [ -f "$LOG_FILE" ]; then fi if [ -n "$DEBUG" ]; then - log_info "D: [ OK ]" >&2 + log_info "D: \[ OK \]" >&2 fi From 0cd102399f12f5c46f15a65c6f51d82ed535de28 Mon Sep 17 00:00:00 2001 From: Christian Kvasny Date: Mon, 23 Oct 2023 23:52:03 +0200 Subject: [PATCH 06/59] Update run Fix Venus OS failure --- scripts/run | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/scripts/run b/scripts/run index 82a2441..23cb308 100755 --- a/scripts/run +++ b/scripts/run @@ -1,6 +1,7 @@ -#!/bin/sh +#!/bin/bash -License=$(cat < "$rc_local_file" + echo "#!/bin/sh" >"$rc_local_file" chmod +x "$rc_local_file" - $code fi # Check if the code is already in rc.local, if not, add it if ! grep -qF "$code" "$rc_local_file"; then - echo "$code" >> "$rc_local_file" - $code + { + echo '(crontab -l | grep -Fxq "0 * * * * /data/etc/Spotmarket-Switcher/controller.sh") || (crontab -l; echo "0 * * * * /data/etc/Spotmarket-Switcher/controller.sh")' + (crontab -l | grep -Fxq "0 * * * * /data/etc/Spotmarket-Switcher/controller.sh") || ( + crontab -l + echo "0 * * * * /data/etc/Spotmarket-Switcher/controller.sh" + ) | crontab - + } | tee -a "$rc_local_file" fi # Display a success message in DEBUG mode From 4315d7441cbb2b3cd323ff2b6387736b6fb5fb98 Mon Sep 17 00:00:00 2001 From: Christian Kvasny Date: Mon, 23 Oct 2023 23:54:52 +0200 Subject: [PATCH 07/59] Update victron-venus-os-install.sh fix Venus OS failure fix crontab not found --- victron-venus-os-install.sh | 198 +++++++++++++++++++----------------- 1 file changed, 103 insertions(+), 95 deletions(-) diff --git a/victron-venus-os-install.sh b/victron-venus-os-install.sh index 0b70cc0..de4caa1 100755 --- a/victron-venus-os-install.sh +++ b/victron-venus-os-install.sh @@ -1,7 +1,7 @@ -#!/bin/sh +#!/bin/bash License=$( - cat </dev/null; then - missing="$missing $tool" - fi + if ! which "$tool" >/dev/null; then + missing="$missing $tool" + fi done if [ -n "$missing" ]; then - e_error "E: Install the following tools prior to running this install script or the installed scripts: $missing" - exit 1 + e_error "E: Install the following tools prior to running this install script or the installed scripts: $missing" + exit 1 fi for tool in wget curl; do - if ! which "$tool" >/dev/null; then - missing="$missing $tool" - fi + if ! which "$tool" >/dev/null; then + missing="$missing $tool" + fi done if [ -n "$missing" ]; then - e_note "W: Install the following tools prior to the execution of the installed scripts: $missing." - e_note " Try running 'opkg install $missing'." - echo - e_note " Now continuing with the installation, which will be fine per se, but you as the user are responsible to get those dependencies installed to prevent the control script from failing. Drop an issue at https://github.com/christian1980nrw/Spotmarket-Switcher/issues if this package shall somehow prepare you better." - echo + e_note "W: Install the following tools prior to the execution of the installed scripts: $missing." + e_note " Try running 'opkg install $missing'." + echo + e_note " Now continuing with the installation, which will be fine per se, but you as the user are responsible to get those dependencies installed to prevent the control script from failing. Drop an issue at https://github.com/christian1980nrw/Spotmarket-Switcher/issues if this package shall somehow prepare you better." + echo fi # DESTDIR is optionally set as an environment variable. if [ -n "$DESTDIR" ] && [ "/" != "$DESTDIR" ]; then - e_note "W: The environment variable DESTDIR is set to the value '$DESTDIR' that is different from '/', the root directory." - e_note " This is meant to support testing and packaging, not for a true installation." - e_underline " If you are using Victron Venus OS, the correct installation directory should be '/'." - e_note " No harm is expected to be caused, but it's recommended to install directly to '/' for a standard installation." - e_note " You can cancel now with CTRL-C if this is not what you intended." - sleep 5 - e_underline "I: Will now continue. You can still interrupt at any time." - echo + e_note "W: The environment variable DESTDIR is set to the value '$DESTDIR' that is different from '/', the root directory." + e_note " This is meant to support testing and packaging, not for a true installation." + e_underline " If you are using Victron Venus OS, the correct installation directory should be '/'." + e_note " No harm is expected to be caused, but it's recommended to install directly to '/' for a standard installation." + e_note " You can cancel now with CTRL-C if this is not what you intended." + sleep 5 + e_underline "I: Will now continue. You can still interrupt at any time." + echo else - ln -s /data/etc/Spotmarket-Switcher/service /service/Spotmarket-Switcher - (crontab -l | grep -Fxq "0 * * * * /data/etc/Spotmarket-Switcher/controller.sh") || ( - crontab -l - echo "0 * * * * /data/etc/Spotmarket-Switcher/controller.sh" - ) | crontab - + ln -s /data/etc/Spotmarket-Switcher/service /service/Spotmarket-Switcher + (crontab -l | grep -Fxq "0 * * * * /data/etc/Spotmarket-Switcher/controller.sh") || ( + crontab -l + echo "0 * * * * /data/etc/Spotmarket-Switcher/controller.sh" + ) | crontab - fi if ! mkdir -p "$DESTDIR"/data/etc/Spotmarket-Switcher/service; then - e_error "E: Could not create service directory '$DESTDIR/data/etc/Spotmarket-Switcher/service'." - exit 1 + e_error "E: Could not create service directory '$DESTDIR/data/etc/Spotmarket-Switcher/service'." + exit 1 fi downloadToDest() { - url="$1" - dest="$2" - - echo "I: Downloading '$(basename "$url")'" - if ! wget --no-verbose --continue --no-directories --show-progress -O "$dest" "$url"; then - e_error "E: Download of '$(basename "$url")' failed." - return 1 - fi - chmod +x "$dest" + url="$1" + dest="$2" + + echo "I: Downloading '$(basename "$url")'" + if ! wget --no-verbose --continue --no-directories --show-progress -O "$dest" "$url"; then + e_error "E: Download of '$(basename "$url")' failed." + return 1 + fi + chmod +x "$dest" } download_file_if_missing() { - local file_path="$1" - local dest_path="$2" - local file_url="$3" - - if [ -x "$file_path" ]; then - cp "$file_path" "$dest_path" - else - if [ -n "$DEBUG" ]; then - echo "D: ls \$SRCDIR" - ls "$SRCDIR" || { - echo "D: pwd: $(pwd)" - ls - } - fi - e_note "I: Downloading '$(basename "$file_path")' from github repository - '$BRANCH' branch" - downloadToDest "$file_url" "$dest_path" - fi + local file_path="$1" + local dest_path="$2" + local file_url="$3" + + if [ -x "$file_path" ]; then + cp "$file_path" "$dest_path" + else + if [ -n "$DEBUG" ]; then + echo "D: ls \$SRCDIR" + ls "$SRCDIR" || { + echo "D: pwd: $(pwd)" + ls + } + fi + e_note "I: Downloading '$(basename "$file_path")' from github repository - '$BRANCH' branch" + downloadToDest "$file_url" "$dest_path" + fi } if [ -z "$SRCDIR" ]; then - SRCDIR=scripts + SRCDIR=scripts fi if [ -z "$branch" ]; then - BRANCH=main + BRANCH=main fi download_file_if_missing "$SRCDIR/controller.sh" "$DESTDIR/data/etc/Spotmarket-Switcher/controller.sh" https://raw.githubusercontent.com/christian1980nrw/Spotmarket-Switcher/"$BRANCH"/scripts/controller.sh @@ -227,31 +235,31 @@ cp -n "$DESTDIR/data/etc/Spotmarket-Switcher/sample.config.txt" "$DESTDIR/data/e # $DESTDIR is always an absolut path if [ ! -d "$DESTDIR"/service ]; then - if [ -n "$DESTDIR" ] && [ "/" != "$DESTDIR" ]; then - e_note "I: The '$DESTDIR/service' directory is not existing, as expected because of the custom DESTDIR setting." - e_note " Skipping creation of symbolic link to the Sportmarket-Switcher to register this service." - else - e_note "W: The '$DESTDIR/service' directory is not existing." - e_note " Not installing a symbolic link to the Sportmarket-Switcher to register this service." - e_note " Check on https://github.com/christian1980nrw/Spotmarket-Switcher/issues if that has already been reported." - fi + if [ -n "$DESTDIR" ] && [ "/" != "$DESTDIR" ]; then + e_note "I: The '$DESTDIR/service' directory is not existing, as expected because of the custom DESTDIR setting." + e_note " Skipping creation of symbolic link to the Sportmarket-Switcher to register this service." + else + e_note "W: The '$DESTDIR/service' directory is not existing." + e_note " Not installing a symbolic link to the Sportmarket-Switcher to register this service." + e_note " Check on https://github.com/christian1980nrw/Spotmarket-Switcher/issues if that has already been reported." + fi else - if [ ! -L "$DESTDIR"/service/Spotmarket-Switcher ]; then - ln -s "$DESTDIR"/data/etc/Spotmarket-Switcher/service "$DESTDIR"/service/Spotmarket-Switcher - fi + if [ ! -L "$DESTDIR"/service/Spotmarket-Switcher ]; then + ln -s "$DESTDIR"/data/etc/Spotmarket-Switcher/service "$DESTDIR"/service/Spotmarket-Switcher + fi fi if [ -e "$DESTDIR"/data/rc.local ]; then - if grep -q "Spotmarket-Switcher/service /service/Spotmarket-Switcher" "$DESTDIR"/data/rc.local; then - e_note "I: Spotmarket-Switcher/service is already known to rc.local boot script - not added again." - else - e_note "I: Adding link to Spotmarket-Switcher/service to rc.local boot script." - sed -i '1s|^|ln -s /data/etc/Spotmarket-Switcher/service /service/Spotmarket-Switcher\n|' /data/rc.local - fi + if grep -q "Spotmarket-Switcher/service /service/Spotmarket-Switcher" "$DESTDIR"/data/rc.local; then + e_note "I: Spotmarket-Switcher/service is already known to rc.local boot script - not added again." + else + e_note "I: Adding link to Spotmarket-Switcher/service to rc.local boot script." + sed -i '1s|^|ln -s /data/etc/Spotmarket-Switcher/service /service/Spotmarket-Switcher\n|' /data/rc.local + fi else - e_note "I: Creating new data/rc.local boot script" - echo "ln -s /data/etc/Spotmarket-Switcher/service /service/Spotmarket-Switcher" >"$DESTDIR"/data/rc.local - chmod +x "$DESTDIR"/data/rc.local + e_note "I: Creating new data/rc.local boot script" + echo "ln -s /data/etc/Spotmarket-Switcher/service /service/Spotmarket-Switcher" >"$DESTDIR"/data/rc.local + chmod +x "$DESTDIR"/data/rc.local fi echo @@ -266,6 +274,6 @@ echo e_note "Note: This installation will survive a Venus OS firmware update." echo if [ -n "$missing" ]; then - e_note "Note: Remember to install these missing executables: $missing" - echo + e_note "Note: Remember to install these missing executables: $missing" + echo fi From e20cb32e7baf466dc9359502252f5654ac2bd4e8 Mon Sep 17 00:00:00 2001 From: Christian Kvasny Date: Mon, 23 Oct 2023 23:57:23 +0200 Subject: [PATCH 08/59] Update venus.yml fix --- .github/workflows/venus.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/venus.yml b/.github/workflows/venus.yml index 6f63c93..4b8aee5 100644 --- a/.github/workflows/venus.yml +++ b/.github/workflows/venus.yml @@ -19,7 +19,7 @@ jobs: if which apt > /dev/null then apt update - apt -y install wget curl + apt -y install wget curl cron elif which opkg > /dev/null then opkg install wget @@ -27,19 +27,24 @@ jobs: else echo "W: Tests limited because of non-avail of wget and curl" fi + - name: Set script permissions + run: chmod +x ./victron-venus-os-install.sh + env: + DEBUG: 1 - name: Execute Installation under Venus OS run: | echo pwd pwd echo ls ls - echo victron-venus-install.sh + echo victron-venus-os-install.sh if ./victron-venus-os-install.sh ; then echo "[OK]" else echo "[FAIL]" pwd - find . | head -n 30 + HEAD_PATH=$(which head) + find . | $HEAD_PATH -n 30 exit 1 fi env: From 9be9b544585e696d5d6ec3a4724abb0bc70b55d5 Mon Sep 17 00:00:00 2001 From: Christian Kvasny Date: Tue, 24 Oct 2023 00:01:24 +0200 Subject: [PATCH 09/59] Update victron-venus-os-install.sh fix the right code :) --- victron-venus-os-install.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/victron-venus-os-install.sh b/victron-venus-os-install.sh index de4caa1..a6aca1d 100755 --- a/victron-venus-os-install.sh +++ b/victron-venus-os-install.sh @@ -28,6 +28,7 @@ EOLICENSE ) set -e +set -x if [ -z "$LANG" ]; then export LANG=C @@ -223,7 +224,7 @@ if [ -z "$SRCDIR" ]; then SRCDIR=scripts fi if [ -z "$branch" ]; then - BRANCH=main + BRANCH=dev fi download_file_if_missing "$SRCDIR/controller.sh" "$DESTDIR/data/etc/Spotmarket-Switcher/controller.sh" https://raw.githubusercontent.com/christian1980nrw/Spotmarket-Switcher/"$BRANCH"/scripts/controller.sh From 44464e61a9c4296ddffe45ad3e60e9273adca9d0 Mon Sep 17 00:00:00 2001 From: Christian Kvasny Date: Tue, 24 Oct 2023 00:07:47 +0200 Subject: [PATCH 10/59] Create dev_versioning.yml add versioning --- .github/workflows/dev_versioning.yml | 44 ++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 .github/workflows/dev_versioning.yml diff --git a/.github/workflows/dev_versioning.yml b/.github/workflows/dev_versioning.yml new file mode 100644 index 0000000..8736e94 --- /dev/null +++ b/.github/workflows/dev_versioning.yml @@ -0,0 +1,44 @@ +name: DEV Versioning + +# This workflow triggers on every push to the 'dev' branch +on: + push: + branches: + - dev + +jobs: + update_version: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Determine size of changes + id: changes + run: | + # Get the number of lines changed since the last commit + LINES_CHANGED=$(git diff HEAD~1 --stat | tail -n 1 | awk '{print $4+$6}') + echo "LINES_CHANGED=$LINES_CHANGED" + # Check if the change is "big" (more than 100 lines in this example) + if [[ "$LINES_CHANGED" -gt 100 ]]; then + echo "::set-output name=big_change::true" + else + echo "::set-output name=big_change::false" + fi + + - name: Update VERSION + run: | + # If the change is "big", increment the minor version. Otherwise, increment the patch version. + CURRENT_VERSION=$(sed -n 's/^VERSION=\"\(.*\)-DEV\"$/\1/p' scripts/controller.sh) + if [[ "${{ steps.changes.outputs.big_change }}" == "true" ]]; then + NEW_VERSION=$(echo "$CURRENT_VERSION" | awk -F. -v OFS=. '{$2=$2+1; $3=0;print}') + else + NEW_VERSION=$(echo "$CURRENT_VERSION" | awk -F. -v OFS=. '{$3=$3+1;print}') + fi + # Append -DEV to the version number + sed -i "s/^VERSION=\".*\"$/VERSION=\"$NEW_VERSION-DEV\"/" scripts/controller.sh + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + git commit -am "Update version to $NEW_VERSION-DEV" + git push From a2f6ac6d92033941b821167a53998269c101fad8 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 23 Oct 2023 22:08:07 +0000 Subject: [PATCH 11/59] Update version to 2.3.1-DEV --- scripts/controller.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/controller.sh b/scripts/controller.sh index f887516..a88450d 100755 --- a/scripts/controller.sh +++ b/scripts/controller.sh @@ -28,7 +28,7 @@ License=$( EOLICENSE ) -VERSION="2.3.0-DEV" +VERSION="2.3.1-DEV" set -e From 81b0559dfad115fd589976df24472c8253db2ee2 Mon Sep 17 00:00:00 2001 From: Christian Kvasny Date: Tue, 24 Oct 2023 00:09:45 +0200 Subject: [PATCH 12/59] Update venus.yml fix --- .github/workflows/venus.yml | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/.github/workflows/venus.yml b/.github/workflows/venus.yml index 6f63c93..b2ff7ea 100644 --- a/.github/workflows/venus.yml +++ b/.github/workflows/venus.yml @@ -19,7 +19,7 @@ jobs: if which apt > /dev/null then apt update - apt -y install wget curl + apt -y install wget curl cron elif which opkg > /dev/null then opkg install wget @@ -27,19 +27,28 @@ jobs: else echo "W: Tests limited because of non-avail of wget and curl" fi + - name: Check crontab + run: which crontab + env: + DEBUG: 1 + - name: Set script permissions + run: chmod +x ./victron-venus-os-install.sh + env: + DEBUG: 1 - name: Execute Installation under Venus OS run: | echo pwd pwd echo ls ls - echo victron-venus-install.sh + echo victron-venus-os-install.sh if ./victron-venus-os-install.sh ; then echo "[OK]" else echo "[FAIL]" pwd - find . | head -n 30 + HEAD_PATH=$(which head) + find . | $HEAD_PATH -n 30 exit 1 fi env: From aba3e26f29c7d778508f5312a2fb08e409976c80 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 23 Oct 2023 22:14:08 +0000 Subject: [PATCH 13/59] Update version to 2.3.2-DEV --- scripts/controller.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/controller.sh b/scripts/controller.sh index a88450d..2a5c7c8 100755 --- a/scripts/controller.sh +++ b/scripts/controller.sh @@ -28,7 +28,7 @@ License=$( EOLICENSE ) -VERSION="2.3.1-DEV" +VERSION="2.3.2-DEV" set -e From fc759362bf9afcff49d5c00d78ffa3de3056d155 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 23 Oct 2023 22:14:51 +0000 Subject: [PATCH 14/59] Update version to 2.3.2 --- scripts/controller.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/controller.sh b/scripts/controller.sh index 2a5c7c8..82b6a44 100755 --- a/scripts/controller.sh +++ b/scripts/controller.sh @@ -28,7 +28,7 @@ License=$( EOLICENSE ) -VERSION="2.3.2-DEV" +VERSION="2.3.2" set -e From 407ab64394a2877f32411d9700417c80cd80d551 Mon Sep 17 00:00:00 2001 From: Christian Kvasny Date: Tue, 24 Oct 2023 00:21:33 +0200 Subject: [PATCH 15/59] remove set -x I completely forgot to remove the set -x again --- victron-venus-os-install.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/victron-venus-os-install.sh b/victron-venus-os-install.sh index a6aca1d..907e3eb 100755 --- a/victron-venus-os-install.sh +++ b/victron-venus-os-install.sh @@ -28,7 +28,6 @@ EOLICENSE ) set -e -set -x if [ -z "$LANG" ]; then export LANG=C From 6a9fe6a65d98293bce7632b594e39b3ed2075cd4 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 23 Oct 2023 22:21:44 +0000 Subject: [PATCH 16/59] Update version to 2.3.3-DEV --- scripts/controller.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/controller.sh b/scripts/controller.sh index 2a5c7c8..243de36 100755 --- a/scripts/controller.sh +++ b/scripts/controller.sh @@ -28,7 +28,7 @@ License=$( EOLICENSE ) -VERSION="2.3.2-DEV" +VERSION="2.3.3-DEV" set -e From 347db3c08e945206127ef782410c7f617e1beb85 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 23 Oct 2023 22:23:24 +0000 Subject: [PATCH 17/59] Update version to ..1-DEV --- scripts/controller.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/controller.sh b/scripts/controller.sh index 82b6a44..4d2de10 100755 --- a/scripts/controller.sh +++ b/scripts/controller.sh @@ -28,7 +28,7 @@ License=$( EOLICENSE ) -VERSION="2.3.2" +VERSION="..1-DEV" set -e From a3a42832ebcb71cb54b26e4c70f9e242ff67983b Mon Sep 17 00:00:00 2001 From: Christian Kvasny Date: Tue, 24 Oct 2023 00:24:36 +0200 Subject: [PATCH 18/59] Delete .github/workflows/dev_versioning.yml --- .github/workflows/dev_versioning.yml | 44 ---------------------------- 1 file changed, 44 deletions(-) delete mode 100644 .github/workflows/dev_versioning.yml diff --git a/.github/workflows/dev_versioning.yml b/.github/workflows/dev_versioning.yml deleted file mode 100644 index 8736e94..0000000 --- a/.github/workflows/dev_versioning.yml +++ /dev/null @@ -1,44 +0,0 @@ -name: DEV Versioning - -# This workflow triggers on every push to the 'dev' branch -on: - push: - branches: - - dev - -jobs: - update_version: - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - - name: Determine size of changes - id: changes - run: | - # Get the number of lines changed since the last commit - LINES_CHANGED=$(git diff HEAD~1 --stat | tail -n 1 | awk '{print $4+$6}') - echo "LINES_CHANGED=$LINES_CHANGED" - # Check if the change is "big" (more than 100 lines in this example) - if [[ "$LINES_CHANGED" -gt 100 ]]; then - echo "::set-output name=big_change::true" - else - echo "::set-output name=big_change::false" - fi - - - name: Update VERSION - run: | - # If the change is "big", increment the minor version. Otherwise, increment the patch version. - CURRENT_VERSION=$(sed -n 's/^VERSION=\"\(.*\)-DEV\"$/\1/p' scripts/controller.sh) - if [[ "${{ steps.changes.outputs.big_change }}" == "true" ]]; then - NEW_VERSION=$(echo "$CURRENT_VERSION" | awk -F. -v OFS=. '{$2=$2+1; $3=0;print}') - else - NEW_VERSION=$(echo "$CURRENT_VERSION" | awk -F. -v OFS=. '{$3=$3+1;print}') - fi - # Append -DEV to the version number - sed -i "s/^VERSION=\".*\"$/VERSION=\"$NEW_VERSION-DEV\"/" scripts/controller.sh - git config --local user.email "action@github.com" - git config --local user.name "GitHub Action" - git commit -am "Update version to $NEW_VERSION-DEV" - git push From 778aeabf12ebccfe0bb1ec94eb9c1f09f843ab78 Mon Sep 17 00:00:00 2001 From: Christian Kvasny Date: Tue, 24 Oct 2023 00:27:10 +0200 Subject: [PATCH 19/59] Update controller.sh Version --- scripts/controller.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/controller.sh b/scripts/controller.sh index 4d2de10..82b6a44 100755 --- a/scripts/controller.sh +++ b/scripts/controller.sh @@ -28,7 +28,7 @@ License=$( EOLICENSE ) -VERSION="..1-DEV" +VERSION="2.3.2" set -e From a974e179210af85b6ccb6814ee44327de566c400 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 23 Oct 2023 22:27:24 +0000 Subject: [PATCH 20/59] Update version to ..1-DEV --- scripts/controller.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/controller.sh b/scripts/controller.sh index 82b6a44..4d2de10 100755 --- a/scripts/controller.sh +++ b/scripts/controller.sh @@ -28,7 +28,7 @@ License=$( EOLICENSE ) -VERSION="2.3.2" +VERSION="..1-DEV" set -e From d15aecb09434e4c037ec9144326181dca471719f Mon Sep 17 00:00:00 2001 From: Christian Kvasny Date: Tue, 24 Oct 2023 00:28:44 +0200 Subject: [PATCH 21/59] Delete scripts/controller.sh --- scripts/controller.sh | 1219 ----------------------------------------- 1 file changed, 1219 deletions(-) delete mode 100755 scripts/controller.sh diff --git a/scripts/controller.sh b/scripts/controller.sh deleted file mode 100755 index 4d2de10..0000000 --- a/scripts/controller.sh +++ /dev/null @@ -1,1219 +0,0 @@ -#!/bin/bash - -License=$( - cat </dev/null; then - log_info "E: Please ensure the tool '$tool' is found." - num_tools_missing=$((num_tools_missing + 1)) - fi -done - -if [ $num_tools_missing -gt 0 ]; then - log_info "E: $num_tools_missing tools are missing." - exit 127 -fi - -unset num_tools_missing - -####################################### -### Begin of the functions... ### -####################################### - -declare -A valid_vars=( - ["use_fritz_dect_sockets"]="0|1" - ["fbox"]="^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$" - ["user"]="string" - ["passwd"]="string" - ["sockets"]='^\(\"[^"]+\"( \"[^"]+\")*\)$' - ["use_shelly_wlan_sockets"]="0|1" - ["shelly_ips"]="^\(\".*\"\)$" - ["shellyuser"]="string" - ["shellypasswd"]="string" - ["use_victron_charger"]="0|1" - ["energy_loss_percent"]="[0-9]+(\.[0-9]+)?" - ["battery_lifecycle_costs_cent_per_kwh"]="[0-9]+(\.[0-9]+)?" - ["economic_check"]="0|1|2" - ["stop_price"]="[0-9]+(\.[0-9]+)?" - ["start_price"]="[0-9]+(\.[0-9]+)?" - ["feedin_price"]="[0-9]+(\.[0-9]+)?" - ["energy_fee"]="[0-9]+(\.[0-9]+)?" - ["abort_price"]="[0-9]+(\.[0-9]+)?" - ["use_start_stop_logic"]="0|1" - ["switchablesockets_at_start_stop"]="0|1" - ["charge_at_solar_breakeven_logic"]="0|1" - ["switchablesockets_at_solar_breakeven_logic"]="0|1" - ["charge_at_lowest_price"]="0|1" - ["switchablesockets_at_lowest_price"]="0|1" - ["charge_at_second_lowest_price"]="0|1" - ["switchablesockets_at_second_lowest_price"]="0|1" - ["charge_at_third_lowest_price"]="0|1" - ["switchablesockets_at_third_lowest_price"]="0|1" - ["charge_at_fourth_lowest_price"]="0|1" - ["switchablesockets_at_fourth_lowest_price"]="0|1" - ["charge_at_fifth_lowest_price"]="0|1" - ["switchablesockets_at_fifth_lowest_price"]="0|1" - ["charge_at_sixth_lowest_price"]="0|1" - ["switchablesockets_at_sixth_lowest_price"]="0|1" - ["TZ"]="string" - ["select_pricing_api"]="1|2|3" - ["include_second_day"]="0|1" - ["use_solarweather_api_to_abort"]="0|1" - ["abort_solar_yield_today"]="[0-9]+(\.[0-9]+)?" - ["abort_solar_yield_tomorrow"]="[0-9]+(\.[0-9]+)?" - ["abort_suntime"]="[0-9]+" - ["latitude"]="[-]?[0-9]+(\.[0-9]+)?" - ["longitude"]="[-]?[0-9]+(\.[0-9]+)?" - ["visualcrossing_api_key"]="string" - ["awattar"]="de|at" - ["in_Domain"]="string" - ["out_Domain"]="string" - ["entsoe_eu_api_security_token"]="string" - ["tibber_prices"]="energy|total|tax" - ["tibber_api_key"]="string" -) - -declare -A config_values - -parse_and_validate_config() { - local file="$1" - local errors="" - - rotating_spinner & # Start the spinner in the background - local spinner_pid=$! # Get the PID of the spinner - - # Step 1: Parse - while IFS='=' read -r key value; do - # Treat everything after a "#" as a comment and remove it - key=$(echo "$key" | cut -d'#' -f1 | tr -d ' ') - value=$(echo "$value" | awk -F'#' '{gsub(/^ *"|"$|^ *| *$/, "", $1); print $1}') - - # Only process rows with key-value pairs - [[ "$key" == "" || "$value" == "" ]] && continue - - # Set the value in the associative array - config_values["$key"]="$value" - done <"$file" - - # Step 2: Validation - for var_name in "${!valid_vars[@]}"; do - local validation_pattern=${valid_vars[$var_name]} - - # Check whether the variable was set at all - if [[ -z ${config_values[$var_name]+x} ]]; then - errors+="E: $var_name is not set.\n" - continue - fi - - # Special checking for strings, IP, and arrays - if [[ "$validation_pattern" == "string" ]]; then - # Strings can be empty or filled - continue - elif [[ "$validation_pattern" == "array" && "${config_values[$var_name]}" == "" ]]; then - continue - elif [[ "$validation_pattern" == "ip" && ! "${config_values[$var_name]}" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - errors+="E: $var_name has an invalid IP address format: ${config_values[$var_name]}.\n" - continue - fi - - # Standard check against the given pattern - if ! [[ "${config_values[$var_name]}" =~ ^($validation_pattern)$ ]]; then - errors+="E: $var_name has an invalid value: ${config_values[$var_name]}.\n" - fi - done - - # Stop the spinner once the parsing is done - kill $spinner_pid &>/dev/null - - # Output errors if any were found - if [[ -n "$errors" ]]; then - echo -e "$errors" - return 1 - else - echo "Config validation passed." - return 0 - fi -} - -rotating_spinner() { - local delay=0.1 - local spinstr="|/-\\" - while true; do - local temp=${spinstr#?} - printf " [%c] Loading..." "$spinstr" - spinstr=$temp${spinstr%"$temp"} - sleep $delay - printf "\r" - done -} - -download_awattar_prices() { - local url="$1" - local file="$2" - local output_file="$3" - local sleep_time="$4" - - if [ -z "$DEBUG" ]; then - log_info "I: Please be patient. First we wait $sleep_time seconds in case the system clock is not syncronized and not to overload the API." false - sleep "$sleep_time" - fi - if ! curl "$url" >"$file"; then - log_info "E: Download of aWATTar prices from '$url' to '$file' failed." - exit_with_cleanup 1 - fi - - if ! test -f "$file"; then - log_info "E: Could not get aWATTar prices from '$url' to feed file '$file'." - exit_with_cleanup 1 - fi - - if [ -n "$DEBUG" ]; then - log_info "D: Download of file '$file' from URL '$url' successful." >&2 - fi - echo >>"$file" - awk '/data_price_hour_rel_.*_amount: / {print substr($0, index($0, ":") + 2)}' "$file" >"$output_file" - sort -g "$output_file" >"${output_file%.*}_sorted.${output_file##*.}" - timestamp=$(TZ=$TZ date +%d) - echo "date_now_day: $timestamp" >>"$output_file" - echo "date_now_day: $timestamp" >>"${output_file%.*}_sorted.${output_file##*.}" - - if [ -f "$file2" ] && [ "$(wc -l <"$file1")" = "$(wc -l <"$file2")" ]; then - rm -f "$file2" - log_info "I: File '$file2' has no tomorrow data, we have to try it again until the new prices are online." false - fi -} - -get_tibber_api() { - curl --location --request POST 'https://api.tibber.com/v1-beta/gql' \ - --header 'Content-Type: application/json' \ - --header "Authorization: Bearer $tibber_api_key" \ - --data-raw '{"query":"{viewer{homes{currentSubscription{priceInfo{current{total energy tax startsAt}today{total energy tax startsAt}tomorrow{total energy tax startsAt}}}}}}"}' | - awk '{ - gsub(/"current":/, "\n&"); - gsub(/"today":/, "\n&"); - gsub(/"tomorrow":/, "\n&"); - gsub(/"total":/, "\n&"); - print - }' -} - -download_tibber_prices() { - local url="$1" - local file="$2" - local sleep_time="$3" - - if [ -z "$DEBUG" ]; then - log_info "I: Please be patient. First we wait $sleep_time seconds in case the system clock is not syncronized and not to overload the API." false - sleep "$sleep_time" - else - log_info "D: No delay of download of Tibber data since DEBUG variable set." - fi - if ! get_tibber_api | tr -d '{}[]' >"$file"; then - log_info "E: Download of Tibber prices from '$url' to '$file' failed." - exit_with_cleanup 1 - fi - - sed -n '/"today":/,/"tomorrow":/p' "$file" | sed '$d' | sed '/"today":/d' >"$file15" - sort -t, -k1.9n $file15 >"$file16" - sed -n '/"tomorrow":/,$p' "$file" | sed '/"tomorrow":/d' >"$file17" - sort -t, -k1.9n $file17 >"$file18" - if [ "$include_second_day" = 0 ]; then - cp "$file16" "$file12" - else - grep '"total"' "$file14" | sort -t':' -k2 -n >"$file12" - fi - - timestamp=$(TZ=$TZ date +%d) - echo "date_now_day: $timestamp" >>"$file15" - echo "date_now_day: $timestamp" >>"$file17" - - if [ ! -s "$file16" ]; then - log_info "E: Tibber prices cannot be extracted to '$file16', please check your Tibber API Key." - rm "$file" - exit_with_cleanup 1 - fi -} - -download_entsoe_prices() { - local url="$1" - local file="$2" - local output_file="$3" - local sleep_time="$4" - - if [ -z "$DEBUG" ]; then - log_info "I: Please be patient. First we wait $sleep_time seconds in case the system clock is not syncronized and not to overload the API." false - sleep "$sleep_time" - else - log_info "D: No delay of download of entsoe data since DEBUG variable set." >&2 - fi - - if ! curl "$url" >"$file"; then - log_info "E: Retrieval of entsoe data from '$url' into file '$file' failed." - exit_with_cleanup 1 - fi - - if ! test -f "$file"; then - log_info "E: Could not find file '$file' with entsoe price data. Curl itself reported success." - exit_with_cleanup 1 - fi - - if [ -n "$DEBUG" ]; then log_info "D: No delay of download of entsoe data since DEBUG variable set." "D: Entsoe file '$file' with price data downloaded" >&2 >&2; fi - - if [ ! -s "$file" ]; then - log_info "E: Entsoe file '$file' is empty, please check your entsoe API Key." - exit_with_cleanup 1 - fi - - if [ -n "$DEBUG" ]; then log_info "D: No delay of download of entsoe data since DEBUG variable set." "D: Entsoe file '$file' with price data downloaded" >&2; fi - - awk ' - # Capture content inside the tag - // { - capture_period = 1 - } - /<\/Period>/ { - capture_period = 0 - } - # Ensure we are within a valid period and capture prices for one-hour resolution - capture_period && /PT60M<\/resolution>/ { - valid_period = 1 - } - valid_period && // { - gsub("", "", $0) - gsub("<\/price.amount>", "", $0) - gsub(/^[\t ]+|[\t ]+$/, "", $0) - prices = prices $0 ORS - } - valid_period && /<\/Period>/ { - exit - } - - # Capture error information inside the tag - // { - in_reason = 1 - error_message = "" - } - in_reason && // { - gsub(/|<\/code>/, "") - gsub(/^[\t ]+|[\t ]+$/, "", $0) - error_code = $0 - } - in_reason && // { - gsub(/|<\/text>/, "") - gsub(/^[\t ]+|[\t ]+$/, "", $0) - error_message = $0 - } - /<\/Reason>/ { - in_reason = 0 - } - - # At the end of processing, print out the captured prices or any error messages - END { - if (error_code == 999) { - log_info "E: Entsoe data retrieval error:", error_message - exit_with_cleanup 1 - } else if (prices != "") { - printf "%s", prices > "'"$output_file"'" - } else { - if ("'"$output_file"'" != "'"$file13"'") { - log_info "E: No prices found in the today XML data." - exit_with_cleanup 1 - } - } - log_info "E: No prices found in the tomorrow XML data." - } - ' "$file" - - if [ -f "$output_file" ]; then - sort -g "$output_file" >"${output_file%.*}_sorted.${output_file##*.}" - timestamp=$(TZ=$TZ date +%d) - echo "date_now_day: $timestamp" >>"$output_file" - fi - - # Check if tomorrow file contains next day prices - if [ "$include_second_day" = 1 ] && grep -q "PT60M" "$file" && [ "$(wc -l <"$output_file")" -gt 3 ]; then - cat $file10 >$file8 - # echo >> $file8 - if [ -f "$file13" ]; then - cat "$file13" >>"$file8" - fi - sed -i '25d 50d' "$file8" - sort -g "$file8" >"$file19" - timestamp=$(TZ=$TZ date +%d) - echo "date_now_day: $timestamp" >>"$file8" - else - cp $file11 $file19 # If no second day, copy sorted price file. - fi -} - -download_solarenergy() { - if ((use_solarweather_api_to_abort == 1)); then - delay=$((RANDOM % 15 + 1)) - if [ -z "$DEBUG" ]; then - log_info "I: Please be patient. A delay of $delay seconds will help avoid overloading the Solarweather-API." false - # Delaying a random time <=15s to reduce impact on site - download is not time-critical - sleep "$delay" - else - log_info "D: No delay of download of solarenergy data since DEBUG variable set." >&2 - fi - if ! curl "$link3" -o "$file3"; then - log_info "E: Download of solarenergy data from '$link3' failed." - exit_with_cleanup 1 - elif ! test -f "$file3"; then - log_info "E: Could not get solarenergy data, missing file '$file3'." - exit_with_cleanup 1 - fi - if [ -n "$DEBUG" ]; then - log_info "D: File3 $file3 downloaded" >&2 - fi - if ! test -f "$file3"; then - log_info "E: Could not find downloaded file '$file3' with solarenergy data." - exit_with_cleanup 1 - fi - if [ -n "$DEBUG" ]; then - log_info "D: Solarenergy data downloaded to file '$file3'." - fi - fi -} - -get_current_awattar_day() { current_awattar_day=$(sed -n 3p $file1 | grep -Eo '[0-9]+'); } -get_current_awattar_day2() { current_awattar_day2=$(sed -n 3p $file2 | grep -Eo '[0-9]+'); } - -get_awattar_prices() { - current_price=$(sed -n $((2 * $(TZ=$TZ date +%k) + 39))p $file1 | grep -Eo '[+-]?[0-9]+([.][0-9]+)?' | tail -n1) - lowest_price=$(sed -n 1p "$file7") - second_lowest_price=$(sed -n 2p "$file7") - third_lowest_price=$(sed -n 3p "$file7") - fourth_lowest_price=$(sed -n 4p "$file7") - fifth_lowest_price=$(sed -n 5p "$file7") - sixth_lowest_price=$(sed -n 6p "$file7") - # highest_price=$(awk '/^[0-9]+(\.[0-9]+)?$/ && $1 > max { max = $1 } END { print max }' "$file7") - # average_price=$(awk '/^[0-9]+(\.[0-9]+)?$/{sum+=$1; count++} END {if (count > 0) print sum/count}' "$file7") - highest_price=$(grep -E '^[0-9]+\.[0-9]+$' "$file7" | tail -n1) - average_price=$(grep -E '^[0-9]+\.[0-9]+$' "$file7" | awk '{sum+=$1; count++} END {if (count > 0) print sum/count}') -} - -get_tibber_prices() { - current_price=$(sed -n "${now_linenumber}s/.*\"${tibber_prices}\":([^,]*),.*/\1/p" "$file15") - lowest_price=$(sed -n "1s/.*\"${tibber_prices}\":([^,]*),.*/\1/p" "$file12") - second_lowest_price=$(sed -n "2s/.*\"${tibber_prices}\":([^,]*),.*/\1/p" "$file12") - third_lowest_price=$(sed -n "3s/.*\"${tibber_prices}\":([^,]*),.*/\1/p" "$file12") - fourth_lowest_price=$(sed -n "4s/.*\"${tibber_prices}\":([^,]*),.*/\1/p" "$file12") - fifth_lowest_price=$(sed -n "5s/.*\"${tibber_prices}\":([^,]*),.*/\1/p" "$file12") - sixth_lowest_price=$(sed -n "6s/.*\"${tibber_prices}\":([^,]*),.*/\1/p" "$file12") - highest_price=$(sed -n "s/.*\"${tibber_prices}\":([^,]*),.*/\1/p" "$file12" | awk 'BEGIN {max = 0} {if ($1 > max) max = $1} END {print max}') - average_price=$(sed -n "s/.*\"${tibber_prices}\":([^,]*),.*/\1/p" "$file12" | awk '{sum += $1} END {print sum/NR}') -} - -get_current_entsoe_day() { current_entsoe_day=$(sed -n 25p "$file10" | grep -Eo '[0-9]+'); } - -get_current_tibber_day() { current_tibber_day=$(sed -n 25p "$file15" | grep -Eo '[0-9]+'); } - -get_entsoe_prices() { - current_price=$(sed -n ${now_linenumber}p "$file10") - lowest_price=$(sed -n 1p "$file19") - second_lowest_price=$(sed -n 2p "$file19") - third_lowest_price=$(sed -n 3p "$file19") - fourth_lowest_price=$(sed -n 4p "$file19") - fifth_lowest_price=$(sed -n 5p "$file19") - sixth_lowest_price=$(sed -n 6p "$file19") - highest_price=$(awk 'BEGIN {max = 0} $1>max {max=$1} END {print max}' "$file19") - average_price=$(awk 'NF>0 && $1 ~ /^[0-9]*(\.[0-9]*)?$/ {sum+=$1; count++} END {if (count > 0) print sum/count}' "$file19") -} - -convert_vars_to_integer() { - local potency="$1" - shift - for var in "$@"; do - local integer_var="${var}_integer" - printf -v "$integer_var" '%s' "$(euroToMillicent "${!var}" "$potency")" - local value="${!integer_var}" # Speichern Sie den Wert in einer temporären Variable - if [ -n "$DEBUG" ]; then - log_info "D: Variable: $var | Original: ${!var} | Integer: $value | Len: ${#value}" >&2 - fi - done -} - -get_awattar_prices_integer() { - convert_vars_to_integer 15 lowest_price average_price highest_price second_lowest_price third_lowest_price fourth_lowest_price fifth_lowest_price sixth_lowest_price current_price stop_price start_price feedin_price energy_fee abort_price battery_lifecycle_costs_cent_per_kwh -} - -get_tibber_prices_integer() { - convert_vars_to_integer 17 lowest_price average_price highest_price second_lowest_price third_lowest_price fourth_lowest_price fifth_lowest_price sixth_lowest_price current_price - convert_vars_to_integer 15 stop_price start_price feedin_price energy_fee abort_price battery_lifecycle_costs_cent_per_kwh -} - -get_prices_integer_entsoe() { - convert_vars_to_integer 14 lowest_price average_price highest_price second_lowest_price third_lowest_price fourth_lowest_price fifth_lowest_price sixth_lowest_price current_price - convert_vars_to_integer 15 stop_price start_price feedin_price energy_fee abort_price battery_lifecycle_costs_cent_per_kwh -} - -get_solarenergy_today() { - solarenergy_today=$(sed '2!d' $file3 | cut -d',' -f2) - solarenergy_today_integer=$(euroToMillicent "${solarenergy_today}" 15) - abort_solar_yield_today_integer=$(euroToMillicent "${abort_solar_yield_today}" 15) -} - -get_solarenergy_tomorrow() { - solarenergy_tomorrow=$(sed '3!d' $file3 | cut -d',' -f2) - solarenergy_tomorrow_integer=$(euroToMillicent "$solarenergy_tomorrow" 15) - abort_solar_yield_tomorrow_integer=$(euroToMillicent "${abort_solar_yield_tomorrow}" 15) -} - -get_cloudcover_today() { - cloudcover_today=$(sed '2!d' $file3 | cut -d',' -f1) -} - -get_cloudcover_tomorrow() { - cloudcover_tomorrow=$(sed '3!d' $file3 | cut -d',' -f1) -} - -get_sunrise_today() { - sunrise_today=$(sed '2!d' $file3 | cut -d',' -f3 | cut -d 'T' -f2 | awk -F: '{ print $1 ":" $2 }') -} - -get_sunset_today() { - sunset_today=$(sed '2!d' $file3 | cut -d',' -f4 | cut -d 'T' -f2 | awk -F: '{ print $1 ":" $2 }') -} - -get_suntime_today() { - suntime_today=$((($(TZ=$TZ date -d "1970-01-01 $sunset_today" +%s) - $(TZ=$TZ date -d "1970-01-01 $sunrise_today" +%s)) / 60)) -} - -# Function to evaluate charging and switchablesockets conditions -evaluate_conditions() { - local -n conditions_ref="$1" - local -n descriptions_ref="$2" - local -n execute_ref="$3" - local -n condition_met_ref="$4" - local condition_met=0 # checkflag - - for condition in "${!conditions_ref[@]}"; do - if [ -n "$DEBUG" ]; then - description_value="${descriptions_ref[$condition]}" - condition_evaluation=$([ "${conditions_ref[$condition]}" -eq 1 ] && echo true || echo false) - result="($description_value) evaluates to $condition_evaluation" - log_info "D: condition_evaluation [ $result ]." >&2 - fi - - if ((conditions_ref[$condition])) && [[ $condition_met -eq 0 ]]; then - execute_ref=1 - condition_met_ref="$condition" - condition_met=1 - - if [[ $DEBUG -ne 1 ]]; then - break - fi - fi - done -} - -# Function to check economical -is_charging_economical() { - # In the Bash scripting environment, true represents a command that always ends with a success status (exit code 0), and false is a command that always ends with a failure status (exit code 1). - # In the context of comparisons or conditions in Bash: - - # A success status (e.g. a command's exit code 0) is often interpreted as "true". - # A failure status (e.g. any exit code other than 0) is often interpreted as "false". - - # In many programming languages, true represents the value 1 and false represents the value 0, but in the Bash scripting environment things are a little different as it involves the exit code of commands. - # For this reason, a value of 1 is output as false - - local reference_price="$1" - local total_cost="$2" - - local is_economical=1 - [[ $reference_price -ge $total_cost ]] && is_economical=0 - - if [ -n "$DEBUG" ]; then - log_info "D: is_charging_economical [ $is_economical - $([ "$is_economical" -eq 1 ] && echo "false" || echo "true") ]." >&2 - reference_price_euro=$(millicentToEuro $reference_price) - total_cost_euro=$(millicentToEuro $total_cost) - is_economical_str=$([ "$is_economical" -eq 1 ] && echo "false" || echo "true") - log_info "D: if [ reference_price $reference_price_euro > total_cost $total_cost_euro ] result is $is_economical_str." >&2 - fi - - return $is_economical -} - -# Function to manage charging -manage_charging() { - local action=$1 - local reason=$2 - - if [[ $action == "on" ]]; then - $charger_command_turnon >/dev/null - log_info "I: Victron scheduled charging is ON. Battery SOC is at $SOC_percent %. $reason" - else - $charger_command_turnoff >/dev/null - log_info "I: Victron scheduled charging is OFF. Battery SOC is at $SOC_percent %. $reason" - fi -} - -# Function to check abort conditions and log a message -check_abort_condition() { - local condition_result=$1 - local log_message=$2 - - if ((condition_result)); then - log_info "I: $log_message Abort." - execute_charging=0 - execute_switchablesockets_on=0 - fi -} - -# Function to manage fritz sockets and log a message -manage_fritz_sockets() { - local action=$1 - - [ "$action" != "off" ] && action=$([ "$execute_switchablesockets_on" == "1" ] && echo "on" || echo "off") - - if fritz_login; then - log_info "I: Turning $action Fritz sockets." - for socket in "${sockets[@]}"; do - [ "$socket" != "0" ] && manage_fritz_socket "$action" "$socket" - done - else - log_info "E: Fritz login failed." - fi -} - -manage_fritz_socket() { - local action=$1 - local socket=$2 - local url="http://$fbox/webservices/homeautoswitch.lua?sid=$sid&ain=$socket&switchcmd=setswitch$action" - curl -s "$url" >/dev/null || log_info "E: Could not call URL '$url' to switch $action said switch - ignored." -} - -fritz_login() { - # Get session ID (SID) - sid="" - challenge=$(curl -s "http://$fbox/login_sid.lua" | grep -o "[a-z0-9]\{8\}" | cut -d'>' -f 2) - if [ -z "$challenge" ]; then - log_info "E: Could not retrieve challenge from login_sid.lua." - return 1 - fi - - hash=$(echo -n "$challenge-$passwd" | sed -e 's,.,&\n,g' | tr '\n' '\0' | md5sum | grep -o "[0-9a-z]\{32\}") - sid=$(curl -s "http://$fbox/login_sid.lua" -d "response=$challenge-$hash" -d "username=$user" | - grep -o "[a-z0-9]\{16\}" | cut -d'>' -f 2) - - if [ "$sid" = "0000000000000000" ]; then - log_info "E: Login to Fritz!Box failed." - return 1 - fi - - if [ -n "$DEBUG" ]; then - log_info "D: Login to Fritz!Box successful." >&2 - fi - return 0 -} - -# Function to manage shelly and log a message -manage_shelly_sockets() { - local action=$1 - - [ "$action" != "off" ] && action=$([ "$execute_switchablesockets_on" == "1" ] && echo "on" || echo "off") - - log_info "I: Turning $action Shelly sockets." - for ip in "${shelly_ips[@]}"; do - [ "$ip" != "0" ] && manage_shelly_socket "$action" "$ip" - done -} - -manage_shelly_socket() { - local action=$1 - local ip=$2 - curl -s -u "$shellyuser:$shellypasswd" "http://$ip/relay/0?turn=$action" -o /dev/null || log_info "E: Could not execute switch-$action of Shelly socket with IP $ip - ignored." -} - -millicentToEuro() { - local millicents="$1" - - local EURO_FACTOR=100000000000000000 - local DECIMAL_FACTOR=10000000000000 - - local euro_main_part=$((millicents / EURO_FACTOR)) - local euro_decimal_part=$(((millicents % EURO_FACTOR) / DECIMAL_FACTOR)) - - printf "%d.%04d\n" $euro_main_part $euro_decimal_part -} - -euroToMillicent() { - euro="$1" - potency="$2" - - if [ -z "$potency" ]; then - potency=14 - fi - - #if echo "$euro" | grep -q '\,'; then - # echo "E: Could not translate '$euro' to an integer since this has a comma when only a period is accepted as decimal separator." - # return 1 - #fi - - # Replace each comma with a period, fixme if this is wrong - euro=$(echo "$euro" | sed 's/,/./g') - - # v=$(awk "BEGIN {print int($euro * (10 ^ $potency))}") - v=$(awk -v euro="$euro" -v potency="$potency" 'BEGIN {printf "%.0f", euro * (10 ^ potency)}') - - if [ -z "$v" ]; then - log_info "E: Could not translate '$euro' to an integer." - log_info "E: Called from ${FUNCNAME[1]} at line ${BASH_LINENO[0]}" - return 1 - fi - echo "$v" - return 0 -} - -euroToMillicent_test() { - log_info "I: Testing euroToMillicent" false - for i in 123456 12345.6 1234.56 123.456 12.3456 1.23456 0.123456 .123456 .233 .23 .2 2.33 2.3 2 2,33 2,3 2 23; do - echo -n "$i -> " - euroToMillicent $i - done -} - -log_info() { - local msg="$1" - local prefix=$(echo "$msg" | head -n 1 | cut -d' ' -f1) # Extract the first word from the first line - local color="\033[1m" # Default color - local writeToLog=true # Default is true - - case "$prefix" in - "E:") color="\033[1;31m" ;; # Bright Red - "D:") - color="\033[1;34m" # Bright Blue - writeToLog=false - ;; # Default to not log debug messages - "W:") color="\033[1;33m" ;; # Bright Yellow - "I:") color="\033[1;32m" ;; # Bright Green - esac - - writeToLog="${2:-$writeToLog}" # Override default if second parameter is provided - - # Print to console with color codes - printf "${color}%b\033[0m\n" "$msg" - - # If we should write to the log, write without color codes - if [ "$writeToLog" == "true" ]; then - echo -e "$msg" | sed 's/\x1b\[[0-9;]*m//g' >>"$LOG_FILE" - fi -} - -exit_with_cleanup() { - log_info "I: Cleanup and exit with error $1" - manage_charging "off" "Turn off charging." - manage_fritz_sockets "off" - manage_shelly_sockets "off" - exit $1 -} - -#################################### -### Begin of the script... ### -#################################### - -echo >>"$LOG_FILE" - -log_info "I: Bash Version: $(bash --version | head -n 1)" -log_info "I: Spotmarket-Switcher - Version $VERSION" - -parse_and_validate_config "$DIR/config.txt" -# if [ $? -eq 1 ]; then -# Handle error -# fi - -# An independent segment to test the conversion of floats to integers -if [ "tests" == "$1" ]; then - euroToMillicent_test - exit 0 -fi - -if ((select_pricing_api == 1)); then - # Test if Awattar today data exists - if test -f "$file1"; then - # Test if data is current - get_current_awattar_day - if [ "$current_awattar_day" = "$(TZ=$TZ date +%-d)" ]; then - log_info "I: aWATTar today-data is up to date." false - else - log_info "I: aWATTar today-data is outdated, fetching new data." false - rm -f $file1 $file6 $file7 - download_awattar_prices "$link1" "$file1" "$file6" $((RANDOM % 21 + 10)) - fi - else # Data file1 does not exist - log_info "I: Fetching today-data data from aWATTar." false - download_awattar_prices "$link1" "$file1" "$file6" $((RANDOM % 21 + 10)) - fi - -elif ((select_pricing_api == 2)); then - # Test if Entsoe today data exists - if test -f "$file10"; then - # Test if data is current - get_current_entsoe_day - if [ "$current_entsoe_day" = "$(TZ=$TZ date +%d)" ]; then - log_info "I: Entsoe today-data is up to date." false - else - log_info "I: Entsoe today-data is outdated, fetching new data." false - rm -f "$file4" "$file5" "$file8" "$file9" "$file10" "$file11" "$file13" "$file19" - download_entsoe_prices "$link4" "$file4" "$file10" $((RANDOM % 21 + 10)) - fi - else # Entsoe data does not exist - log_info "I: Fetching today-data data from Entsoe." false - download_entsoe_prices "$link4" "$file4" "$file10" $((RANDOM % 21 + 10)) - fi - -elif ((select_pricing_api == 3)); then - - # Test if Tibber today data exists - if test -f "$file14"; then - # Test if data is current - get_current_tibber_day - if [ "$current_tibber_day" = "$(TZ=$TZ date +%d)" ]; then - log_info "I: Tibber today-data is up to date." false - else - log_info "I: Tibber today-data is outdated, fetching new data." false - rm -f "$file14" "$file15" "$file16" - download_tibber_prices "$link6" "$file14" $((RANDOM % 21 + 10)) - fi - else # Tibber data does not exist - log_info "I: Fetching today-data data from Tibber." false - download_tibber_prices "$link6" "$file14" $((RANDOM % 21 + 10)) - fi -fi - -if ((include_second_day == 1)); then - - if ((select_pricing_api == 1)); then - - # Test if Awattar tomorrow data exists - if test -f "$file2"; then - # Test if data is current - get_current_awattar_day2 - if [ "$current_awattar_day2" = "$(TZ=$TZ date +%-d)" ]; then - log_info "I: aWATTar tomorrow-data is up to date." false - else - log_info "I: aWATTar tomorrow-data is outdated, fetching new data." false - rm -f $file3 - download_awattar_prices "$link2" "$file2" "$file6" $((RANDOM % 21 + 10)) - fi - else # Data file2 does not exist - log_info "I: aWATTar tomorrow-data does not exist, fetching data." false - download_awattar_prices "$link2" "$file2" "$file6" $((RANDOM % 21 + 10)) - fi - - elif ((select_pricing_api == 2)); then - - # Test if Entsoe tomorrow data exists - if [ ! -s "$file9" ]; then - log_info "I: File '$file9' has no tomorrow data, we have to try it again until the new prices are online." false - rm -f "$file5" "$file9" "$file13" - download_entsoe_prices "$link5" "$file5" "$file13" $((RANDOM % 21 + 10)) - fi - - elif ((select_pricing_api == 3)); then - - if [ ! -s "$file18" ]; then - rm -f "$file17" "$file18" - log_info "I: File '$file18' has no tomorrow data, we have to try it again until the new prices are online." false - rm -f "$file12" "$file14" "$file15" "$file16" "$file17" - download_tibber_prices "$link6" "$file14" $((RANDOM % 21 + 10)) - sort -t, -k1.9n $file17 >>"$file12" - fi - - fi - -fi # Include second day - -if ((select_pricing_api == 1)); then - Unit="Cent/kWh net" - get_awattar_prices - get_awattar_prices_integer -elif ((select_pricing_api == 2)); then - Unit="EUR/MWh net" - get_entsoe_prices - get_prices_integer_entsoe -elif ((select_pricing_api == 3)); then - Unit="EUR/kWh $tibber_prices price" - get_tibber_prices - get_tibber_prices_integer -fi - -if ((use_solarweather_api_to_abort == 1)); then - download_solarenergy - get_solarenergy_today - get_solarenergy_tomorrow - get_cloudcover_today - get_cloudcover_tomorrow - get_sunrise_today - get_sunset_today - get_suntime_today -fi - -log_info "I: Please verify correct system time and timezone:\n $(TZ=$TZ date)" -echo -log_info "I: Current price is $current_price $Unit." -log_info "I: Lowest price will be $lowest_price $Unit." false -log_info "I: The average price will be $average_price $Unit." false -log_info "I: Highest price will be $highest_price $Unit." false -log_info "I: Second lowest price will be $second_lowest_price $Unit." false -log_info "I: Third lowest price will be $third_lowest_price $Unit." false -log_info "I: Fourth lowest price will be $fourth_lowest_price $Unit." false -log_info "I: Fifth lowest price will be $fifth_lowest_price $Unit." false -log_info "I: Sixth lowest price will be $sixth_lowest_price $Unit." false - -if ((use_solarweather_api_to_abort == 1)); then - log_info "I: Sunrise today will be $sunrise_today and sunset will be $sunset_today. Suntime will be $suntime_today minutes." - log_info "I: Solarenergy today will be $solarenergy_today megajoule per sqaremeter with $cloudcover_today percent clouds." - log_info "I: Solarenergy tomorrow will be $solarenergy_tomorrow megajoule per squaremeter with $cloudcover_tomorrow percent clouds." - if [ ! -s $file3 ]; then - log_info "E: File '$file3' is empty, please check your API Key if download is still not possible tomorrow." - fi - find "$file3" -size 0 -delete # FIXME - looks wrong and complicated - simple RM included in prior if clause? -else - log_info "W: skip Solarweather. not activated" -fi - -charging_condition_met="" -execute_charging=0 -execute_switchablesockets_on=0 - -declare -A charging_conditions_descriptions=( - ["use_start_stop_logic"]="use_start_stop_logic ($use_start_stop_logic) == 1 && start_price_integer ($start_price_integer) > current_price_integer ($current_price_integer)" - ["charge_at_solar_breakeven_logic"]="charge_at_solar_breakeven_logic ($charge_at_solar_breakeven_logic) == 1 && feedin_price_integer ($feedin_price_integer) > current_price_integer ($current_price_integer) + energy_fee_integer ($energy_fee_integer)" - ["charge_at_lowest_price"]="charge_at_lowest_price ($charge_at_lowest_price) == 1 && lowest_price_integer ($lowest_price_integer) == current_price_integer ($current_price_integer)" - ["charge_at_second_lowest_price"]="charge_at_second_lowest_price ($charge_at_second_lowest_price) == 1 && second_lowest_price_integer ($second_lowest_price_integer) == current_price_integer ($current_price_integer)" - ["charge_at_third_lowest_price"]="charge_at_third_lowest_price ($charge_at_third_lowest_price) == 1 && third_lowest_price_integer ($third_lowest_price_integer) == current_price_integer ($current_price_integer)" - ["charge_at_fourth_lowest_price"]="charge_at_fourth_lowest_price ($charge_at_fourth_lowest_price) == 1 && fourth_lowest_price_integer ($fourth_lowest_price_integer) == current_price_integer ($current_price_integer)" - ["charge_at_fifth_lowest_price"]="charge_at_fifth_lowest_price ($charge_at_fifth_lowest_price) == 1 && fifth_lowest_price_integer ($fifth_lowest_price_integer) == current_price_integer ($current_price_integer)" - ["charge_at_sixth_lowest_price"]="charge_at_sixth_lowest_price ($charge_at_sixth_lowest_price) == 1 && sixth_lowest_price_integer ($sixth_lowest_price_integer) == current_price_integer ($current_price_integer)" -) - -declare -A charging_conditions=( - ["use_start_stop_logic"]=$((use_start_stop_logic == 1 && start_price_integer > current_price_integer)) - ["charge_at_solar_breakeven_logic"]=$((charge_at_solar_breakeven_logic == 1 && feedin_price_integer > current_price_integer + energy_fee_integer)) - ["charge_at_lowest_price"]=$((charge_at_lowest_price == 1 && lowest_price_integer == current_price_integer)) - ["charge_at_second_lowest_price"]=$((charge_at_second_lowest_price == 1 && second_lowest_price_integer == current_price_integer)) - ["charge_at_third_lowest_price"]=$((charge_at_third_lowest_price == 1 && third_lowest_price_integer == current_price_integer)) - ["charge_at_fourth_lowest_price"]=$((charge_at_fourth_lowest_price == 1 && fourth_lowest_price_integer == current_price_integer)) - ["charge_at_fifth_lowest_price"]=$((charge_at_fifth_lowest_price == 1 && fifth_lowest_price_integer == current_price_integer)) - ["charge_at_sixth_lowest_price"]=$((charge_at_sixth_lowest_price == 1 && sixth_lowest_price_integer == current_price_integer)) -) - -# Check if any charging condition is met -evaluate_conditions charging_conditions charging_conditions_descriptions execute_charging charging_condition_met - -declare -A switchablesockets_conditions_descriptions=( - ["switchablesockets_at_start_stop"]="switchablesockets_at_start_stop ($switchablesockets_at_start_stop) == 1 && start_price_integer ($start_price_integer) > current_price_integer ($current_price_integer)" - ["switchablesockets_at_solar_breakeven_logic"]="switchablesockets_at_solar_breakeven_logic ($switchablesockets_at_solar_breakeven_logic) == 1 && feedin_price_integer ($feedin_price_integer) > current_price_integer ($current_price_integer) + energy_fee_integer ($energy_fee_integer)" - ["switchablesockets_at_lowest_price"]="switchablesockets_at_lowest_price ($switchablesockets_at_lowest_price) == 1 && lowest_price_integer ($lowest_price_integer) == current_price_integer ($current_price_integer)" - ["switchablesockets_at_second_lowest_price"]="switchablesockets_at_second_lowest_price ($switchablesockets_at_second_lowest_price) == 1 && second_lowest_price_integer ($second_lowest_price_integer) == current_price_integer ($current_price_integer)" - ["switchablesockets_at_third_lowest_price"]="switchablesockets_at_third_lowest_price ($switchablesockets_at_third_lowest_price) == 1 && third_lowest_price_integer ($third_lowest_price_integer) == current_price_integer ($current_price_integer)" - ["switchablesockets_at_fourth_lowest_price"]="switchablesockets_at_fourth_lowest_price ($switchablesockets_at_fourth_lowest_price) == 1 && fourth_lowest_price_integer ($fourth_lowest_price_integer) == current_price_integer ($current_price_integer)" - ["switchablesockets_at_fifth_lowest_price"]="switchablesockets_at_fifth_lowest_price ($switchablesockets_at_fifth_lowest_price) == 1 && fifth_lowest_price_integer ($fifth_lowest_price_integer) == current_price_integer ($current_price_integer)" - ["switchablesockets_at_sixth_lowest_price"]="switchablesockets_at_sixth_lowest_price ($switchablesockets_at_sixth_lowest_price) == 1 && sixth_lowest_price_integer ($sixth_lowest_price_integer) == current_price_integer ($current_price_integer)" -) - -declare -A switchablesockets_conditions=( - ["switchablesockets_at_start_stop"]=$((switchablesockets_at_start_stop == 1 && start_price_integer > current_price_integer)) - ["switchablesockets_at_solar_breakeven_logic"]=$((switchablesockets_at_solar_breakeven_logic == 1 && feedin_price_integer > current_price_integer + energy_fee_integer)) - ["switchablesockets_at_lowest_price"]=$((switchablesockets_at_lowest_price == 1 && lowest_price_integer == current_price_integer)) - ["switchablesockets_at_second_lowest_price"]=$((switchablesockets_at_second_lowest_price == 1 && second_lowest_price_integer == current_price_integer)) - ["switchablesockets_at_third_lowest_price"]=$((switchablesockets_at_third_lowest_price == 1 && third_lowest_price_integer == current_price_integer)) - ["switchablesockets_at_fourth_lowest_price"]=$((switchablesockets_at_fourth_lowest_price == 1 && fourth_lowest_price_integer == current_price_integer)) - ["switchablesockets_at_fifth_lowest_price"]=$((switchablesockets_at_fifth_lowest_price == 1 && fifth_lowest_price_integer == current_price_integer)) - ["switchablesockets_at_sixth_lowest_price"]=$((switchablesockets_at_sixth_lowest_price == 1 && sixth_lowest_price_integer == current_price_integer)) -) - -# Check if any switching condition is met -evaluate_conditions switchablesockets_conditions switchablesockets_conditions_descriptions execute_switchablesockets_on switchablesockets_condition_met - -if ((use_solarweather_api_to_abort == 1)); then - check_abort_condition $((abort_suntime <= suntime_today)) "There are enough sun minutes today." - check_abort_condition $((abort_solar_yield_today_integer <= solarenergy_today_integer)) "There is enough solarenergy today." - check_abort_condition $((abort_solar_yield_tomorrow_integer <= solarenergy_tomorrow_integer)) "There is enough sun tomorrow." -fi - -# abort_price_integer cannot be found by shellcheck can be ignored, false positive -check_abort_condition $((abort_price_integer <= current_price_integer)) "Current price ($(millicentToEuro "$current_price_integer")€) is too high. Abort. ($(millicentToEuro "$abort_price_integer")€)" - -# If any charging condition is met, start charging -percent_of_current_price_integer=$(awk "BEGIN {print $current_price_integer*$energy_loss_percent/100}" | printf "%.0f") -total_cost_integer=$((current_price_integer + percent_of_current_price_integer + battery_lifecycle_costs_cent_per_kwh_integer)) - -if ((execute_charging == 1 && use_victron_charger == 1)); then - if [ "$economic_check" -eq 0 ]; then - manage_charging "on" "Charging based on condition met of: $charging_condition_met." - elif [ "$economic_check" -eq 1 ] && is_charging_economical $highest_price_integer $total_cost_integer; then - manage_charging "on" "Charging based on highest price ($(millicentToEuro "$highest_price_integer") €) comparison makes sense. total_cost=$(millicentToEuro "$total_cost_integer") €" - elif [ "$economic_check" -eq 2 ] && is_charging_economical $average_price_integer $total_cost_integer; then - manage_charging "on" "Charging based on average price ($(millicentToEuro "$average_price_integer") €) comparison makes sense. total_cost=$(millicentToEuro "$total_cost_integer") €" - else - reason_msg="Considering charging losses and costs, charging is too expensive." - - [ "$economic_check" -eq 1 ] && reason_msg="Charging is too expensive based on the highest price ($(millicentToEuro "$highest_price_integer") €) comparison." - [ "$economic_check" -eq 2 ] && reason_msg="Charging is too expensive based on the average price ($(millicentToEuro "$average_price_integer") €) comparison." - - manage_charging "off" "$reason_msg (total_cost=$(millicentToEuro "$total_cost_integer") €)" - fi -elif ((execute_charging != 1 && use_victron_charger == 1)); then - manage_charging "off" "Charging was not executed." -else - log_info "W: skip Victron Charger. not activated" -fi - -# Execute Fritz DECT on command -if ((use_fritz_dect_sockets == 1)); then - manage_fritz_sockets -else - log_info "W: skip Fritz DECT. not activated" -fi - -if ((use_shelly_wlan_sockets == 1)); then - manage_shelly_sockets -else - log_info "W: skip Shelly Api. not activated" -fi - -echo >>"$LOG_FILE" - -# Rotating log files -if [ -f "$LOG_FILE" ]; then - if [ "$(du -k "$LOG_FILE" | awk '{print $1}')" -gt "$LOG_MAX_SIZE" ]; then - log_info "I: Rotating log files" - mv "$LOG_FILE" "${LOG_FILE}.$(date +%Y%m%d%H%M%S)" - touch "$LOG_FILE" - find . -maxdepth 1 -name "${LOG_FILE}*" -type f -exec ls -1t {} + | - sed 's|^\./||' | - tail -n +$((LOG_FILES_TO_KEEP + 1)) | - xargs --no-run-if-empty rm - fi -fi - -if [ -n "$DEBUG" ]; then - log_info "D: \[ OK \]" >&2 -fi From f2fbfd743ec56fa04540dcd77de14e8b14b099fc Mon Sep 17 00:00:00 2001 From: Christian Kvasny Date: Tue, 24 Oct 2023 00:40:18 +0200 Subject: [PATCH 22/59] Create controller.sh deleted by mistake --- scripts/controller.sh | 1219 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1219 insertions(+) create mode 100644 scripts/controller.sh diff --git a/scripts/controller.sh b/scripts/controller.sh new file mode 100644 index 0000000..243de36 --- /dev/null +++ b/scripts/controller.sh @@ -0,0 +1,1219 @@ +#!/bin/bash + +License=$( + cat </dev/null; then + log_info "E: Please ensure the tool '$tool' is found." + num_tools_missing=$((num_tools_missing + 1)) + fi +done + +if [ $num_tools_missing -gt 0 ]; then + log_info "E: $num_tools_missing tools are missing." + exit 127 +fi + +unset num_tools_missing + +####################################### +### Begin of the functions... ### +####################################### + +declare -A valid_vars=( + ["use_fritz_dect_sockets"]="0|1" + ["fbox"]="^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$" + ["user"]="string" + ["passwd"]="string" + ["sockets"]='^\(\"[^"]+\"( \"[^"]+\")*\)$' + ["use_shelly_wlan_sockets"]="0|1" + ["shelly_ips"]="^\(\".*\"\)$" + ["shellyuser"]="string" + ["shellypasswd"]="string" + ["use_victron_charger"]="0|1" + ["energy_loss_percent"]="[0-9]+(\.[0-9]+)?" + ["battery_lifecycle_costs_cent_per_kwh"]="[0-9]+(\.[0-9]+)?" + ["economic_check"]="0|1|2" + ["stop_price"]="[0-9]+(\.[0-9]+)?" + ["start_price"]="[0-9]+(\.[0-9]+)?" + ["feedin_price"]="[0-9]+(\.[0-9]+)?" + ["energy_fee"]="[0-9]+(\.[0-9]+)?" + ["abort_price"]="[0-9]+(\.[0-9]+)?" + ["use_start_stop_logic"]="0|1" + ["switchablesockets_at_start_stop"]="0|1" + ["charge_at_solar_breakeven_logic"]="0|1" + ["switchablesockets_at_solar_breakeven_logic"]="0|1" + ["charge_at_lowest_price"]="0|1" + ["switchablesockets_at_lowest_price"]="0|1" + ["charge_at_second_lowest_price"]="0|1" + ["switchablesockets_at_second_lowest_price"]="0|1" + ["charge_at_third_lowest_price"]="0|1" + ["switchablesockets_at_third_lowest_price"]="0|1" + ["charge_at_fourth_lowest_price"]="0|1" + ["switchablesockets_at_fourth_lowest_price"]="0|1" + ["charge_at_fifth_lowest_price"]="0|1" + ["switchablesockets_at_fifth_lowest_price"]="0|1" + ["charge_at_sixth_lowest_price"]="0|1" + ["switchablesockets_at_sixth_lowest_price"]="0|1" + ["TZ"]="string" + ["select_pricing_api"]="1|2|3" + ["include_second_day"]="0|1" + ["use_solarweather_api_to_abort"]="0|1" + ["abort_solar_yield_today"]="[0-9]+(\.[0-9]+)?" + ["abort_solar_yield_tomorrow"]="[0-9]+(\.[0-9]+)?" + ["abort_suntime"]="[0-9]+" + ["latitude"]="[-]?[0-9]+(\.[0-9]+)?" + ["longitude"]="[-]?[0-9]+(\.[0-9]+)?" + ["visualcrossing_api_key"]="string" + ["awattar"]="de|at" + ["in_Domain"]="string" + ["out_Domain"]="string" + ["entsoe_eu_api_security_token"]="string" + ["tibber_prices"]="energy|total|tax" + ["tibber_api_key"]="string" +) + +declare -A config_values + +parse_and_validate_config() { + local file="$1" + local errors="" + + rotating_spinner & # Start the spinner in the background + local spinner_pid=$! # Get the PID of the spinner + + # Step 1: Parse + while IFS='=' read -r key value; do + # Treat everything after a "#" as a comment and remove it + key=$(echo "$key" | cut -d'#' -f1 | tr -d ' ') + value=$(echo "$value" | awk -F'#' '{gsub(/^ *"|"$|^ *| *$/, "", $1); print $1}') + + # Only process rows with key-value pairs + [[ "$key" == "" || "$value" == "" ]] && continue + + # Set the value in the associative array + config_values["$key"]="$value" + done <"$file" + + # Step 2: Validation + for var_name in "${!valid_vars[@]}"; do + local validation_pattern=${valid_vars[$var_name]} + + # Check whether the variable was set at all + if [[ -z ${config_values[$var_name]+x} ]]; then + errors+="E: $var_name is not set.\n" + continue + fi + + # Special checking for strings, IP, and arrays + if [[ "$validation_pattern" == "string" ]]; then + # Strings can be empty or filled + continue + elif [[ "$validation_pattern" == "array" && "${config_values[$var_name]}" == "" ]]; then + continue + elif [[ "$validation_pattern" == "ip" && ! "${config_values[$var_name]}" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + errors+="E: $var_name has an invalid IP address format: ${config_values[$var_name]}.\n" + continue + fi + + # Standard check against the given pattern + if ! [[ "${config_values[$var_name]}" =~ ^($validation_pattern)$ ]]; then + errors+="E: $var_name has an invalid value: ${config_values[$var_name]}.\n" + fi + done + + # Stop the spinner once the parsing is done + kill $spinner_pid &>/dev/null + + # Output errors if any were found + if [[ -n "$errors" ]]; then + echo -e "$errors" + return 1 + else + echo "Config validation passed." + return 0 + fi +} + +rotating_spinner() { + local delay=0.1 + local spinstr="|/-\\" + while true; do + local temp=${spinstr#?} + printf " [%c] Loading..." "$spinstr" + spinstr=$temp${spinstr%"$temp"} + sleep $delay + printf "\r" + done +} + +download_awattar_prices() { + local url="$1" + local file="$2" + local output_file="$3" + local sleep_time="$4" + + if [ -z "$DEBUG" ]; then + log_info "I: Please be patient. First we wait $sleep_time seconds in case the system clock is not syncronized and not to overload the API." false + sleep "$sleep_time" + fi + if ! curl "$url" >"$file"; then + log_info "E: Download of aWATTar prices from '$url' to '$file' failed." + exit_with_cleanup 1 + fi + + if ! test -f "$file"; then + log_info "E: Could not get aWATTar prices from '$url' to feed file '$file'." + exit_with_cleanup 1 + fi + + if [ -n "$DEBUG" ]; then + log_info "D: Download of file '$file' from URL '$url' successful." >&2 + fi + echo >>"$file" + awk '/data_price_hour_rel_.*_amount: / {print substr($0, index($0, ":") + 2)}' "$file" >"$output_file" + sort -g "$output_file" >"${output_file%.*}_sorted.${output_file##*.}" + timestamp=$(TZ=$TZ date +%d) + echo "date_now_day: $timestamp" >>"$output_file" + echo "date_now_day: $timestamp" >>"${output_file%.*}_sorted.${output_file##*.}" + + if [ -f "$file2" ] && [ "$(wc -l <"$file1")" = "$(wc -l <"$file2")" ]; then + rm -f "$file2" + log_info "I: File '$file2' has no tomorrow data, we have to try it again until the new prices are online." false + fi +} + +get_tibber_api() { + curl --location --request POST 'https://api.tibber.com/v1-beta/gql' \ + --header 'Content-Type: application/json' \ + --header "Authorization: Bearer $tibber_api_key" \ + --data-raw '{"query":"{viewer{homes{currentSubscription{priceInfo{current{total energy tax startsAt}today{total energy tax startsAt}tomorrow{total energy tax startsAt}}}}}}"}' | + awk '{ + gsub(/"current":/, "\n&"); + gsub(/"today":/, "\n&"); + gsub(/"tomorrow":/, "\n&"); + gsub(/"total":/, "\n&"); + print + }' +} + +download_tibber_prices() { + local url="$1" + local file="$2" + local sleep_time="$3" + + if [ -z "$DEBUG" ]; then + log_info "I: Please be patient. First we wait $sleep_time seconds in case the system clock is not syncronized and not to overload the API." false + sleep "$sleep_time" + else + log_info "D: No delay of download of Tibber data since DEBUG variable set." + fi + if ! get_tibber_api | tr -d '{}[]' >"$file"; then + log_info "E: Download of Tibber prices from '$url' to '$file' failed." + exit_with_cleanup 1 + fi + + sed -n '/"today":/,/"tomorrow":/p' "$file" | sed '$d' | sed '/"today":/d' >"$file15" + sort -t, -k1.9n $file15 >"$file16" + sed -n '/"tomorrow":/,$p' "$file" | sed '/"tomorrow":/d' >"$file17" + sort -t, -k1.9n $file17 >"$file18" + if [ "$include_second_day" = 0 ]; then + cp "$file16" "$file12" + else + grep '"total"' "$file14" | sort -t':' -k2 -n >"$file12" + fi + + timestamp=$(TZ=$TZ date +%d) + echo "date_now_day: $timestamp" >>"$file15" + echo "date_now_day: $timestamp" >>"$file17" + + if [ ! -s "$file16" ]; then + log_info "E: Tibber prices cannot be extracted to '$file16', please check your Tibber API Key." + rm "$file" + exit_with_cleanup 1 + fi +} + +download_entsoe_prices() { + local url="$1" + local file="$2" + local output_file="$3" + local sleep_time="$4" + + if [ -z "$DEBUG" ]; then + log_info "I: Please be patient. First we wait $sleep_time seconds in case the system clock is not syncronized and not to overload the API." false + sleep "$sleep_time" + else + log_info "D: No delay of download of entsoe data since DEBUG variable set." >&2 + fi + + if ! curl "$url" >"$file"; then + log_info "E: Retrieval of entsoe data from '$url' into file '$file' failed." + exit_with_cleanup 1 + fi + + if ! test -f "$file"; then + log_info "E: Could not find file '$file' with entsoe price data. Curl itself reported success." + exit_with_cleanup 1 + fi + + if [ -n "$DEBUG" ]; then log_info "D: No delay of download of entsoe data since DEBUG variable set." "D: Entsoe file '$file' with price data downloaded" >&2 >&2; fi + + if [ ! -s "$file" ]; then + log_info "E: Entsoe file '$file' is empty, please check your entsoe API Key." + exit_with_cleanup 1 + fi + + if [ -n "$DEBUG" ]; then log_info "D: No delay of download of entsoe data since DEBUG variable set." "D: Entsoe file '$file' with price data downloaded" >&2; fi + + awk ' + # Capture content inside the tag + // { + capture_period = 1 + } + /<\/Period>/ { + capture_period = 0 + } + # Ensure we are within a valid period and capture prices for one-hour resolution + capture_period && /PT60M<\/resolution>/ { + valid_period = 1 + } + valid_period && // { + gsub("", "", $0) + gsub("<\/price.amount>", "", $0) + gsub(/^[\t ]+|[\t ]+$/, "", $0) + prices = prices $0 ORS + } + valid_period && /<\/Period>/ { + exit + } + + # Capture error information inside the tag + // { + in_reason = 1 + error_message = "" + } + in_reason && // { + gsub(/|<\/code>/, "") + gsub(/^[\t ]+|[\t ]+$/, "", $0) + error_code = $0 + } + in_reason && // { + gsub(/|<\/text>/, "") + gsub(/^[\t ]+|[\t ]+$/, "", $0) + error_message = $0 + } + /<\/Reason>/ { + in_reason = 0 + } + + # At the end of processing, print out the captured prices or any error messages + END { + if (error_code == 999) { + log_info "E: Entsoe data retrieval error:", error_message + exit_with_cleanup 1 + } else if (prices != "") { + printf "%s", prices > "'"$output_file"'" + } else { + if ("'"$output_file"'" != "'"$file13"'") { + log_info "E: No prices found in the today XML data." + exit_with_cleanup 1 + } + } + log_info "E: No prices found in the tomorrow XML data." + } + ' "$file" + + if [ -f "$output_file" ]; then + sort -g "$output_file" >"${output_file%.*}_sorted.${output_file##*.}" + timestamp=$(TZ=$TZ date +%d) + echo "date_now_day: $timestamp" >>"$output_file" + fi + + # Check if tomorrow file contains next day prices + if [ "$include_second_day" = 1 ] && grep -q "PT60M" "$file" && [ "$(wc -l <"$output_file")" -gt 3 ]; then + cat $file10 >$file8 + # echo >> $file8 + if [ -f "$file13" ]; then + cat "$file13" >>"$file8" + fi + sed -i '25d 50d' "$file8" + sort -g "$file8" >"$file19" + timestamp=$(TZ=$TZ date +%d) + echo "date_now_day: $timestamp" >>"$file8" + else + cp $file11 $file19 # If no second day, copy sorted price file. + fi +} + +download_solarenergy() { + if ((use_solarweather_api_to_abort == 1)); then + delay=$((RANDOM % 15 + 1)) + if [ -z "$DEBUG" ]; then + log_info "I: Please be patient. A delay of $delay seconds will help avoid overloading the Solarweather-API." false + # Delaying a random time <=15s to reduce impact on site - download is not time-critical + sleep "$delay" + else + log_info "D: No delay of download of solarenergy data since DEBUG variable set." >&2 + fi + if ! curl "$link3" -o "$file3"; then + log_info "E: Download of solarenergy data from '$link3' failed." + exit_with_cleanup 1 + elif ! test -f "$file3"; then + log_info "E: Could not get solarenergy data, missing file '$file3'." + exit_with_cleanup 1 + fi + if [ -n "$DEBUG" ]; then + log_info "D: File3 $file3 downloaded" >&2 + fi + if ! test -f "$file3"; then + log_info "E: Could not find downloaded file '$file3' with solarenergy data." + exit_with_cleanup 1 + fi + if [ -n "$DEBUG" ]; then + log_info "D: Solarenergy data downloaded to file '$file3'." + fi + fi +} + +get_current_awattar_day() { current_awattar_day=$(sed -n 3p $file1 | grep -Eo '[0-9]+'); } +get_current_awattar_day2() { current_awattar_day2=$(sed -n 3p $file2 | grep -Eo '[0-9]+'); } + +get_awattar_prices() { + current_price=$(sed -n $((2 * $(TZ=$TZ date +%k) + 39))p $file1 | grep -Eo '[+-]?[0-9]+([.][0-9]+)?' | tail -n1) + lowest_price=$(sed -n 1p "$file7") + second_lowest_price=$(sed -n 2p "$file7") + third_lowest_price=$(sed -n 3p "$file7") + fourth_lowest_price=$(sed -n 4p "$file7") + fifth_lowest_price=$(sed -n 5p "$file7") + sixth_lowest_price=$(sed -n 6p "$file7") + # highest_price=$(awk '/^[0-9]+(\.[0-9]+)?$/ && $1 > max { max = $1 } END { print max }' "$file7") + # average_price=$(awk '/^[0-9]+(\.[0-9]+)?$/{sum+=$1; count++} END {if (count > 0) print sum/count}' "$file7") + highest_price=$(grep -E '^[0-9]+\.[0-9]+$' "$file7" | tail -n1) + average_price=$(grep -E '^[0-9]+\.[0-9]+$' "$file7" | awk '{sum+=$1; count++} END {if (count > 0) print sum/count}') +} + +get_tibber_prices() { + current_price=$(sed -n "${now_linenumber}s/.*\"${tibber_prices}\":([^,]*),.*/\1/p" "$file15") + lowest_price=$(sed -n "1s/.*\"${tibber_prices}\":([^,]*),.*/\1/p" "$file12") + second_lowest_price=$(sed -n "2s/.*\"${tibber_prices}\":([^,]*),.*/\1/p" "$file12") + third_lowest_price=$(sed -n "3s/.*\"${tibber_prices}\":([^,]*),.*/\1/p" "$file12") + fourth_lowest_price=$(sed -n "4s/.*\"${tibber_prices}\":([^,]*),.*/\1/p" "$file12") + fifth_lowest_price=$(sed -n "5s/.*\"${tibber_prices}\":([^,]*),.*/\1/p" "$file12") + sixth_lowest_price=$(sed -n "6s/.*\"${tibber_prices}\":([^,]*),.*/\1/p" "$file12") + highest_price=$(sed -n "s/.*\"${tibber_prices}\":([^,]*),.*/\1/p" "$file12" | awk 'BEGIN {max = 0} {if ($1 > max) max = $1} END {print max}') + average_price=$(sed -n "s/.*\"${tibber_prices}\":([^,]*),.*/\1/p" "$file12" | awk '{sum += $1} END {print sum/NR}') +} + +get_current_entsoe_day() { current_entsoe_day=$(sed -n 25p "$file10" | grep -Eo '[0-9]+'); } + +get_current_tibber_day() { current_tibber_day=$(sed -n 25p "$file15" | grep -Eo '[0-9]+'); } + +get_entsoe_prices() { + current_price=$(sed -n ${now_linenumber}p "$file10") + lowest_price=$(sed -n 1p "$file19") + second_lowest_price=$(sed -n 2p "$file19") + third_lowest_price=$(sed -n 3p "$file19") + fourth_lowest_price=$(sed -n 4p "$file19") + fifth_lowest_price=$(sed -n 5p "$file19") + sixth_lowest_price=$(sed -n 6p "$file19") + highest_price=$(awk 'BEGIN {max = 0} $1>max {max=$1} END {print max}' "$file19") + average_price=$(awk 'NF>0 && $1 ~ /^[0-9]*(\.[0-9]*)?$/ {sum+=$1; count++} END {if (count > 0) print sum/count}' "$file19") +} + +convert_vars_to_integer() { + local potency="$1" + shift + for var in "$@"; do + local integer_var="${var}_integer" + printf -v "$integer_var" '%s' "$(euroToMillicent "${!var}" "$potency")" + local value="${!integer_var}" # Speichern Sie den Wert in einer temporären Variable + if [ -n "$DEBUG" ]; then + log_info "D: Variable: $var | Original: ${!var} | Integer: $value | Len: ${#value}" >&2 + fi + done +} + +get_awattar_prices_integer() { + convert_vars_to_integer 15 lowest_price average_price highest_price second_lowest_price third_lowest_price fourth_lowest_price fifth_lowest_price sixth_lowest_price current_price stop_price start_price feedin_price energy_fee abort_price battery_lifecycle_costs_cent_per_kwh +} + +get_tibber_prices_integer() { + convert_vars_to_integer 17 lowest_price average_price highest_price second_lowest_price third_lowest_price fourth_lowest_price fifth_lowest_price sixth_lowest_price current_price + convert_vars_to_integer 15 stop_price start_price feedin_price energy_fee abort_price battery_lifecycle_costs_cent_per_kwh +} + +get_prices_integer_entsoe() { + convert_vars_to_integer 14 lowest_price average_price highest_price second_lowest_price third_lowest_price fourth_lowest_price fifth_lowest_price sixth_lowest_price current_price + convert_vars_to_integer 15 stop_price start_price feedin_price energy_fee abort_price battery_lifecycle_costs_cent_per_kwh +} + +get_solarenergy_today() { + solarenergy_today=$(sed '2!d' $file3 | cut -d',' -f2) + solarenergy_today_integer=$(euroToMillicent "${solarenergy_today}" 15) + abort_solar_yield_today_integer=$(euroToMillicent "${abort_solar_yield_today}" 15) +} + +get_solarenergy_tomorrow() { + solarenergy_tomorrow=$(sed '3!d' $file3 | cut -d',' -f2) + solarenergy_tomorrow_integer=$(euroToMillicent "$solarenergy_tomorrow" 15) + abort_solar_yield_tomorrow_integer=$(euroToMillicent "${abort_solar_yield_tomorrow}" 15) +} + +get_cloudcover_today() { + cloudcover_today=$(sed '2!d' $file3 | cut -d',' -f1) +} + +get_cloudcover_tomorrow() { + cloudcover_tomorrow=$(sed '3!d' $file3 | cut -d',' -f1) +} + +get_sunrise_today() { + sunrise_today=$(sed '2!d' $file3 | cut -d',' -f3 | cut -d 'T' -f2 | awk -F: '{ print $1 ":" $2 }') +} + +get_sunset_today() { + sunset_today=$(sed '2!d' $file3 | cut -d',' -f4 | cut -d 'T' -f2 | awk -F: '{ print $1 ":" $2 }') +} + +get_suntime_today() { + suntime_today=$((($(TZ=$TZ date -d "1970-01-01 $sunset_today" +%s) - $(TZ=$TZ date -d "1970-01-01 $sunrise_today" +%s)) / 60)) +} + +# Function to evaluate charging and switchablesockets conditions +evaluate_conditions() { + local -n conditions_ref="$1" + local -n descriptions_ref="$2" + local -n execute_ref="$3" + local -n condition_met_ref="$4" + local condition_met=0 # checkflag + + for condition in "${!conditions_ref[@]}"; do + if [ -n "$DEBUG" ]; then + description_value="${descriptions_ref[$condition]}" + condition_evaluation=$([ "${conditions_ref[$condition]}" -eq 1 ] && echo true || echo false) + result="($description_value) evaluates to $condition_evaluation" + log_info "D: condition_evaluation [ $result ]." >&2 + fi + + if ((conditions_ref[$condition])) && [[ $condition_met -eq 0 ]]; then + execute_ref=1 + condition_met_ref="$condition" + condition_met=1 + + if [[ $DEBUG -ne 1 ]]; then + break + fi + fi + done +} + +# Function to check economical +is_charging_economical() { + # In the Bash scripting environment, true represents a command that always ends with a success status (exit code 0), and false is a command that always ends with a failure status (exit code 1). + # In the context of comparisons or conditions in Bash: + + # A success status (e.g. a command's exit code 0) is often interpreted as "true". + # A failure status (e.g. any exit code other than 0) is often interpreted as "false". + + # In many programming languages, true represents the value 1 and false represents the value 0, but in the Bash scripting environment things are a little different as it involves the exit code of commands. + # For this reason, a value of 1 is output as false + + local reference_price="$1" + local total_cost="$2" + + local is_economical=1 + [[ $reference_price -ge $total_cost ]] && is_economical=0 + + if [ -n "$DEBUG" ]; then + log_info "D: is_charging_economical [ $is_economical - $([ "$is_economical" -eq 1 ] && echo "false" || echo "true") ]." >&2 + reference_price_euro=$(millicentToEuro $reference_price) + total_cost_euro=$(millicentToEuro $total_cost) + is_economical_str=$([ "$is_economical" -eq 1 ] && echo "false" || echo "true") + log_info "D: if [ reference_price $reference_price_euro > total_cost $total_cost_euro ] result is $is_economical_str." >&2 + fi + + return $is_economical +} + +# Function to manage charging +manage_charging() { + local action=$1 + local reason=$2 + + if [[ $action == "on" ]]; then + $charger_command_turnon >/dev/null + log_info "I: Victron scheduled charging is ON. Battery SOC is at $SOC_percent %. $reason" + else + $charger_command_turnoff >/dev/null + log_info "I: Victron scheduled charging is OFF. Battery SOC is at $SOC_percent %. $reason" + fi +} + +# Function to check abort conditions and log a message +check_abort_condition() { + local condition_result=$1 + local log_message=$2 + + if ((condition_result)); then + log_info "I: $log_message Abort." + execute_charging=0 + execute_switchablesockets_on=0 + fi +} + +# Function to manage fritz sockets and log a message +manage_fritz_sockets() { + local action=$1 + + [ "$action" != "off" ] && action=$([ "$execute_switchablesockets_on" == "1" ] && echo "on" || echo "off") + + if fritz_login; then + log_info "I: Turning $action Fritz sockets." + for socket in "${sockets[@]}"; do + [ "$socket" != "0" ] && manage_fritz_socket "$action" "$socket" + done + else + log_info "E: Fritz login failed." + fi +} + +manage_fritz_socket() { + local action=$1 + local socket=$2 + local url="http://$fbox/webservices/homeautoswitch.lua?sid=$sid&ain=$socket&switchcmd=setswitch$action" + curl -s "$url" >/dev/null || log_info "E: Could not call URL '$url' to switch $action said switch - ignored." +} + +fritz_login() { + # Get session ID (SID) + sid="" + challenge=$(curl -s "http://$fbox/login_sid.lua" | grep -o "[a-z0-9]\{8\}" | cut -d'>' -f 2) + if [ -z "$challenge" ]; then + log_info "E: Could not retrieve challenge from login_sid.lua." + return 1 + fi + + hash=$(echo -n "$challenge-$passwd" | sed -e 's,.,&\n,g' | tr '\n' '\0' | md5sum | grep -o "[0-9a-z]\{32\}") + sid=$(curl -s "http://$fbox/login_sid.lua" -d "response=$challenge-$hash" -d "username=$user" | + grep -o "[a-z0-9]\{16\}" | cut -d'>' -f 2) + + if [ "$sid" = "0000000000000000" ]; then + log_info "E: Login to Fritz!Box failed." + return 1 + fi + + if [ -n "$DEBUG" ]; then + log_info "D: Login to Fritz!Box successful." >&2 + fi + return 0 +} + +# Function to manage shelly and log a message +manage_shelly_sockets() { + local action=$1 + + [ "$action" != "off" ] && action=$([ "$execute_switchablesockets_on" == "1" ] && echo "on" || echo "off") + + log_info "I: Turning $action Shelly sockets." + for ip in "${shelly_ips[@]}"; do + [ "$ip" != "0" ] && manage_shelly_socket "$action" "$ip" + done +} + +manage_shelly_socket() { + local action=$1 + local ip=$2 + curl -s -u "$shellyuser:$shellypasswd" "http://$ip/relay/0?turn=$action" -o /dev/null || log_info "E: Could not execute switch-$action of Shelly socket with IP $ip - ignored." +} + +millicentToEuro() { + local millicents="$1" + + local EURO_FACTOR=100000000000000000 + local DECIMAL_FACTOR=10000000000000 + + local euro_main_part=$((millicents / EURO_FACTOR)) + local euro_decimal_part=$(((millicents % EURO_FACTOR) / DECIMAL_FACTOR)) + + printf "%d.%04d\n" $euro_main_part $euro_decimal_part +} + +euroToMillicent() { + euro="$1" + potency="$2" + + if [ -z "$potency" ]; then + potency=14 + fi + + #if echo "$euro" | grep -q '\,'; then + # echo "E: Could not translate '$euro' to an integer since this has a comma when only a period is accepted as decimal separator." + # return 1 + #fi + + # Replace each comma with a period, fixme if this is wrong + euro=$(echo "$euro" | sed 's/,/./g') + + # v=$(awk "BEGIN {print int($euro * (10 ^ $potency))}") + v=$(awk -v euro="$euro" -v potency="$potency" 'BEGIN {printf "%.0f", euro * (10 ^ potency)}') + + if [ -z "$v" ]; then + log_info "E: Could not translate '$euro' to an integer." + log_info "E: Called from ${FUNCNAME[1]} at line ${BASH_LINENO[0]}" + return 1 + fi + echo "$v" + return 0 +} + +euroToMillicent_test() { + log_info "I: Testing euroToMillicent" false + for i in 123456 12345.6 1234.56 123.456 12.3456 1.23456 0.123456 .123456 .233 .23 .2 2.33 2.3 2 2,33 2,3 2 23; do + echo -n "$i -> " + euroToMillicent $i + done +} + +log_info() { + local msg="$1" + local prefix=$(echo "$msg" | head -n 1 | cut -d' ' -f1) # Extract the first word from the first line + local color="\033[1m" # Default color + local writeToLog=true # Default is true + + case "$prefix" in + "E:") color="\033[1;31m" ;; # Bright Red + "D:") + color="\033[1;34m" # Bright Blue + writeToLog=false + ;; # Default to not log debug messages + "W:") color="\033[1;33m" ;; # Bright Yellow + "I:") color="\033[1;32m" ;; # Bright Green + esac + + writeToLog="${2:-$writeToLog}" # Override default if second parameter is provided + + # Print to console with color codes + printf "${color}%b\033[0m\n" "$msg" + + # If we should write to the log, write without color codes + if [ "$writeToLog" == "true" ]; then + echo -e "$msg" | sed 's/\x1b\[[0-9;]*m//g' >>"$LOG_FILE" + fi +} + +exit_with_cleanup() { + log_info "I: Cleanup and exit with error $1" + manage_charging "off" "Turn off charging." + manage_fritz_sockets "off" + manage_shelly_sockets "off" + exit $1 +} + +#################################### +### Begin of the script... ### +#################################### + +echo >>"$LOG_FILE" + +log_info "I: Bash Version: $(bash --version | head -n 1)" +log_info "I: Spotmarket-Switcher - Version $VERSION" + +parse_and_validate_config "$DIR/config.txt" +# if [ $? -eq 1 ]; then +# Handle error +# fi + +# An independent segment to test the conversion of floats to integers +if [ "tests" == "$1" ]; then + euroToMillicent_test + exit 0 +fi + +if ((select_pricing_api == 1)); then + # Test if Awattar today data exists + if test -f "$file1"; then + # Test if data is current + get_current_awattar_day + if [ "$current_awattar_day" = "$(TZ=$TZ date +%-d)" ]; then + log_info "I: aWATTar today-data is up to date." false + else + log_info "I: aWATTar today-data is outdated, fetching new data." false + rm -f $file1 $file6 $file7 + download_awattar_prices "$link1" "$file1" "$file6" $((RANDOM % 21 + 10)) + fi + else # Data file1 does not exist + log_info "I: Fetching today-data data from aWATTar." false + download_awattar_prices "$link1" "$file1" "$file6" $((RANDOM % 21 + 10)) + fi + +elif ((select_pricing_api == 2)); then + # Test if Entsoe today data exists + if test -f "$file10"; then + # Test if data is current + get_current_entsoe_day + if [ "$current_entsoe_day" = "$(TZ=$TZ date +%d)" ]; then + log_info "I: Entsoe today-data is up to date." false + else + log_info "I: Entsoe today-data is outdated, fetching new data." false + rm -f "$file4" "$file5" "$file8" "$file9" "$file10" "$file11" "$file13" "$file19" + download_entsoe_prices "$link4" "$file4" "$file10" $((RANDOM % 21 + 10)) + fi + else # Entsoe data does not exist + log_info "I: Fetching today-data data from Entsoe." false + download_entsoe_prices "$link4" "$file4" "$file10" $((RANDOM % 21 + 10)) + fi + +elif ((select_pricing_api == 3)); then + + # Test if Tibber today data exists + if test -f "$file14"; then + # Test if data is current + get_current_tibber_day + if [ "$current_tibber_day" = "$(TZ=$TZ date +%d)" ]; then + log_info "I: Tibber today-data is up to date." false + else + log_info "I: Tibber today-data is outdated, fetching new data." false + rm -f "$file14" "$file15" "$file16" + download_tibber_prices "$link6" "$file14" $((RANDOM % 21 + 10)) + fi + else # Tibber data does not exist + log_info "I: Fetching today-data data from Tibber." false + download_tibber_prices "$link6" "$file14" $((RANDOM % 21 + 10)) + fi +fi + +if ((include_second_day == 1)); then + + if ((select_pricing_api == 1)); then + + # Test if Awattar tomorrow data exists + if test -f "$file2"; then + # Test if data is current + get_current_awattar_day2 + if [ "$current_awattar_day2" = "$(TZ=$TZ date +%-d)" ]; then + log_info "I: aWATTar tomorrow-data is up to date." false + else + log_info "I: aWATTar tomorrow-data is outdated, fetching new data." false + rm -f $file3 + download_awattar_prices "$link2" "$file2" "$file6" $((RANDOM % 21 + 10)) + fi + else # Data file2 does not exist + log_info "I: aWATTar tomorrow-data does not exist, fetching data." false + download_awattar_prices "$link2" "$file2" "$file6" $((RANDOM % 21 + 10)) + fi + + elif ((select_pricing_api == 2)); then + + # Test if Entsoe tomorrow data exists + if [ ! -s "$file9" ]; then + log_info "I: File '$file9' has no tomorrow data, we have to try it again until the new prices are online." false + rm -f "$file5" "$file9" "$file13" + download_entsoe_prices "$link5" "$file5" "$file13" $((RANDOM % 21 + 10)) + fi + + elif ((select_pricing_api == 3)); then + + if [ ! -s "$file18" ]; then + rm -f "$file17" "$file18" + log_info "I: File '$file18' has no tomorrow data, we have to try it again until the new prices are online." false + rm -f "$file12" "$file14" "$file15" "$file16" "$file17" + download_tibber_prices "$link6" "$file14" $((RANDOM % 21 + 10)) + sort -t, -k1.9n $file17 >>"$file12" + fi + + fi + +fi # Include second day + +if ((select_pricing_api == 1)); then + Unit="Cent/kWh net" + get_awattar_prices + get_awattar_prices_integer +elif ((select_pricing_api == 2)); then + Unit="EUR/MWh net" + get_entsoe_prices + get_prices_integer_entsoe +elif ((select_pricing_api == 3)); then + Unit="EUR/kWh $tibber_prices price" + get_tibber_prices + get_tibber_prices_integer +fi + +if ((use_solarweather_api_to_abort == 1)); then + download_solarenergy + get_solarenergy_today + get_solarenergy_tomorrow + get_cloudcover_today + get_cloudcover_tomorrow + get_sunrise_today + get_sunset_today + get_suntime_today +fi + +log_info "I: Please verify correct system time and timezone:\n $(TZ=$TZ date)" +echo +log_info "I: Current price is $current_price $Unit." +log_info "I: Lowest price will be $lowest_price $Unit." false +log_info "I: The average price will be $average_price $Unit." false +log_info "I: Highest price will be $highest_price $Unit." false +log_info "I: Second lowest price will be $second_lowest_price $Unit." false +log_info "I: Third lowest price will be $third_lowest_price $Unit." false +log_info "I: Fourth lowest price will be $fourth_lowest_price $Unit." false +log_info "I: Fifth lowest price will be $fifth_lowest_price $Unit." false +log_info "I: Sixth lowest price will be $sixth_lowest_price $Unit." false + +if ((use_solarweather_api_to_abort == 1)); then + log_info "I: Sunrise today will be $sunrise_today and sunset will be $sunset_today. Suntime will be $suntime_today minutes." + log_info "I: Solarenergy today will be $solarenergy_today megajoule per sqaremeter with $cloudcover_today percent clouds." + log_info "I: Solarenergy tomorrow will be $solarenergy_tomorrow megajoule per squaremeter with $cloudcover_tomorrow percent clouds." + if [ ! -s $file3 ]; then + log_info "E: File '$file3' is empty, please check your API Key if download is still not possible tomorrow." + fi + find "$file3" -size 0 -delete # FIXME - looks wrong and complicated - simple RM included in prior if clause? +else + log_info "W: skip Solarweather. not activated" +fi + +charging_condition_met="" +execute_charging=0 +execute_switchablesockets_on=0 + +declare -A charging_conditions_descriptions=( + ["use_start_stop_logic"]="use_start_stop_logic ($use_start_stop_logic) == 1 && start_price_integer ($start_price_integer) > current_price_integer ($current_price_integer)" + ["charge_at_solar_breakeven_logic"]="charge_at_solar_breakeven_logic ($charge_at_solar_breakeven_logic) == 1 && feedin_price_integer ($feedin_price_integer) > current_price_integer ($current_price_integer) + energy_fee_integer ($energy_fee_integer)" + ["charge_at_lowest_price"]="charge_at_lowest_price ($charge_at_lowest_price) == 1 && lowest_price_integer ($lowest_price_integer) == current_price_integer ($current_price_integer)" + ["charge_at_second_lowest_price"]="charge_at_second_lowest_price ($charge_at_second_lowest_price) == 1 && second_lowest_price_integer ($second_lowest_price_integer) == current_price_integer ($current_price_integer)" + ["charge_at_third_lowest_price"]="charge_at_third_lowest_price ($charge_at_third_lowest_price) == 1 && third_lowest_price_integer ($third_lowest_price_integer) == current_price_integer ($current_price_integer)" + ["charge_at_fourth_lowest_price"]="charge_at_fourth_lowest_price ($charge_at_fourth_lowest_price) == 1 && fourth_lowest_price_integer ($fourth_lowest_price_integer) == current_price_integer ($current_price_integer)" + ["charge_at_fifth_lowest_price"]="charge_at_fifth_lowest_price ($charge_at_fifth_lowest_price) == 1 && fifth_lowest_price_integer ($fifth_lowest_price_integer) == current_price_integer ($current_price_integer)" + ["charge_at_sixth_lowest_price"]="charge_at_sixth_lowest_price ($charge_at_sixth_lowest_price) == 1 && sixth_lowest_price_integer ($sixth_lowest_price_integer) == current_price_integer ($current_price_integer)" +) + +declare -A charging_conditions=( + ["use_start_stop_logic"]=$((use_start_stop_logic == 1 && start_price_integer > current_price_integer)) + ["charge_at_solar_breakeven_logic"]=$((charge_at_solar_breakeven_logic == 1 && feedin_price_integer > current_price_integer + energy_fee_integer)) + ["charge_at_lowest_price"]=$((charge_at_lowest_price == 1 && lowest_price_integer == current_price_integer)) + ["charge_at_second_lowest_price"]=$((charge_at_second_lowest_price == 1 && second_lowest_price_integer == current_price_integer)) + ["charge_at_third_lowest_price"]=$((charge_at_third_lowest_price == 1 && third_lowest_price_integer == current_price_integer)) + ["charge_at_fourth_lowest_price"]=$((charge_at_fourth_lowest_price == 1 && fourth_lowest_price_integer == current_price_integer)) + ["charge_at_fifth_lowest_price"]=$((charge_at_fifth_lowest_price == 1 && fifth_lowest_price_integer == current_price_integer)) + ["charge_at_sixth_lowest_price"]=$((charge_at_sixth_lowest_price == 1 && sixth_lowest_price_integer == current_price_integer)) +) + +# Check if any charging condition is met +evaluate_conditions charging_conditions charging_conditions_descriptions execute_charging charging_condition_met + +declare -A switchablesockets_conditions_descriptions=( + ["switchablesockets_at_start_stop"]="switchablesockets_at_start_stop ($switchablesockets_at_start_stop) == 1 && start_price_integer ($start_price_integer) > current_price_integer ($current_price_integer)" + ["switchablesockets_at_solar_breakeven_logic"]="switchablesockets_at_solar_breakeven_logic ($switchablesockets_at_solar_breakeven_logic) == 1 && feedin_price_integer ($feedin_price_integer) > current_price_integer ($current_price_integer) + energy_fee_integer ($energy_fee_integer)" + ["switchablesockets_at_lowest_price"]="switchablesockets_at_lowest_price ($switchablesockets_at_lowest_price) == 1 && lowest_price_integer ($lowest_price_integer) == current_price_integer ($current_price_integer)" + ["switchablesockets_at_second_lowest_price"]="switchablesockets_at_second_lowest_price ($switchablesockets_at_second_lowest_price) == 1 && second_lowest_price_integer ($second_lowest_price_integer) == current_price_integer ($current_price_integer)" + ["switchablesockets_at_third_lowest_price"]="switchablesockets_at_third_lowest_price ($switchablesockets_at_third_lowest_price) == 1 && third_lowest_price_integer ($third_lowest_price_integer) == current_price_integer ($current_price_integer)" + ["switchablesockets_at_fourth_lowest_price"]="switchablesockets_at_fourth_lowest_price ($switchablesockets_at_fourth_lowest_price) == 1 && fourth_lowest_price_integer ($fourth_lowest_price_integer) == current_price_integer ($current_price_integer)" + ["switchablesockets_at_fifth_lowest_price"]="switchablesockets_at_fifth_lowest_price ($switchablesockets_at_fifth_lowest_price) == 1 && fifth_lowest_price_integer ($fifth_lowest_price_integer) == current_price_integer ($current_price_integer)" + ["switchablesockets_at_sixth_lowest_price"]="switchablesockets_at_sixth_lowest_price ($switchablesockets_at_sixth_lowest_price) == 1 && sixth_lowest_price_integer ($sixth_lowest_price_integer) == current_price_integer ($current_price_integer)" +) + +declare -A switchablesockets_conditions=( + ["switchablesockets_at_start_stop"]=$((switchablesockets_at_start_stop == 1 && start_price_integer > current_price_integer)) + ["switchablesockets_at_solar_breakeven_logic"]=$((switchablesockets_at_solar_breakeven_logic == 1 && feedin_price_integer > current_price_integer + energy_fee_integer)) + ["switchablesockets_at_lowest_price"]=$((switchablesockets_at_lowest_price == 1 && lowest_price_integer == current_price_integer)) + ["switchablesockets_at_second_lowest_price"]=$((switchablesockets_at_second_lowest_price == 1 && second_lowest_price_integer == current_price_integer)) + ["switchablesockets_at_third_lowest_price"]=$((switchablesockets_at_third_lowest_price == 1 && third_lowest_price_integer == current_price_integer)) + ["switchablesockets_at_fourth_lowest_price"]=$((switchablesockets_at_fourth_lowest_price == 1 && fourth_lowest_price_integer == current_price_integer)) + ["switchablesockets_at_fifth_lowest_price"]=$((switchablesockets_at_fifth_lowest_price == 1 && fifth_lowest_price_integer == current_price_integer)) + ["switchablesockets_at_sixth_lowest_price"]=$((switchablesockets_at_sixth_lowest_price == 1 && sixth_lowest_price_integer == current_price_integer)) +) + +# Check if any switching condition is met +evaluate_conditions switchablesockets_conditions switchablesockets_conditions_descriptions execute_switchablesockets_on switchablesockets_condition_met + +if ((use_solarweather_api_to_abort == 1)); then + check_abort_condition $((abort_suntime <= suntime_today)) "There are enough sun minutes today." + check_abort_condition $((abort_solar_yield_today_integer <= solarenergy_today_integer)) "There is enough solarenergy today." + check_abort_condition $((abort_solar_yield_tomorrow_integer <= solarenergy_tomorrow_integer)) "There is enough sun tomorrow." +fi + +# abort_price_integer cannot be found by shellcheck can be ignored, false positive +check_abort_condition $((abort_price_integer <= current_price_integer)) "Current price ($(millicentToEuro "$current_price_integer")€) is too high. Abort. ($(millicentToEuro "$abort_price_integer")€)" + +# If any charging condition is met, start charging +percent_of_current_price_integer=$(awk "BEGIN {print $current_price_integer*$energy_loss_percent/100}" | printf "%.0f") +total_cost_integer=$((current_price_integer + percent_of_current_price_integer + battery_lifecycle_costs_cent_per_kwh_integer)) + +if ((execute_charging == 1 && use_victron_charger == 1)); then + if [ "$economic_check" -eq 0 ]; then + manage_charging "on" "Charging based on condition met of: $charging_condition_met." + elif [ "$economic_check" -eq 1 ] && is_charging_economical $highest_price_integer $total_cost_integer; then + manage_charging "on" "Charging based on highest price ($(millicentToEuro "$highest_price_integer") €) comparison makes sense. total_cost=$(millicentToEuro "$total_cost_integer") €" + elif [ "$economic_check" -eq 2 ] && is_charging_economical $average_price_integer $total_cost_integer; then + manage_charging "on" "Charging based on average price ($(millicentToEuro "$average_price_integer") €) comparison makes sense. total_cost=$(millicentToEuro "$total_cost_integer") €" + else + reason_msg="Considering charging losses and costs, charging is too expensive." + + [ "$economic_check" -eq 1 ] && reason_msg="Charging is too expensive based on the highest price ($(millicentToEuro "$highest_price_integer") €) comparison." + [ "$economic_check" -eq 2 ] && reason_msg="Charging is too expensive based on the average price ($(millicentToEuro "$average_price_integer") €) comparison." + + manage_charging "off" "$reason_msg (total_cost=$(millicentToEuro "$total_cost_integer") €)" + fi +elif ((execute_charging != 1 && use_victron_charger == 1)); then + manage_charging "off" "Charging was not executed." +else + log_info "W: skip Victron Charger. not activated" +fi + +# Execute Fritz DECT on command +if ((use_fritz_dect_sockets == 1)); then + manage_fritz_sockets +else + log_info "W: skip Fritz DECT. not activated" +fi + +if ((use_shelly_wlan_sockets == 1)); then + manage_shelly_sockets +else + log_info "W: skip Shelly Api. not activated" +fi + +echo >>"$LOG_FILE" + +# Rotating log files +if [ -f "$LOG_FILE" ]; then + if [ "$(du -k "$LOG_FILE" | awk '{print $1}')" -gt "$LOG_MAX_SIZE" ]; then + log_info "I: Rotating log files" + mv "$LOG_FILE" "${LOG_FILE}.$(date +%Y%m%d%H%M%S)" + touch "$LOG_FILE" + find . -maxdepth 1 -name "${LOG_FILE}*" -type f -exec ls -1t {} + | + sed 's|^\./||' | + tail -n +$((LOG_FILES_TO_KEEP + 1)) | + xargs --no-run-if-empty rm + fi +fi + +if [ -n "$DEBUG" ]; then + log_info "D: \[ OK \]" >&2 +fi From 0e934ecf86a4927afc14a38f07500a423df8144c Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 23 Oct 2023 22:40:30 +0000 Subject: [PATCH 23/59] Update version to 2.3.4-DEV --- scripts/controller.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/controller.sh b/scripts/controller.sh index 243de36..60a1cd0 100644 --- a/scripts/controller.sh +++ b/scripts/controller.sh @@ -28,7 +28,7 @@ License=$( EOLICENSE ) -VERSION="2.3.3-DEV" +VERSION="2.3.4-DEV" set -e From 5716b13a8ece3a9e1544fc2cdcef7de9ba1a40a3 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 23 Oct 2023 22:42:59 +0000 Subject: [PATCH 24/59] Update version to 2.3.4 --- scripts/controller.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/controller.sh b/scripts/controller.sh index 60a1cd0..ebb762b 100644 --- a/scripts/controller.sh +++ b/scripts/controller.sh @@ -28,7 +28,7 @@ License=$( EOLICENSE ) -VERSION="2.3.4-DEV" +VERSION="2.3.4" set -e From b2f85cd3bdb583879e6147445680b80d31d9b132 Mon Sep 17 00:00:00 2001 From: Christian Kvasny Date: Tue, 24 Oct 2023 00:54:45 +0200 Subject: [PATCH 25/59] Update main_versioning.yml remove merged back --- .github/workflows/main_versioning.yml | 32 +++++++++------------------ 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/.github/workflows/main_versioning.yml b/.github/workflows/main_versioning.yml index 12053ce..9c6a416 100644 --- a/.github/workflows/main_versioning.yml +++ b/.github/workflows/main_versioning.yml @@ -14,25 +14,15 @@ jobs: runs-on: ubuntu-latest steps: - - name: Checkout repository - uses: actions/checkout@v3 + - name: Checkout repository + uses: actions/checkout@v3 - - name: Update VERSION in main - run: | - # Remove the -DEV suffix from the version number - NEW_VERSION=$(sed -n 's/^VERSION=\"\(.*\)-DEV\"$/\1/p' scripts/controller.sh) - sed -i "s/^VERSION=\".*\"$/VERSION=\"${NEW_VERSION}\"/" scripts/controller.sh - git config --local user.email "action@github.com" - git config --local user.name "GitHub Action" - git commit -am "Update version to ${NEW_VERSION}" - git push origin main - - - name: Merge changes back to dev and append -DEV - run: | - git checkout dev - git merge main --no-ff -m "Merge main changes back to dev" - # Append -DEV back to the version number for the dev branch - NEW_DEV_VERSION="${NEW_VERSION}-DEV" - sed -i "s/^VERSION=\".*\"$/VERSION=\"${NEW_DEV_VERSION}\"/" scripts/controller.sh - git commit -am "Update version to ${NEW_DEV_VERSION} in dev" - git push origin dev + - name: Update VERSION in main + run: | + # Remove the -DEV suffix from the version number + NEW_VERSION=$(sed -n 's/^VERSION=\"\(.*\)-DEV\"$/\1/p' scripts/controller.sh) + sed -i "s/^VERSION=\".*\"$/VERSION=\"${NEW_VERSION}\"/" scripts/controller.sh + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + git commit -am "Update version to ${NEW_VERSION}" + git push origin main From 7be9035de72846ce1b39cc7e1fe19ee7c0615e16 Mon Sep 17 00:00:00 2001 From: Christian Kvasny Date: Tue, 24 Oct 2023 01:26:09 +0200 Subject: [PATCH 26/59] Update run fix rc.local env --- scripts/run | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/run b/scripts/run index 23cb308..696cf44 100755 --- a/scripts/run +++ b/scripts/run @@ -37,8 +37,10 @@ if [ -z "$DEBUG" ]; then sleep 25 fi -# Define the rc.local file path -rc_local_file="/data/rc.local" +if [ -z "$rc_local_file" ]; then + # Define the rc.local file path + rc_local_file="/data/rc.local" +fi # Check if rc.local file exists, if not, create it if [ ! -e "$rc_local_file" ]; then From 1630731affbcca8f7851f2537070780f08a8a9e5 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 23 Oct 2023 23:26:23 +0000 Subject: [PATCH 27/59] Update version to 2.3.5-DEV --- scripts/controller.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/controller.sh b/scripts/controller.sh index 60a1cd0..dea262d 100644 --- a/scripts/controller.sh +++ b/scripts/controller.sh @@ -28,7 +28,7 @@ License=$( EOLICENSE ) -VERSION="2.3.4-DEV" +VERSION="2.3.5-DEV" set -e From 042eb127e01e6219ddc5adbeb40a6273ec8c6c88 Mon Sep 17 00:00:00 2001 From: Christian Kvasny Date: Tue, 24 Oct 2023 01:27:54 +0200 Subject: [PATCH 28/59] Update victron-venus-os-install.sh fix BRANCH --- victron-venus-os-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/victron-venus-os-install.sh b/victron-venus-os-install.sh index 907e3eb..de4caa1 100755 --- a/victron-venus-os-install.sh +++ b/victron-venus-os-install.sh @@ -223,7 +223,7 @@ if [ -z "$SRCDIR" ]; then SRCDIR=scripts fi if [ -z "$branch" ]; then - BRANCH=dev + BRANCH=main fi download_file_if_missing "$SRCDIR/controller.sh" "$DESTDIR/data/etc/Spotmarket-Switcher/controller.sh" https://raw.githubusercontent.com/christian1980nrw/Spotmarket-Switcher/"$BRANCH"/scripts/controller.sh From 74f946437d3a07eb1c83d80af3aef44dcb32221f Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 23 Oct 2023 23:28:05 +0000 Subject: [PATCH 29/59] Update version to 2.3.6-DEV --- scripts/controller.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/controller.sh b/scripts/controller.sh index dea262d..e75396e 100644 --- a/scripts/controller.sh +++ b/scripts/controller.sh @@ -28,7 +28,7 @@ License=$( EOLICENSE ) -VERSION="2.3.5-DEV" +VERSION="2.3.6-DEV" set -e From 56e4c891b46c80e78244846f88a25c9b0d42f865 Mon Sep 17 00:00:00 2001 From: Christian Kvasny Date: Tue, 24 Oct 2023 01:37:56 +0200 Subject: [PATCH 30/59] Update controller.sh fix log_info not found macos --- scripts/controller.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/controller.sh b/scripts/controller.sh index e75396e..d015039 100644 --- a/scripts/controller.sh +++ b/scripts/controller.sh @@ -142,7 +142,7 @@ if [ -z "$UNAME" ]; then UNAME=$(uname) fi if [ "Darwin" = "$UNAME" ]; then - log_info "W: MacOS has a different implementation of 'date' - use conda if hunting a bug on a mac". + echo "W: MacOS has a different implementation of 'date' - use conda if hunting a bug on a mac". fi # further API parameters (no need to edit) From 2b4c8279650d9e67b22d8666af94f65be69bfdb3 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 23 Oct 2023 23:38:06 +0000 Subject: [PATCH 31/59] Update version to 2.3.7-DEV --- scripts/controller.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/controller.sh b/scripts/controller.sh index d015039..cd80cfc 100644 --- a/scripts/controller.sh +++ b/scripts/controller.sh @@ -28,7 +28,7 @@ License=$( EOLICENSE ) -VERSION="2.3.6-DEV" +VERSION="2.3.7-DEV" set -e From 17d0c5f208e22c5c6db2abf42628e86dcc5884d5 Mon Sep 17 00:00:00 2001 From: Christian Kvasny Date: Tue, 24 Oct 2023 01:55:36 +0200 Subject: [PATCH 32/59] Fix main.yml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix ubuntu run I have no idea how I can fix the error “declare: -A: invalid option” on macOS --- .github/workflows/main.yml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 767e42c..90ed4d0 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -76,18 +76,20 @@ jobs: else echo "I: Nothing to install for ${{ matrix.os }}" fi + - name: Set script permissions + run: chmod +x ./victron-venus-os-install.sh - name: Install run: ./victron-venus-os-install.sh env: DEBUG: 1 DESTDIR: /tmp/testing - - name: Control - run: scripts/controller.sh + - name: Execute Run-Script + run: /tmp/testing//data/etc/Spotmarket-Switcher/service/run env: DEBUG: 1 - LOG_FILE: /tmp/testing_controller.log - - name: Run - run: scripts/run + rc_local_file: /tmp/testing_rc.local + - name: Execute Control-Script + run: /tmp/testing//data/etc/Spotmarket-Switcher/controller.sh env: DEBUG: 1 - rc_local_file: /tmp/testing_rc.local + LOG_FILE: /tmp/testing_controller.log From e6bfe9179c9d55fa813076adce671a089fb37c23 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 23 Oct 2023 23:56:32 +0000 Subject: [PATCH 33/59] Update version to --- scripts/controller.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/controller.sh b/scripts/controller.sh index d489683..2f7cc42 100644 --- a/scripts/controller.sh +++ b/scripts/controller.sh @@ -28,7 +28,7 @@ License=$( EOLICENSE ) -VERSION="2.3.7" +VERSION="" set -e From 004a5746d51da6e0e4c52ac9b00bcfed3c40adb6 Mon Sep 17 00:00:00 2001 From: Christian Kvasny Date: Tue, 24 Oct 2023 02:00:17 +0200 Subject: [PATCH 34/59] Update controller.sh version (auto update doesn't seem to work) --- scripts/controller.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/controller.sh b/scripts/controller.sh index 2f7cc42..d489683 100644 --- a/scripts/controller.sh +++ b/scripts/controller.sh @@ -28,7 +28,7 @@ License=$( EOLICENSE ) -VERSION="" +VERSION="2.3.7" set -e From e47286868601cde36bb0fb7d24db8d98cb62152a Mon Sep 17 00:00:00 2001 From: Christian Kvasny Date: Tue, 24 Oct 2023 02:22:11 +0200 Subject: [PATCH 35/59] Fix Division by zero Fix Division by zero --- scripts/controller.sh | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/scripts/controller.sh b/scripts/controller.sh index d489683..2fe0b7d 100644 --- a/scripts/controller.sh +++ b/scripts/controller.sh @@ -617,15 +617,15 @@ get_awattar_prices() { } get_tibber_prices() { - current_price=$(sed -n "${now_linenumber}s/.*\"${tibber_prices}\":([^,]*),.*/\1/p" "$file15") - lowest_price=$(sed -n "1s/.*\"${tibber_prices}\":([^,]*),.*/\1/p" "$file12") - second_lowest_price=$(sed -n "2s/.*\"${tibber_prices}\":([^,]*),.*/\1/p" "$file12") - third_lowest_price=$(sed -n "3s/.*\"${tibber_prices}\":([^,]*),.*/\1/p" "$file12") - fourth_lowest_price=$(sed -n "4s/.*\"${tibber_prices}\":([^,]*),.*/\1/p" "$file12") - fifth_lowest_price=$(sed -n "5s/.*\"${tibber_prices}\":([^,]*),.*/\1/p" "$file12") - sixth_lowest_price=$(sed -n "6s/.*\"${tibber_prices}\":([^,]*),.*/\1/p" "$file12") - highest_price=$(sed -n "s/.*\"${tibber_prices}\":([^,]*),.*/\1/p" "$file12" | awk 'BEGIN {max = 0} {if ($1 > max) max = $1} END {print max}') - average_price=$(sed -n "s/.*\"${tibber_prices}\":([^,]*),.*/\1/p" "$file12" | awk '{sum += $1} END {print sum/NR}') + current_price=$(sed -n "${now_linenumber}s/.*\"${tibber_prices}\":\([^,]*\),.*/\1/p" "$file15") + lowest_price=$(sed -n "1s/.*\"${tibber_prices}\":\([^,]*\),.*/\1/p" "$file12") + second_lowest_price=$(sed -n "2s/.*\"${tibber_prices}\":\([^,]*\),.*/\1/p" "$file12") + third_lowest_price=$(sed -n "3s/.*\"${tibber_prices}\":\([^,]*\),.*/\1/p" "$file12") + fourth_lowest_price=$(sed -n "4s/.*\"${tibber_prices}\":\([^,]*\),.*/\1/p" "$file12") + fifth_lowest_price=$(sed -n "5s/.*\"${tibber_prices}\":\([^,]*\),.*/\1/p" "$file12") + sixth_lowest_price=$(sed -n "6s/.*\"${tibber_prices}\":\([^,]*\),.*/\1/p" "$file12") + highest_price=$(sed -n "s/.*\"${tibber_prices}\":\([^,]*\),.*/\1/p" "$file12" | awk 'BEGIN {max = 0} {if ($1 > max) max = $1} END {print max}') + average_price=$(sed -n "s/.*\"${tibber_prices}\":\([^,]*\),.*/\1/p" "$file12" | awk '{sum += $1} END {print sum/NR}') } get_current_entsoe_day() { current_entsoe_day=$(sed -n 25p "$file10" | grep -Eo '[0-9]+'); } From 4bd831e242053996dd8a7db875d2dc8bddb9afce Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 24 Oct 2023 00:23:24 +0000 Subject: [PATCH 36/59] Update version to --- scripts/controller.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/controller.sh b/scripts/controller.sh index 2fe0b7d..5f5949b 100644 --- a/scripts/controller.sh +++ b/scripts/controller.sh @@ -28,7 +28,7 @@ License=$( EOLICENSE ) -VERSION="2.3.7" +VERSION="" set -e From f39722a60c3de9fe41dbe0e8b6951e402ab4cd1a Mon Sep 17 00:00:00 2001 From: Christian Kvasny Date: Tue, 24 Oct 2023 02:28:44 +0200 Subject: [PATCH 37/59] Update controller.sh --- scripts/controller.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/controller.sh b/scripts/controller.sh index 5f5949b..2fe0b7d 100644 --- a/scripts/controller.sh +++ b/scripts/controller.sh @@ -28,7 +28,7 @@ License=$( EOLICENSE ) -VERSION="" +VERSION="2.3.7" set -e From b45f36a2937d246b49d3597abf3da3588f198a12 Mon Sep 17 00:00:00 2001 From: Christian Kvasny Date: Tue, 24 Oct 2023 02:37:16 +0200 Subject: [PATCH 38/59] Update main_versioning.yml Fix empty VERSION --- .github/workflows/main_versioning.yml | 30 +++++++++++++++++---------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/.github/workflows/main_versioning.yml b/.github/workflows/main_versioning.yml index 9c6a416..96cb6f3 100644 --- a/.github/workflows/main_versioning.yml +++ b/.github/workflows/main_versioning.yml @@ -14,15 +14,23 @@ jobs: runs-on: ubuntu-latest steps: - - name: Checkout repository - uses: actions/checkout@v3 + - name: Checkout repository + uses: actions/checkout@v3 - - name: Update VERSION in main - run: | - # Remove the -DEV suffix from the version number - NEW_VERSION=$(sed -n 's/^VERSION=\"\(.*\)-DEV\"$/\1/p' scripts/controller.sh) - sed -i "s/^VERSION=\".*\"$/VERSION=\"${NEW_VERSION}\"/" scripts/controller.sh - git config --local user.email "action@github.com" - git config --local user.name "GitHub Action" - git commit -am "Update version to ${NEW_VERSION}" - git push origin main + - name: Update VERSION in main + run: | + # Attempt to remove the -DEV suffix from the version number + NEW_VERSION=$(sed -n 's/^VERSION=\"\(.*\)-DEV\"$/\1/p' scripts/controller.sh) + + # If NEW_VERSION is empty (meaning there was no -DEV), take the current value of VERSION + if [[ -z $NEW_VERSION ]]; then + NEW_VERSION=$(sed -n 's/^VERSION=\"\(.*\)\"$/\1/p' scripts/controller.sh) + fi + + # Replace VERSION in the file with the NEW_VERSION value + sed -i "s/^VERSION=\".*\"$/VERSION=\"${NEW_VERSION}\"/" scripts/controller.sh + + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + git commit -am "Update version to ${NEW_VERSION}" + git push origin main From 33fac2b0468325677005d731fb14c1c682573449 Mon Sep 17 00:00:00 2001 From: Christian Kvasny Date: Tue, 24 Oct 2023 19:05:02 +0200 Subject: [PATCH 39/59] Update main_versioning.yml Fix spaces --- .github/workflows/main_versioning.yml | 38 +++++++++++++-------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/.github/workflows/main_versioning.yml b/.github/workflows/main_versioning.yml index 96cb6f3..b380174 100644 --- a/.github/workflows/main_versioning.yml +++ b/.github/workflows/main_versioning.yml @@ -14,23 +14,23 @@ jobs: runs-on: ubuntu-latest steps: - - name: Checkout repository - uses: actions/checkout@v3 + - name: Checkout repository + uses: actions/checkout@v3 - - name: Update VERSION in main - run: | - # Attempt to remove the -DEV suffix from the version number - NEW_VERSION=$(sed -n 's/^VERSION=\"\(.*\)-DEV\"$/\1/p' scripts/controller.sh) - - # If NEW_VERSION is empty (meaning there was no -DEV), take the current value of VERSION - if [[ -z $NEW_VERSION ]]; then - NEW_VERSION=$(sed -n 's/^VERSION=\"\(.*\)\"$/\1/p' scripts/controller.sh) - fi - - # Replace VERSION in the file with the NEW_VERSION value - sed -i "s/^VERSION=\".*\"$/VERSION=\"${NEW_VERSION}\"/" scripts/controller.sh - - git config --local user.email "action@github.com" - git config --local user.name "GitHub Action" - git commit -am "Update version to ${NEW_VERSION}" - git push origin main + - name: Update VERSION in main + run: | + # Attempt to remove the -DEV suffix from the version number + NEW_VERSION=$(sed -n 's/^VERSION=\"\(.*\)-DEV\"$/\1/p' scripts/controller.sh) + + # If NEW_VERSION is empty (meaning there was no -DEV), take the current value of VERSION + if [[ -z $NEW_VERSION ]]; then + NEW_VERSION=$(sed -n 's/^VERSION=\"\(.*\)\"$/\1/p' scripts/controller.sh) + fi + + # Replace VERSION in the file with the NEW_VERSION value + sed -i "s/^VERSION=\".*\"$/VERSION=\"${NEW_VERSION}\"/" scripts/controller.sh + + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + git commit -am "Update version to ${NEW_VERSION}" + git push origin main From 3c7fb00466e0fbcc226bcd1b9637baa925a86362 Mon Sep 17 00:00:00 2001 From: Christian Kvasny Date: Tue, 24 Oct 2023 19:15:26 +0200 Subject: [PATCH 40/59] Update controller.sh Functions have been moved up to ensure that all functions for the script flow are present --- scripts/controller.sh | 226 +++++++++++++++++++++--------------------- 1 file changed, 114 insertions(+), 112 deletions(-) diff --git a/scripts/controller.sh b/scripts/controller.sh index 2fe0b7d..986ca76 100644 --- a/scripts/controller.sh +++ b/scripts/controller.sh @@ -127,118 +127,6 @@ fi # Note: This script is only for hourly-based tariff data, please create your own fork for higher resolutions like 15 minute intervals. # After an API reconfiguration please delete the old API-Downloadfiles with rm /tmp/awattar*.* /tmp/entsoe*.* -# Path to the current script directory -DIR="$(dirname "$0")" - -if [ -f "$DIR/config.txt" ]; then - # Include the configuration file - source "$DIR/config.txt" -else - echo "E: The file $DIR/config.txt was not found! Configure the existing sample.config.txt file and then save it as config.txt in the same directory." false - exit 127 -fi - -if [ -z "$UNAME" ]; then - UNAME=$(uname) -fi -if [ "Darwin" = "$UNAME" ]; then - echo "W: MacOS has a different implementation of 'date' - use conda if hunting a bug on a mac". -fi - -# further API parameters (no need to edit) -dateInSeconds=$(LC_ALL=C TZ=$TZ date +"%s") -if [ "Darwin" = "$UNAME" ]; then - yesterday=$(LC_ALL=C TZ=$TZ date -j -f "%s" $((dateInSeconds - 86400)) +%d)2300 - yestermonth=$(LC_ALL=C TZ=$TZ date -j -f "%s" $((dateInSeconds - 86400)) +%m) - yesteryear=$(LC_ALL=C TZ=$TZ date -j -f "%s" $((dateInSeconds - 86400)) +%Y) - today=$(LC_ALL=C TZ=$TZ date -j -f "%s" $((dateInSeconds)) +%d)2300 - today2=$(LC_ALL=C TZ=$TZ date -j -f "%s" $((dateInSeconds)) +%d) - todaymonth=$(LC_ALL=C TZ=$TZ date -j -f "%s" $((dateInSeconds)) +%m) - todayyear=$(LC_ALL=C TZ=$TZ date -j -f "%s" $((dateInSeconds)) +%Y) - tomorrow=$(LC_ALL=C TZ=$TZ date -j -f "%s" $((dateInSeconds + 86400)) +%d)2300 - tomorrow2=$(LC_ALL=C TZ=$TZ date -j -f "%s" $((dateInSeconds + 86400)) +%d) - tomorrowmonth=$(LC_ALL=C TZ=$TZ date -j -f "%s" $((dateInSeconds + 86400)) +%m) - tomorrowyear=$(LC_ALL=C TZ=$TZ date -j -f "%s" $((dateInSeconds + 86400)) +%Y) - getnow=$(LC_ALL=C TZ=$TZ date -j -f "%s" $((dateInSeconds)) +%k) -else - yesterday=$(LC_ALL=C TZ=$TZ date -d @$((dateInSeconds - 86400)) +%d)2300 - yestermonth=$(LC_ALL=C TZ=$TZ date -d @$((dateInSeconds - 86400)) +%m) - yesteryear=$(LC_ALL=C TZ=$TZ date -d @$((dateInSeconds - 86400)) +%Y) - today=$(LC_ALL=C TZ=$TZ date -d @$((dateInSeconds)) +%d)2300 - today2=$(LC_ALL=C TZ=$TZ date -d @$((dateInSeconds)) +%d) - todaymonth=$(LC_ALL=C TZ=$TZ date -d @$((dateInSeconds)) +%m) - todayyear=$(LC_ALL=C TZ=$TZ date -d @$((dateInSeconds)) +%Y) - tomorrow=$(LC_ALL=C TZ=$TZ date -d @$((dateInSeconds + 86400)) +%d)2300 - tomorrow2=$(LC_ALL=C TZ=$TZ date -d @$((dateInSeconds + 86400)) +%d) - tomorrowmonth=$(LC_ALL=C TZ=$TZ date -d @$((dateInSeconds + 86400)) +%m) - tomorrowyear=$(LC_ALL=C TZ=$TZ date -d @$((dateInSeconds + 86400)) +%Y) - getnow=$(LC_ALL=C TZ=$TZ date -d @$((dateInSeconds)) +%k) -fi - -now_linenumber=$((getnow + 1)) -link1="https://api.awattar.$awattar/v1/marketdata/current.yaml" -link2="http://api.awattar.$awattar/v1/marketdata/current.yaml?tomorrow=include" -link3="https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline/$latitude%2C%20$longitude/$todayyear-$todaymonth-$today2/$tomorrowyear-$tomorrowmonth-$tomorrow2?unitGroup=metric&elements=solarenergy%2Ccloudcover%2Csunrise%2Csunset&include=days&key=$visualcrossing_api_key&contentType=csv" -link4="https://web-api.tp.entsoe.eu/api?securityToken=$entsoe_eu_api_security_token&documentType=A44&in_Domain=$in_Domain&out_Domain=$out_Domain&periodStart=$yesteryear$yestermonth$yesterday&periodEnd=$todayyear$todaymonth$today" -link5="https://web-api.tp.entsoe.eu/api?securityToken=$entsoe_eu_api_security_token&documentType=A44&in_Domain=$in_Domain&out_Domain=$out_Domain&periodStart=$todayyear$todaymonth$today&periodEnd=$tomorrowyear$tomorrowmonth$tomorrow" -link6="https://api.tibber.com/v1-beta/gql" -file1=/tmp/awattar_today_prices.yaml -file2=/tmp/awattar_tomorrow_prices.yaml -file3=/tmp/expected_solarenergy.csv -file4=/tmp/entsoe_today_prices.xml -file5=/tmp/entsoe_tomorrow_prices.xml -file6=/tmp/awattar_prices.txt -file7=/tmp/awattar_prices_sorted.txt -file8=/tmp/entsoe_prices.txt -file9=/tmp/entsoe_tomorrow_prices_sorted.txt -file10=/tmp/entsoe_today_prices.txt -file11=/tmp/entsoe_today_prices_sorted.txt -file12=/tmp/tibber_prices_sorted.txt -file13=/tmp/entsoe_tomorrow_prices.txt -file14=/tmp/tibber_prices.txt -file15=/tmp/tibber_today_prices.txt -file16=/tmp/tibber_today_prices_sorted.txt -file17=/tmp/tibber_tomorrow_prices.txt -file18=/tmp/tibber_tomorrow_prices_sorted.txt -file19=/tmp/entsoe_prices_sorted.txt - -########## Optional environmental variables - -if [ -z "$LOG_FILE" ]; then - LOG_FILE="/tmp/spotmarket-switcher.log" -fi -if [ -z "$LOG_MAX_SIZE" ]; then - LOG_MAX_SIZE=1024 # 1 MB -fi -if [ -z "$LOG_FILES_TO_KEEP" ]; then - LOG_FILES_TO_KEEP=2 -fi - -########## Testing series of preconditions prior to execution of script - -num_tools_missing=0 -tools="awk curl cat sed sort head tail" -if [ 0 -lt $use_victron_charger ]; then - tools="$tools dbus" - charger_command_turnon="dbus -y com.victronenergy.settings /Settings/CGwacs/BatteryLife/Schedule/Charge/0/Day SetValue -- 7" - charger_command_turnoff="dbus -y com.victronenergy.settings /Settings/CGwacs/BatteryLife/Schedule/Charge/0/Day SetValue -- -7" - SOC_percent=$(dbus-send --system --print-reply --dest=com.victronenergy.system /Dc/Battery/Soc com.victronenergy.BusItem.GetValue | grep variant | awk '{print $3}') # This will get the battery state of charge (SOC) from a Victron Energy system -fi - -for tool in $tools; do - if ! which "$tool" >/dev/null; then - log_info "E: Please ensure the tool '$tool' is found." - num_tools_missing=$((num_tools_missing + 1)) - fi -done - -if [ $num_tools_missing -gt 0 ]; then - log_info "E: $num_tools_missing tools are missing." - exit 127 -fi - -unset num_tools_missing - ####################################### ### Begin of the functions... ### ####################################### @@ -937,6 +825,120 @@ exit_with_cleanup() { ### Begin of the script... ### #################################### +# Path to the current script directory +DIR="$(dirname "$0")" + +if [ -f "$DIR/config.txt" ]; then + # Include the configuration file + source "$DIR/config.txt" +else + log_info "E: The file $DIR/config.txt was not found! Configure the existing sample.config.txt file and then save it as config.txt in the same directory." false + exit 127 +fi + +if [ -z "$UNAME" ]; then + UNAME=$(uname) +fi +if [ "Darwin" = "$UNAME" ]; then + log_info "W: MacOS has a different implementation of 'date' - use conda if hunting a bug on a mac". +fi + +# further API parameters (no need to edit) +dateInSeconds=$(LC_ALL=C TZ=$TZ date +"%s") +if [ "Darwin" = "$UNAME" ]; then + yesterday=$(LC_ALL=C TZ=$TZ date -j -f "%s" $((dateInSeconds - 86400)) +%d)2300 + yestermonth=$(LC_ALL=C TZ=$TZ date -j -f "%s" $((dateInSeconds - 86400)) +%m) + yesteryear=$(LC_ALL=C TZ=$TZ date -j -f "%s" $((dateInSeconds - 86400)) +%Y) + today=$(LC_ALL=C TZ=$TZ date -j -f "%s" $((dateInSeconds)) +%d)2300 + today2=$(LC_ALL=C TZ=$TZ date -j -f "%s" $((dateInSeconds)) +%d) + todaymonth=$(LC_ALL=C TZ=$TZ date -j -f "%s" $((dateInSeconds)) +%m) + todayyear=$(LC_ALL=C TZ=$TZ date -j -f "%s" $((dateInSeconds)) +%Y) + tomorrow=$(LC_ALL=C TZ=$TZ date -j -f "%s" $((dateInSeconds + 86400)) +%d)2300 + tomorrow2=$(LC_ALL=C TZ=$TZ date -j -f "%s" $((dateInSeconds + 86400)) +%d) + tomorrowmonth=$(LC_ALL=C TZ=$TZ date -j -f "%s" $((dateInSeconds + 86400)) +%m) + tomorrowyear=$(LC_ALL=C TZ=$TZ date -j -f "%s" $((dateInSeconds + 86400)) +%Y) + getnow=$(LC_ALL=C TZ=$TZ date -j -f "%s" $((dateInSeconds)) +%k) +else + yesterday=$(LC_ALL=C TZ=$TZ date -d @$((dateInSeconds - 86400)) +%d)2300 + yestermonth=$(LC_ALL=C TZ=$TZ date -d @$((dateInSeconds - 86400)) +%m) + yesteryear=$(LC_ALL=C TZ=$TZ date -d @$((dateInSeconds - 86400)) +%Y) + today=$(LC_ALL=C TZ=$TZ date -d @$((dateInSeconds)) +%d)2300 + today2=$(LC_ALL=C TZ=$TZ date -d @$((dateInSeconds)) +%d) + todaymonth=$(LC_ALL=C TZ=$TZ date -d @$((dateInSeconds)) +%m) + todayyear=$(LC_ALL=C TZ=$TZ date -d @$((dateInSeconds)) +%Y) + tomorrow=$(LC_ALL=C TZ=$TZ date -d @$((dateInSeconds + 86400)) +%d)2300 + tomorrow2=$(LC_ALL=C TZ=$TZ date -d @$((dateInSeconds + 86400)) +%d) + tomorrowmonth=$(LC_ALL=C TZ=$TZ date -d @$((dateInSeconds + 86400)) +%m) + tomorrowyear=$(LC_ALL=C TZ=$TZ date -d @$((dateInSeconds + 86400)) +%Y) + getnow=$(LC_ALL=C TZ=$TZ date -d @$((dateInSeconds)) +%k) +fi + +now_linenumber=$((getnow + 1)) +link1="https://api.awattar.$awattar/v1/marketdata/current.yaml" +link2="http://api.awattar.$awattar/v1/marketdata/current.yaml?tomorrow=include" +link3="https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline/$latitude%2C%20$longitude/$todayyear-$todaymonth-$today2/$tomorrowyear-$tomorrowmonth-$tomorrow2?unitGroup=metric&elements=solarenergy%2Ccloudcover%2Csunrise%2Csunset&include=days&key=$visualcrossing_api_key&contentType=csv" +link4="https://web-api.tp.entsoe.eu/api?securityToken=$entsoe_eu_api_security_token&documentType=A44&in_Domain=$in_Domain&out_Domain=$out_Domain&periodStart=$yesteryear$yestermonth$yesterday&periodEnd=$todayyear$todaymonth$today" +link5="https://web-api.tp.entsoe.eu/api?securityToken=$entsoe_eu_api_security_token&documentType=A44&in_Domain=$in_Domain&out_Domain=$out_Domain&periodStart=$todayyear$todaymonth$today&periodEnd=$tomorrowyear$tomorrowmonth$tomorrow" +link6="https://api.tibber.com/v1-beta/gql" +file1=/tmp/awattar_today_prices.yaml +file2=/tmp/awattar_tomorrow_prices.yaml +file3=/tmp/expected_solarenergy.csv +file4=/tmp/entsoe_today_prices.xml +file5=/tmp/entsoe_tomorrow_prices.xml +file6=/tmp/awattar_prices.txt +file7=/tmp/awattar_prices_sorted.txt +file8=/tmp/entsoe_prices.txt +file9=/tmp/entsoe_tomorrow_prices_sorted.txt +file10=/tmp/entsoe_today_prices.txt +file11=/tmp/entsoe_today_prices_sorted.txt +file12=/tmp/tibber_prices_sorted.txt +file13=/tmp/entsoe_tomorrow_prices.txt +file14=/tmp/tibber_prices.txt +file15=/tmp/tibber_today_prices.txt +file16=/tmp/tibber_today_prices_sorted.txt +file17=/tmp/tibber_tomorrow_prices.txt +file18=/tmp/tibber_tomorrow_prices_sorted.txt +file19=/tmp/entsoe_prices_sorted.txt + +########## Optional environmental variables + +if [ -z "$LOG_FILE" ]; then + LOG_FILE="/tmp/spotmarket-switcher.log" +fi +if [ -z "$LOG_MAX_SIZE" ]; then + LOG_MAX_SIZE=1024 # 1 MB +fi +if [ -z "$LOG_FILES_TO_KEEP" ]; then + LOG_FILES_TO_KEEP=2 +fi + +########## Testing series of preconditions prior to execution of script + +num_tools_missing=0 +tools="awk curl cat sed sort head tail" +if [ 0 -lt $use_victron_charger ]; then + tools="$tools dbus" + charger_command_turnon="dbus -y com.victronenergy.settings /Settings/CGwacs/BatteryLife/Schedule/Charge/0/Day SetValue -- 7" + charger_command_turnoff="dbus -y com.victronenergy.settings /Settings/CGwacs/BatteryLife/Schedule/Charge/0/Day SetValue -- -7" + SOC_percent=$(dbus-send --system --print-reply --dest=com.victronenergy.system /Dc/Battery/Soc com.victronenergy.BusItem.GetValue | grep variant | awk '{print $3}') # This will get the battery state of charge (SOC) from a Victron Energy system +fi + +for tool in $tools; do + if ! which "$tool" >/dev/null; then + log_info "E: Please ensure the tool '$tool' is found." + num_tools_missing=$((num_tools_missing + 1)) + fi +done + +if [ $num_tools_missing -gt 0 ]; then + log_info "E: $num_tools_missing tools are missing." + exit 127 +fi + +unset num_tools_missing + +########## Start ########## + echo >>"$LOG_FILE" log_info "I: Bash Version: $(bash --version | head -n 1)" From 8f44a0720dfb17b1403306be396aa9c962da7d6c Mon Sep 17 00:00:00 2001 From: Christian Butterweck Date: Tue, 24 Oct 2023 21:57:26 +0200 Subject: [PATCH 41/59] Fix MacOS log_info error --- scripts/controller.sh | 186 +++++++++++++++++++++++------------------- 1 file changed, 100 insertions(+), 86 deletions(-) diff --git a/scripts/controller.sh b/scripts/controller.sh index 986ca76..cfec16b 100644 --- a/scripts/controller.sh +++ b/scripts/controller.sh @@ -184,6 +184,20 @@ declare -A valid_vars=( ["tibber_api_key"]="string" ) +# Define a logging function and try out different methods. Maybe log_message is not available at MacOS for example. +log_message() { + message="$1" + if type log_info >/dev/null 2>&1; then + log_info "$message" + elif type logger >/dev/null 2>&1; then + logger -p user.info -t "Spotmarket-Switcher" "$message" + elif type syslog >/dev/null 2>&1; then + syslog -s "$message" + else + echo "${message:3}" >&2 + fi +} + declare -A config_values parse_and_validate_config() { @@ -265,21 +279,21 @@ download_awattar_prices() { local sleep_time="$4" if [ -z "$DEBUG" ]; then - log_info "I: Please be patient. First we wait $sleep_time seconds in case the system clock is not syncronized and not to overload the API." false + log_message "I: Please be patient. First we wait $sleep_time seconds in case the system clock is not syncronized and not to overload the API." false sleep "$sleep_time" fi if ! curl "$url" >"$file"; then - log_info "E: Download of aWATTar prices from '$url' to '$file' failed." + log_message "E: Download of aWATTar prices from '$url' to '$file' failed." exit_with_cleanup 1 fi if ! test -f "$file"; then - log_info "E: Could not get aWATTar prices from '$url' to feed file '$file'." + log_message "E: Could not get aWATTar prices from '$url' to feed file '$file'." exit_with_cleanup 1 fi if [ -n "$DEBUG" ]; then - log_info "D: Download of file '$file' from URL '$url' successful." >&2 + log_message "D: Download of file '$file' from URL '$url' successful." >&2 fi echo >>"$file" awk '/data_price_hour_rel_.*_amount: / {print substr($0, index($0, ":") + 2)}' "$file" >"$output_file" @@ -290,7 +304,7 @@ download_awattar_prices() { if [ -f "$file2" ] && [ "$(wc -l <"$file1")" = "$(wc -l <"$file2")" ]; then rm -f "$file2" - log_info "I: File '$file2' has no tomorrow data, we have to try it again until the new prices are online." false + log_message "I: File '$file2' has no tomorrow data, we have to try it again until the new prices are online." false fi } @@ -314,13 +328,13 @@ download_tibber_prices() { local sleep_time="$3" if [ -z "$DEBUG" ]; then - log_info "I: Please be patient. First we wait $sleep_time seconds in case the system clock is not syncronized and not to overload the API." false + log_message "I: Please be patient. First we wait $sleep_time seconds in case the system clock is not syncronized and not to overload the API." false sleep "$sleep_time" else - log_info "D: No delay of download of Tibber data since DEBUG variable set." + log_message "D: No delay of download of Tibber data since DEBUG variable set." fi if ! get_tibber_api | tr -d '{}[]' >"$file"; then - log_info "E: Download of Tibber prices from '$url' to '$file' failed." + log_message "E: Download of Tibber prices from '$url' to '$file' failed." exit_with_cleanup 1 fi @@ -339,7 +353,7 @@ download_tibber_prices() { echo "date_now_day: $timestamp" >>"$file17" if [ ! -s "$file16" ]; then - log_info "E: Tibber prices cannot be extracted to '$file16', please check your Tibber API Key." + log_message "E: Tibber prices cannot be extracted to '$file16', please check your Tibber API Key." rm "$file" exit_with_cleanup 1 fi @@ -352,30 +366,30 @@ download_entsoe_prices() { local sleep_time="$4" if [ -z "$DEBUG" ]; then - log_info "I: Please be patient. First we wait $sleep_time seconds in case the system clock is not syncronized and not to overload the API." false + log_message "I: Please be patient. First we wait $sleep_time seconds in case the system clock is not syncronized and not to overload the API." false sleep "$sleep_time" else - log_info "D: No delay of download of entsoe data since DEBUG variable set." >&2 + log_message "D: No delay of download of entsoe data since DEBUG variable set." >&2 fi if ! curl "$url" >"$file"; then - log_info "E: Retrieval of entsoe data from '$url' into file '$file' failed." + log_message "E: Retrieval of entsoe data from '$url' into file '$file' failed." exit_with_cleanup 1 fi if ! test -f "$file"; then - log_info "E: Could not find file '$file' with entsoe price data. Curl itself reported success." + log_message "E: Could not find file '$file' with entsoe price data. Curl itself reported success." exit_with_cleanup 1 fi - if [ -n "$DEBUG" ]; then log_info "D: No delay of download of entsoe data since DEBUG variable set." "D: Entsoe file '$file' with price data downloaded" >&2 >&2; fi + if [ -n "$DEBUG" ]; then log_message "D: No delay of download of entsoe data since DEBUG variable set." "D: Entsoe file '$file' with price data downloaded" >&2 >&2; fi if [ ! -s "$file" ]; then - log_info "E: Entsoe file '$file' is empty, please check your entsoe API Key." + log_message "E: Entsoe file '$file' is empty, please check your entsoe API Key." exit_with_cleanup 1 fi - if [ -n "$DEBUG" ]; then log_info "D: No delay of download of entsoe data since DEBUG variable set." "D: Entsoe file '$file' with price data downloaded" >&2; fi + if [ -n "$DEBUG" ]; then log_message "D: No delay of download of entsoe data since DEBUG variable set." "D: Entsoe file '$file' with price data downloaded" >&2; fi awk ' # Capture content inside the tag @@ -421,17 +435,17 @@ download_entsoe_prices() { # At the end of processing, print out the captured prices or any error messages END { if (error_code == 999) { - log_info "E: Entsoe data retrieval error:", error_message + log_message "E: Entsoe data retrieval error:", error_message exit_with_cleanup 1 } else if (prices != "") { printf "%s", prices > "'"$output_file"'" } else { if ("'"$output_file"'" != "'"$file13"'") { - log_info "E: No prices found in the today XML data." + log_message "E: No prices found in the today XML data." exit_with_cleanup 1 } } - log_info "E: No prices found in the tomorrow XML data." + log_message "E: No prices found in the tomorrow XML data." } ' "$file" @@ -461,28 +475,28 @@ download_solarenergy() { if ((use_solarweather_api_to_abort == 1)); then delay=$((RANDOM % 15 + 1)) if [ -z "$DEBUG" ]; then - log_info "I: Please be patient. A delay of $delay seconds will help avoid overloading the Solarweather-API." false + log_message "I: Please be patient. A delay of $delay seconds will help avoid overloading the Solarweather-API." false # Delaying a random time <=15s to reduce impact on site - download is not time-critical sleep "$delay" else - log_info "D: No delay of download of solarenergy data since DEBUG variable set." >&2 + log_message "D: No delay of download of solarenergy data since DEBUG variable set." >&2 fi if ! curl "$link3" -o "$file3"; then - log_info "E: Download of solarenergy data from '$link3' failed." + log_message "E: Download of solarenergy data from '$link3' failed." exit_with_cleanup 1 elif ! test -f "$file3"; then - log_info "E: Could not get solarenergy data, missing file '$file3'." + log_message "E: Could not get solarenergy data, missing file '$file3'." exit_with_cleanup 1 fi if [ -n "$DEBUG" ]; then - log_info "D: File3 $file3 downloaded" >&2 + log_message "D: File3 $file3 downloaded" >&2 fi if ! test -f "$file3"; then - log_info "E: Could not find downloaded file '$file3' with solarenergy data." + log_message "E: Could not find downloaded file '$file3' with solarenergy data." exit_with_cleanup 1 fi if [ -n "$DEBUG" ]; then - log_info "D: Solarenergy data downloaded to file '$file3'." + log_message "D: Solarenergy data downloaded to file '$file3'." fi fi } @@ -540,7 +554,7 @@ convert_vars_to_integer() { printf -v "$integer_var" '%s' "$(euroToMillicent "${!var}" "$potency")" local value="${!integer_var}" # Speichern Sie den Wert in einer temporären Variable if [ -n "$DEBUG" ]; then - log_info "D: Variable: $var | Original: ${!var} | Integer: $value | Len: ${#value}" >&2 + log_message "D: Variable: $var | Original: ${!var} | Integer: $value | Len: ${#value}" >&2 fi done } @@ -604,7 +618,7 @@ evaluate_conditions() { description_value="${descriptions_ref[$condition]}" condition_evaluation=$([ "${conditions_ref[$condition]}" -eq 1 ] && echo true || echo false) result="($description_value) evaluates to $condition_evaluation" - log_info "D: condition_evaluation [ $result ]." >&2 + log_message "D: condition_evaluation [ $result ]." >&2 fi if ((conditions_ref[$condition])) && [[ $condition_met -eq 0 ]]; then @@ -637,11 +651,11 @@ is_charging_economical() { [[ $reference_price -ge $total_cost ]] && is_economical=0 if [ -n "$DEBUG" ]; then - log_info "D: is_charging_economical [ $is_economical - $([ "$is_economical" -eq 1 ] && echo "false" || echo "true") ]." >&2 + log_message "D: is_charging_economical [ $is_economical - $([ "$is_economical" -eq 1 ] && echo "false" || echo "true") ]." >&2 reference_price_euro=$(millicentToEuro $reference_price) total_cost_euro=$(millicentToEuro $total_cost) is_economical_str=$([ "$is_economical" -eq 1 ] && echo "false" || echo "true") - log_info "D: if [ reference_price $reference_price_euro > total_cost $total_cost_euro ] result is $is_economical_str." >&2 + log_message "D: if [ reference_price $reference_price_euro > total_cost $total_cost_euro ] result is $is_economical_str." >&2 fi return $is_economical @@ -654,10 +668,10 @@ manage_charging() { if [[ $action == "on" ]]; then $charger_command_turnon >/dev/null - log_info "I: Victron scheduled charging is ON. Battery SOC is at $SOC_percent %. $reason" + log_message "I: Victron scheduled charging is ON. Battery SOC is at $SOC_percent %. $reason" else $charger_command_turnoff >/dev/null - log_info "I: Victron scheduled charging is OFF. Battery SOC is at $SOC_percent %. $reason" + log_message "I: Victron scheduled charging is OFF. Battery SOC is at $SOC_percent %. $reason" fi } @@ -667,7 +681,7 @@ check_abort_condition() { local log_message=$2 if ((condition_result)); then - log_info "I: $log_message Abort." + log_message "I: $log_message Abort." execute_charging=0 execute_switchablesockets_on=0 fi @@ -680,12 +694,12 @@ manage_fritz_sockets() { [ "$action" != "off" ] && action=$([ "$execute_switchablesockets_on" == "1" ] && echo "on" || echo "off") if fritz_login; then - log_info "I: Turning $action Fritz sockets." + log_message "I: Turning $action Fritz sockets." for socket in "${sockets[@]}"; do [ "$socket" != "0" ] && manage_fritz_socket "$action" "$socket" done else - log_info "E: Fritz login failed." + log_message "E: Fritz login failed." fi } @@ -693,7 +707,7 @@ manage_fritz_socket() { local action=$1 local socket=$2 local url="http://$fbox/webservices/homeautoswitch.lua?sid=$sid&ain=$socket&switchcmd=setswitch$action" - curl -s "$url" >/dev/null || log_info "E: Could not call URL '$url' to switch $action said switch - ignored." + curl -s "$url" >/dev/null || log_message "E: Could not call URL '$url' to switch $action said switch - ignored." } fritz_login() { @@ -701,7 +715,7 @@ fritz_login() { sid="" challenge=$(curl -s "http://$fbox/login_sid.lua" | grep -o "[a-z0-9]\{8\}" | cut -d'>' -f 2) if [ -z "$challenge" ]; then - log_info "E: Could not retrieve challenge from login_sid.lua." + log_message "E: Could not retrieve challenge from login_sid.lua." return 1 fi @@ -710,12 +724,12 @@ fritz_login() { grep -o "[a-z0-9]\{16\}" | cut -d'>' -f 2) if [ "$sid" = "0000000000000000" ]; then - log_info "E: Login to Fritz!Box failed." + log_message "E: Login to Fritz!Box failed." return 1 fi if [ -n "$DEBUG" ]; then - log_info "D: Login to Fritz!Box successful." >&2 + log_message "D: Login to Fritz!Box successful." >&2 fi return 0 } @@ -726,7 +740,7 @@ manage_shelly_sockets() { [ "$action" != "off" ] && action=$([ "$execute_switchablesockets_on" == "1" ] && echo "on" || echo "off") - log_info "I: Turning $action Shelly sockets." + log_message "I: Turning $action Shelly sockets." for ip in "${shelly_ips[@]}"; do [ "$ip" != "0" ] && manage_shelly_socket "$action" "$ip" done @@ -735,7 +749,7 @@ manage_shelly_sockets() { manage_shelly_socket() { local action=$1 local ip=$2 - curl -s -u "$shellyuser:$shellypasswd" "http://$ip/relay/0?turn=$action" -o /dev/null || log_info "E: Could not execute switch-$action of Shelly socket with IP $ip - ignored." + curl -s -u "$shellyuser:$shellypasswd" "http://$ip/relay/0?turn=$action" -o /dev/null || log_message "E: Could not execute switch-$action of Shelly socket with IP $ip - ignored." } millicentToEuro() { @@ -770,8 +784,8 @@ euroToMillicent() { v=$(awk -v euro="$euro" -v potency="$potency" 'BEGIN {printf "%.0f", euro * (10 ^ potency)}') if [ -z "$v" ]; then - log_info "E: Could not translate '$euro' to an integer." - log_info "E: Called from ${FUNCNAME[1]} at line ${BASH_LINENO[0]}" + log_message "E: Could not translate '$euro' to an integer." + log_message "E: Called from ${FUNCNAME[1]} at line ${BASH_LINENO[0]}" return 1 fi echo "$v" @@ -779,14 +793,14 @@ euroToMillicent() { } euroToMillicent_test() { - log_info "I: Testing euroToMillicent" false + log_message "I: Testing euroToMillicent" false for i in 123456 12345.6 1234.56 123.456 12.3456 1.23456 0.123456 .123456 .233 .23 .2 2.33 2.3 2 2,33 2,3 2 23; do echo -n "$i -> " euroToMillicent $i done } -log_info() { +log_message() { local msg="$1" local prefix=$(echo "$msg" | head -n 1 | cut -d' ' -f1) # Extract the first word from the first line local color="\033[1m" # Default color @@ -814,7 +828,7 @@ log_info() { } exit_with_cleanup() { - log_info "I: Cleanup and exit with error $1" + log_message "I: Cleanup and exit with error $1" manage_charging "off" "Turn off charging." manage_fritz_sockets "off" manage_shelly_sockets "off" @@ -832,7 +846,7 @@ if [ -f "$DIR/config.txt" ]; then # Include the configuration file source "$DIR/config.txt" else - log_info "E: The file $DIR/config.txt was not found! Configure the existing sample.config.txt file and then save it as config.txt in the same directory." false + log_message "E: The file $DIR/config.txt was not found! Configure the existing sample.config.txt file and then save it as config.txt in the same directory." false exit 127 fi @@ -840,7 +854,7 @@ if [ -z "$UNAME" ]; then UNAME=$(uname) fi if [ "Darwin" = "$UNAME" ]; then - log_info "W: MacOS has a different implementation of 'date' - use conda if hunting a bug on a mac". + log_message "W: MacOS has a different implementation of 'date' - use conda if hunting a bug on a mac". fi # further API parameters (no need to edit) @@ -925,13 +939,13 @@ fi for tool in $tools; do if ! which "$tool" >/dev/null; then - log_info "E: Please ensure the tool '$tool' is found." + log_message "E: Please ensure the tool '$tool' is found." num_tools_missing=$((num_tools_missing + 1)) fi done if [ $num_tools_missing -gt 0 ]; then - log_info "E: $num_tools_missing tools are missing." + log_message "E: $num_tools_missing tools are missing." exit 127 fi @@ -941,8 +955,8 @@ unset num_tools_missing echo >>"$LOG_FILE" -log_info "I: Bash Version: $(bash --version | head -n 1)" -log_info "I: Spotmarket-Switcher - Version $VERSION" +log_message "I: Bash Version: $(bash --version | head -n 1)" +log_message "I: Spotmarket-Switcher - Version $VERSION" parse_and_validate_config "$DIR/config.txt" # if [ $? -eq 1 ]; then @@ -961,14 +975,14 @@ if ((select_pricing_api == 1)); then # Test if data is current get_current_awattar_day if [ "$current_awattar_day" = "$(TZ=$TZ date +%-d)" ]; then - log_info "I: aWATTar today-data is up to date." false + log_message "I: aWATTar today-data is up to date." false else - log_info "I: aWATTar today-data is outdated, fetching new data." false + log_message "I: aWATTar today-data is outdated, fetching new data." false rm -f $file1 $file6 $file7 download_awattar_prices "$link1" "$file1" "$file6" $((RANDOM % 21 + 10)) fi else # Data file1 does not exist - log_info "I: Fetching today-data data from aWATTar." false + log_message "I: Fetching today-data data from aWATTar." false download_awattar_prices "$link1" "$file1" "$file6" $((RANDOM % 21 + 10)) fi @@ -978,14 +992,14 @@ elif ((select_pricing_api == 2)); then # Test if data is current get_current_entsoe_day if [ "$current_entsoe_day" = "$(TZ=$TZ date +%d)" ]; then - log_info "I: Entsoe today-data is up to date." false + log_message "I: Entsoe today-data is up to date." false else - log_info "I: Entsoe today-data is outdated, fetching new data." false + log_message "I: Entsoe today-data is outdated, fetching new data." false rm -f "$file4" "$file5" "$file8" "$file9" "$file10" "$file11" "$file13" "$file19" download_entsoe_prices "$link4" "$file4" "$file10" $((RANDOM % 21 + 10)) fi else # Entsoe data does not exist - log_info "I: Fetching today-data data from Entsoe." false + log_message "I: Fetching today-data data from Entsoe." false download_entsoe_prices "$link4" "$file4" "$file10" $((RANDOM % 21 + 10)) fi @@ -996,14 +1010,14 @@ elif ((select_pricing_api == 3)); then # Test if data is current get_current_tibber_day if [ "$current_tibber_day" = "$(TZ=$TZ date +%d)" ]; then - log_info "I: Tibber today-data is up to date." false + log_message "I: Tibber today-data is up to date." false else - log_info "I: Tibber today-data is outdated, fetching new data." false + log_message "I: Tibber today-data is outdated, fetching new data." false rm -f "$file14" "$file15" "$file16" download_tibber_prices "$link6" "$file14" $((RANDOM % 21 + 10)) fi else # Tibber data does not exist - log_info "I: Fetching today-data data from Tibber." false + log_message "I: Fetching today-data data from Tibber." false download_tibber_prices "$link6" "$file14" $((RANDOM % 21 + 10)) fi fi @@ -1017,14 +1031,14 @@ if ((include_second_day == 1)); then # Test if data is current get_current_awattar_day2 if [ "$current_awattar_day2" = "$(TZ=$TZ date +%-d)" ]; then - log_info "I: aWATTar tomorrow-data is up to date." false + log_message "I: aWATTar tomorrow-data is up to date." false else - log_info "I: aWATTar tomorrow-data is outdated, fetching new data." false + log_message "I: aWATTar tomorrow-data is outdated, fetching new data." false rm -f $file3 download_awattar_prices "$link2" "$file2" "$file6" $((RANDOM % 21 + 10)) fi else # Data file2 does not exist - log_info "I: aWATTar tomorrow-data does not exist, fetching data." false + log_message "I: aWATTar tomorrow-data does not exist, fetching data." false download_awattar_prices "$link2" "$file2" "$file6" $((RANDOM % 21 + 10)) fi @@ -1032,7 +1046,7 @@ if ((include_second_day == 1)); then # Test if Entsoe tomorrow data exists if [ ! -s "$file9" ]; then - log_info "I: File '$file9' has no tomorrow data, we have to try it again until the new prices are online." false + log_message "I: File '$file9' has no tomorrow data, we have to try it again until the new prices are online." false rm -f "$file5" "$file9" "$file13" download_entsoe_prices "$link5" "$file5" "$file13" $((RANDOM % 21 + 10)) fi @@ -1041,7 +1055,7 @@ if ((include_second_day == 1)); then if [ ! -s "$file18" ]; then rm -f "$file17" "$file18" - log_info "I: File '$file18' has no tomorrow data, we have to try it again until the new prices are online." false + log_message "I: File '$file18' has no tomorrow data, we have to try it again until the new prices are online." false rm -f "$file12" "$file14" "$file15" "$file16" "$file17" download_tibber_prices "$link6" "$file14" $((RANDOM % 21 + 10)) sort -t, -k1.9n $file17 >>"$file12" @@ -1076,28 +1090,28 @@ if ((use_solarweather_api_to_abort == 1)); then get_suntime_today fi -log_info "I: Please verify correct system time and timezone:\n $(TZ=$TZ date)" +log_message "I: Please verify correct system time and timezone:\n $(TZ=$TZ date)" echo -log_info "I: Current price is $current_price $Unit." -log_info "I: Lowest price will be $lowest_price $Unit." false -log_info "I: The average price will be $average_price $Unit." false -log_info "I: Highest price will be $highest_price $Unit." false -log_info "I: Second lowest price will be $second_lowest_price $Unit." false -log_info "I: Third lowest price will be $third_lowest_price $Unit." false -log_info "I: Fourth lowest price will be $fourth_lowest_price $Unit." false -log_info "I: Fifth lowest price will be $fifth_lowest_price $Unit." false -log_info "I: Sixth lowest price will be $sixth_lowest_price $Unit." false +log_message "I: Current price is $current_price $Unit." +log_message "I: Lowest price will be $lowest_price $Unit." false +log_message "I: The average price will be $average_price $Unit." false +log_message "I: Highest price will be $highest_price $Unit." false +log_message "I: Second lowest price will be $second_lowest_price $Unit." false +log_message "I: Third lowest price will be $third_lowest_price $Unit." false +log_message "I: Fourth lowest price will be $fourth_lowest_price $Unit." false +log_message "I: Fifth lowest price will be $fifth_lowest_price $Unit." false +log_message "I: Sixth lowest price will be $sixth_lowest_price $Unit." false if ((use_solarweather_api_to_abort == 1)); then - log_info "I: Sunrise today will be $sunrise_today and sunset will be $sunset_today. Suntime will be $suntime_today minutes." - log_info "I: Solarenergy today will be $solarenergy_today megajoule per sqaremeter with $cloudcover_today percent clouds." - log_info "I: Solarenergy tomorrow will be $solarenergy_tomorrow megajoule per squaremeter with $cloudcover_tomorrow percent clouds." + log_message "I: Sunrise today will be $sunrise_today and sunset will be $sunset_today. Suntime will be $suntime_today minutes." + log_message "I: Solarenergy today will be $solarenergy_today megajoule per sqaremeter with $cloudcover_today percent clouds." + log_message "I: Solarenergy tomorrow will be $solarenergy_tomorrow megajoule per squaremeter with $cloudcover_tomorrow percent clouds." if [ ! -s $file3 ]; then - log_info "E: File '$file3' is empty, please check your API Key if download is still not possible tomorrow." + log_message "E: File '$file3' is empty, please check your API Key if download is still not possible tomorrow." fi find "$file3" -size 0 -delete # FIXME - looks wrong and complicated - simple RM included in prior if clause? else - log_info "W: skip Solarweather. not activated" + log_message "W: skip Solarweather. not activated" fi charging_condition_met="" @@ -1185,20 +1199,20 @@ if ((execute_charging == 1 && use_victron_charger == 1)); then elif ((execute_charging != 1 && use_victron_charger == 1)); then manage_charging "off" "Charging was not executed." else - log_info "W: skip Victron Charger. not activated" + log_message "W: skip Victron Charger. not activated" fi # Execute Fritz DECT on command if ((use_fritz_dect_sockets == 1)); then manage_fritz_sockets else - log_info "W: skip Fritz DECT. not activated" + log_message "W: skip Fritz DECT. not activated" fi if ((use_shelly_wlan_sockets == 1)); then manage_shelly_sockets else - log_info "W: skip Shelly Api. not activated" + log_message "W: skip Shelly Api. not activated" fi echo >>"$LOG_FILE" @@ -1206,7 +1220,7 @@ echo >>"$LOG_FILE" # Rotating log files if [ -f "$LOG_FILE" ]; then if [ "$(du -k "$LOG_FILE" | awk '{print $1}')" -gt "$LOG_MAX_SIZE" ]; then - log_info "I: Rotating log files" + log_message "I: Rotating log files" mv "$LOG_FILE" "${LOG_FILE}.$(date +%Y%m%d%H%M%S)" touch "$LOG_FILE" find . -maxdepth 1 -name "${LOG_FILE}*" -type f -exec ls -1t {} + | @@ -1217,5 +1231,5 @@ if [ -f "$LOG_FILE" ]; then fi if [ -n "$DEBUG" ]; then - log_info "D: \[ OK \]" >&2 + log_message "D: \[ OK \]" >&2 fi From 63194ffa4d5d515ef14971370d6898d92b55a049 Mon Sep 17 00:00:00 2001 From: Christian Butterweck Date: Tue, 24 Oct 2023 22:04:52 +0200 Subject: [PATCH 42/59] Update controller.sh --- scripts/controller.sh | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/scripts/controller.sh b/scripts/controller.sh index cfec16b..5a5a329 100644 --- a/scripts/controller.sh +++ b/scripts/controller.sh @@ -188,13 +188,25 @@ declare -A valid_vars=( log_message() { message="$1" if type log_info >/dev/null 2>&1; then - log_info "$message" - elif type logger >/dev/null 2>&1; then - logger -p user.info -t "Spotmarket-Switcher" "$message" - elif type syslog >/dev/null 2>&1; then - syslog -s "$message" + log_info "$message" || { + # If log_info fails (returns a non-zero exit code), use an alternative logging method. + if type logger >/dev/null 2>&1; then + logger -p user.info -t "Spotmarket-Switcher" "$message" + elif type syslog >/dev/null 2>&1; then + syslog -s "$message" + else + echo "${message:3}" >&2 + fi + } else - echo "${message:3}" >&2 + # If log_info is not available, use an alternative logging method. + if type logger >/dev/null 2>&1; then + logger -p user.info -t "Spotmarket-Switcher" "$message" + elif type syslog >/dev/null 2>&1; then + syslog -s "$message" + else + echo "${message:3}" >&2 + fi fi } From fa76f59801ca08c653dcb5c4eeecef21fd39e1bd Mon Sep 17 00:00:00 2001 From: Christian Butterweck Date: Tue, 24 Oct 2023 22:09:31 +0200 Subject: [PATCH 43/59] Update controller.sh --- scripts/controller.sh | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/scripts/controller.sh b/scripts/controller.sh index 5a5a329..cfec16b 100644 --- a/scripts/controller.sh +++ b/scripts/controller.sh @@ -188,25 +188,13 @@ declare -A valid_vars=( log_message() { message="$1" if type log_info >/dev/null 2>&1; then - log_info "$message" || { - # If log_info fails (returns a non-zero exit code), use an alternative logging method. - if type logger >/dev/null 2>&1; then - logger -p user.info -t "Spotmarket-Switcher" "$message" - elif type syslog >/dev/null 2>&1; then - syslog -s "$message" - else - echo "${message:3}" >&2 - fi - } + log_info "$message" + elif type logger >/dev/null 2>&1; then + logger -p user.info -t "Spotmarket-Switcher" "$message" + elif type syslog >/dev/null 2>&1; then + syslog -s "$message" else - # If log_info is not available, use an alternative logging method. - if type logger >/dev/null 2>&1; then - logger -p user.info -t "Spotmarket-Switcher" "$message" - elif type syslog >/dev/null 2>&1; then - syslog -s "$message" - else - echo "${message:3}" >&2 - fi + echo "${message:3}" >&2 fi } From 10b309a1cea674a1a1da77ee23396ce86ab7a209 Mon Sep 17 00:00:00 2001 From: Christian Butterweck Date: Tue, 24 Oct 2023 22:22:26 +0200 Subject: [PATCH 44/59] Update controller.sh --- scripts/controller.sh | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/scripts/controller.sh b/scripts/controller.sh index cfec16b..2004dbe 100644 --- a/scripts/controller.sh +++ b/scripts/controller.sh @@ -184,20 +184,17 @@ declare -A valid_vars=( ["tibber_api_key"]="string" ) -# Define a logging function and try out different methods. Maybe log_message is not available at MacOS for example. +# Define a logging function and try out different methods. Maybe log_message is not available at MacOS. log_message() { message="$1" - if type log_info >/dev/null 2>&1; then - log_info "$message" - elif type logger >/dev/null 2>&1; then + if [ -z "$(command -v log_info)" ]; then logger -p user.info -t "Spotmarket-Switcher" "$message" - elif type syslog >/dev/null 2>&1; then - syslog -s "$message" else - echo "${message:3}" >&2 + log_info "$message" fi } + declare -A config_values parse_and_validate_config() { From cce154e40e60da882db54916925e6f0f8ca141d8 Mon Sep 17 00:00:00 2001 From: Christian Butterweck Date: Tue, 24 Oct 2023 22:50:05 +0200 Subject: [PATCH 45/59] Undo Entso E dev changes to fix API parsing error --- scripts/controller.sh | 134 +++++++++++++++++++++--------------------- 1 file changed, 67 insertions(+), 67 deletions(-) diff --git a/scripts/controller.sh b/scripts/controller.sh index 2004dbe..6c72cd8 100644 --- a/scripts/controller.sh +++ b/scripts/controller.sh @@ -363,93 +363,93 @@ download_entsoe_prices() { local sleep_time="$4" if [ -z "$DEBUG" ]; then - log_message "I: Please be patient. First we wait $sleep_time seconds in case the system clock is not syncronized and not to overload the API." false + echo "I: Please be patient. First we wait $sleep_time seconds in case the system clock is not syncronized and not to overload the API." sleep "$sleep_time" else - log_message "D: No delay of download of entsoe data since DEBUG variable set." >&2 + echo "D: No delay of download of entsoe data since DEBUG variable set." >&2 fi if ! curl "$url" >"$file"; then - log_message "E: Retrieval of entsoe data from '$url' into file '$file' failed." - exit_with_cleanup 1 + log_info "E: Retrieval of entsoe data from '$url' into file '$file' failed." + exit 1 fi if ! test -f "$file"; then - log_message "E: Could not find file '$file' with entsoe price data. Curl itself reported success." - exit_with_cleanup 1 + log_info "E: Could not find file '$file' with entsoe price data. Curl itself reported success." + exit 1 fi - if [ -n "$DEBUG" ]; then log_message "D: No delay of download of entsoe data since DEBUG variable set." "D: Entsoe file '$file' with price data downloaded" >&2 >&2; fi + if [ -n "$DEBUG" ]; then echo "D: Entsoe file '$file' with price data downloaded" >&2; fi if [ ! -s "$file" ]; then - log_message "E: Entsoe file '$file' is empty, please check your entsoe API Key." - exit_with_cleanup 1 + log_info "E: Entsoe file '$file' is empty, please check your entsoe API Key." + exit 1 fi - if [ -n "$DEBUG" ]; then log_message "D: No delay of download of entsoe data since DEBUG variable set." "D: Entsoe file '$file' with price data downloaded" >&2; fi + if [ -n "$DEBUG" ]; then echo "D: Entsoe file '$file' with price data downloaded"; fi awk ' - # Capture content inside the tag - // { - capture_period = 1 - } - /<\/Period>/ { - capture_period = 0 - } - # Ensure we are within a valid period and capture prices for one-hour resolution - capture_period && /PT60M<\/resolution>/ { - valid_period = 1 - } - valid_period && // { - gsub("", "", $0) - gsub("<\/price.amount>", "", $0) - gsub(/^[\t ]+|[\t ]+$/, "", $0) - prices = prices $0 ORS - } - valid_period && /<\/Period>/ { - exit - } - - # Capture error information inside the tag - // { - in_reason = 1 - error_message = "" - } - in_reason && // { - gsub(/|<\/code>/, "") - gsub(/^[\t ]+|[\t ]+$/, "", $0) - error_code = $0 - } - in_reason && // { - gsub(/|<\/text>/, "") - gsub(/^[\t ]+|[\t ]+$/, "", $0) - error_message = $0 - } - /<\/Reason>/ { - in_reason = 0 - } - - # At the end of processing, print out the captured prices or any error messages - END { - if (error_code == 999) { - log_message "E: Entsoe data retrieval error:", error_message - exit_with_cleanup 1 - } else if (prices != "") { - printf "%s", prices > "'"$output_file"'" - } else { - if ("'"$output_file"'" != "'"$file13"'") { - log_message "E: No prices found in the today XML data." - exit_with_cleanup 1 - } +// { + capture_period = 1 +} +/<\/Period>/ { + capture_period = 0 +} +capture_period && /PT60M<\/resolution>/ { + valid_period = 1 +} +valid_period && // { + gsub("", "", $0) + gsub("<\/price.amount>", "", $0) + gsub(/^[\t ]+|[\t ]+$/, "", $0) + prices = prices $0 ORS +} +valid_period && /<\/Period>/ { + exit +} + + +// { + in_reason = 1 + error_message = "" +} + +in_reason && // { + gsub(/|<\/code>/, "") + gsub(/^[\t ]+|[\t ]+$/, "", $0) + error_code = $0 +} + +in_reason && // { + gsub(/|<\/text>/, "") + gsub(/^[\t ]+|[\t ]+$/, "", $0) + error_message = $0 +} + +/<\/Reason>/ { + in_reason = 0 +} + +END { + if (error_code == 999) { + print "E: Entsoe data retrieval error:", error_message + exit 1 + } else if (prices != "") { + printf "%s", prices > "'"$output_file"'" + } else { + if ("'"$output_file"'" != "'"$file13"'") { + print "E: No prices found in the today XML data." + exit 1 } - log_message "E: No prices found in the tomorrow XML data." - } - ' "$file" + } + print "E: No prices found in the tomorrow XML data." +} +' "$file" if [ -f "$output_file" ]; then - sort -g "$output_file" >"${output_file%.*}_sorted.${output_file##*.}" + sort -g "$output_file" > "${output_file%.*}_sorted.${output_file##*.}" timestamp=$(TZ=$TZ date +%d) - echo "date_now_day: $timestamp" >>"$output_file" + echo "date_now_day: $timestamp" >> "$output_file" fi # Check if tomorrow file contains next day prices From 6b4213ed0f9ee5e8f8de7ef85d576c8ac919c7ce Mon Sep 17 00:00:00 2001 From: Christian Butterweck Date: Tue, 24 Oct 2023 23:06:42 +0200 Subject: [PATCH 46/59] Update controller.sh --- scripts/controller.sh | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/scripts/controller.sh b/scripts/controller.sh index 6c72cd8..f9189cd 100644 --- a/scripts/controller.sh +++ b/scripts/controller.sh @@ -363,30 +363,30 @@ download_entsoe_prices() { local sleep_time="$4" if [ -z "$DEBUG" ]; then - echo "I: Please be patient. First we wait $sleep_time seconds in case the system clock is not syncronized and not to overload the API." + log_message "I: Please be patient. First we wait $sleep_time seconds in case the system clock is not syncronized and not to overload the API." sleep "$sleep_time" else - echo "D: No delay of download of entsoe data since DEBUG variable set." >&2 + log_message "D: No delay of download of entsoe data since DEBUG variable set." >&2 fi if ! curl "$url" >"$file"; then - log_info "E: Retrieval of entsoe data from '$url' into file '$file' failed." - exit 1 + log_message "E: Retrieval of entsoe data from '$url' into file '$file' failed." + exit_with_cleanup 1 fi if ! test -f "$file"; then - log_info "E: Could not find file '$file' with entsoe price data. Curl itself reported success." - exit 1 + log_message "E: Could not find file '$file' with entsoe price data. Curl itself reported success." + exit_with_cleanup 1 fi - if [ -n "$DEBUG" ]; then echo "D: Entsoe file '$file' with price data downloaded" >&2; fi + if [ -n "$DEBUG" ]; then log_message "D: Entsoe file '$file' with price data downloaded" >&2; fi if [ ! -s "$file" ]; then - log_info "E: Entsoe file '$file' is empty, please check your entsoe API Key." - exit 1 + log_message "E: Entsoe file '$file' is empty, please check your entsoe API Key." + exit_with_cleanup 1 fi - if [ -n "$DEBUG" ]; then echo "D: Entsoe file '$file' with price data downloaded"; fi + if [ -n "$DEBUG" ]; then log_message "D: No delay of download of entsoe data since DEBUG variable set." "D: Entsoe file '$file' with price data downloaded" >&2; fi awk ' // { @@ -433,16 +433,16 @@ in_reason && // { END { if (error_code == 999) { print "E: Entsoe data retrieval error:", error_message - exit 1 + exit_with_cleanup 1 } else if (prices != "") { printf "%s", prices > "'"$output_file"'" } else { if ("'"$output_file"'" != "'"$file13"'") { print "E: No prices found in the today XML data." - exit 1 + exit_with_cleanup 1 } } - print "E: No prices found in the tomorrow XML data." + log_message "E: No prices found in the tomorrow XML data." } ' "$file" From bd4b840bb896ac01c0ffc25ffdfff3319c857ea3 Mon Sep 17 00:00:00 2001 From: Christian Butterweck Date: Tue, 24 Oct 2023 23:13:45 +0200 Subject: [PATCH 47/59] Fix Entso-E --- scripts/controller.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/scripts/controller.sh b/scripts/controller.sh index f9189cd..5d901d3 100644 --- a/scripts/controller.sh +++ b/scripts/controller.sh @@ -432,13 +432,13 @@ in_reason && // { END { if (error_code == 999) { - print "E: Entsoe data retrieval error:", error_message + log_message "E: Entsoe data retrieval error:", error_message exit_with_cleanup 1 } else if (prices != "") { printf "%s", prices > "'"$output_file"'" } else { if ("'"$output_file"'" != "'"$file13"'") { - print "E: No prices found in the today XML data." + log_message "E: No prices found in the today XML data." exit_with_cleanup 1 } } @@ -455,7 +455,6 @@ END { # Check if tomorrow file contains next day prices if [ "$include_second_day" = 1 ] && grep -q "PT60M" "$file" && [ "$(wc -l <"$output_file")" -gt 3 ]; then cat $file10 >$file8 - # echo >> $file8 if [ -f "$file13" ]; then cat "$file13" >>"$file8" fi @@ -464,7 +463,9 @@ END { timestamp=$(TZ=$TZ date +%d) echo "date_now_day: $timestamp" >>"$file8" else + if [ -f "$file11" ]; then cp $file11 $file19 # If no second day, copy sorted price file. + fi fi } From 95f96335dd2270432807de2dbf1ac40d4f44eef5 Mon Sep 17 00:00:00 2001 From: Christian Butterweck Date: Tue, 24 Oct 2023 23:21:01 +0200 Subject: [PATCH 48/59] Update controller.sh --- scripts/controller.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/controller.sh b/scripts/controller.sh index 5d901d3..2af56fb 100644 --- a/scripts/controller.sh +++ b/scripts/controller.sh @@ -433,16 +433,16 @@ in_reason && // { END { if (error_code == 999) { log_message "E: Entsoe data retrieval error:", error_message - exit_with_cleanup 1 + exit 1 } else if (prices != "") { printf "%s", prices > "'"$output_file"'" } else { if ("'"$output_file"'" != "'"$file13"'") { - log_message "E: No prices found in the today XML data." - exit_with_cleanup 1 + print "E: No prices found in the today XML data." + exit 1 } } - log_message "E: No prices found in the tomorrow XML data." + print "E: No prices found in the tomorrow XML data." } ' "$file" From 69b18a13d1a0337a91b6991649e960b17df95b98 Mon Sep 17 00:00:00 2001 From: Christian Butterweck Date: Tue, 24 Oct 2023 23:23:07 +0200 Subject: [PATCH 49/59] Update controller.sh --- scripts/controller.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/scripts/controller.sh b/scripts/controller.sh index 2af56fb..530fcdc 100644 --- a/scripts/controller.sh +++ b/scripts/controller.sh @@ -432,14 +432,12 @@ in_reason && // { END { if (error_code == 999) { - log_message "E: Entsoe data retrieval error:", error_message - exit 1 + print "E: Entsoe data retrieval error:", error_message } else if (prices != "") { printf "%s", prices > "'"$output_file"'" } else { if ("'"$output_file"'" != "'"$file13"'") { print "E: No prices found in the today XML data." - exit 1 } } print "E: No prices found in the tomorrow XML data." From 8495271ec7ef6bda79563992201276869806eb69 Mon Sep 17 00:00:00 2001 From: Christian Butterweck Date: Tue, 24 Oct 2023 23:25:44 +0200 Subject: [PATCH 50/59] Update controller.sh From cce3a19b65947875a533e0c11385e6e59dfe1211 Mon Sep 17 00:00:00 2001 From: Christian Butterweck Date: Tue, 24 Oct 2023 23:32:50 +0200 Subject: [PATCH 51/59] Update controller.sh --- scripts/controller.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/controller.sh b/scripts/controller.sh index 530fcdc..c655e51 100644 --- a/scripts/controller.sh +++ b/scripts/controller.sh @@ -448,7 +448,7 @@ END { sort -g "$output_file" > "${output_file%.*}_sorted.${output_file##*.}" timestamp=$(TZ=$TZ date +%d) echo "date_now_day: $timestamp" >> "$output_file" - fi + # Check if tomorrow file contains next day prices if [ "$include_second_day" = 1 ] && grep -q "PT60M" "$file" && [ "$(wc -l <"$output_file")" -gt 3 ]; then @@ -461,9 +461,9 @@ END { timestamp=$(TZ=$TZ date +%d) echo "date_now_day: $timestamp" >>"$file8" else - if [ -f "$file11" ]; then cp $file11 $file19 # If no second day, copy sorted price file. - fi + fi + else exit_with_cleanup 1 fi } From 1fa0c81276af07d42d3e583f0237420d080d7b0c Mon Sep 17 00:00:00 2001 From: Christian Butterweck Date: Tue, 24 Oct 2023 23:47:33 +0200 Subject: [PATCH 52/59] Update controller.sh --- scripts/controller.sh | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/scripts/controller.sh b/scripts/controller.sh index c655e51..3adbf79 100644 --- a/scripts/controller.sh +++ b/scripts/controller.sh @@ -435,12 +435,8 @@ END { print "E: Entsoe data retrieval error:", error_message } else if (prices != "") { printf "%s", prices > "'"$output_file"'" - } else { - if ("'"$output_file"'" != "'"$file13"'") { - print "E: No prices found in the today XML data." - } - } - print "E: No prices found in the tomorrow XML data." + } else + print "E: No prices found in the XML data." } ' "$file" From 69633018134a9d5dd1158c1b5d9f1390a9561b23 Mon Sep 17 00:00:00 2001 From: Christian Butterweck Date: Tue, 24 Oct 2023 23:59:55 +0200 Subject: [PATCH 53/59] use always the branch of the commit --- .github/workflows/main.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 90ed4d0..24e5b27 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -61,11 +61,9 @@ jobs: fi execute: runs-on: ${{ matrix.os }} - strategy: matrix: os: [ubuntu-latest, macos-latest] - steps: - name: Checkout code uses: actions/checkout@v3 From 196017190feb62393aa9b9faaed1f180bba58119 Mon Sep 17 00:00:00 2001 From: Christian Butterweck Date: Wed, 25 Oct 2023 00:10:18 +0200 Subject: [PATCH 54/59] Update main.yml --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 24e5b27..824e96c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -85,6 +85,7 @@ jobs: run: /tmp/testing//data/etc/Spotmarket-Switcher/service/run env: DEBUG: 1 + BRANCH: "${{ steps.set-branch-name.outputs.branch_name }}" rc_local_file: /tmp/testing_rc.local - name: Execute Control-Script run: /tmp/testing//data/etc/Spotmarket-Switcher/controller.sh From a7e64ca661276fe299e1bf5beeef83fdf53b53af Mon Sep 17 00:00:00 2001 From: Christian Butterweck Date: Wed, 25 Oct 2023 00:14:13 +0200 Subject: [PATCH 55/59] Update main.yml --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 824e96c..8ec7f2c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -81,6 +81,7 @@ jobs: env: DEBUG: 1 DESTDIR: /tmp/testing + BRANCH: "${{ steps.set-branch-name.outputs.branch_name }}" - name: Execute Run-Script run: /tmp/testing//data/etc/Spotmarket-Switcher/service/run env: From 69f3617c66b4021395832327ca0bfaee7e5e81c8 Mon Sep 17 00:00:00 2001 From: Christian Butterweck Date: Wed, 25 Oct 2023 00:18:20 +0200 Subject: [PATCH 56/59] Update main.yml --- .github/workflows/main.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8ec7f2c..abb1924 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -80,13 +80,12 @@ jobs: run: ./victron-venus-os-install.sh env: DEBUG: 1 - DESTDIR: /tmp/testing BRANCH: "${{ steps.set-branch-name.outputs.branch_name }}" + DESTDIR: /tmp/testing - name: Execute Run-Script run: /tmp/testing//data/etc/Spotmarket-Switcher/service/run env: DEBUG: 1 - BRANCH: "${{ steps.set-branch-name.outputs.branch_name }}" rc_local_file: /tmp/testing_rc.local - name: Execute Control-Script run: /tmp/testing//data/etc/Spotmarket-Switcher/controller.sh From 08c7eb184e74364168baeddca7f428eede24fd21 Mon Sep 17 00:00:00 2001 From: Christian Butterweck Date: Wed, 25 Oct 2023 00:25:35 +0200 Subject: [PATCH 57/59] Update main.yml --- .github/workflows/main.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index abb1924..8ec7f2c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -80,12 +80,13 @@ jobs: run: ./victron-venus-os-install.sh env: DEBUG: 1 - BRANCH: "${{ steps.set-branch-name.outputs.branch_name }}" DESTDIR: /tmp/testing + BRANCH: "${{ steps.set-branch-name.outputs.branch_name }}" - name: Execute Run-Script run: /tmp/testing//data/etc/Spotmarket-Switcher/service/run env: DEBUG: 1 + BRANCH: "${{ steps.set-branch-name.outputs.branch_name }}" rc_local_file: /tmp/testing_rc.local - name: Execute Control-Script run: /tmp/testing//data/etc/Spotmarket-Switcher/controller.sh From 10e36c9a12316af8cc34d37e859414d2a1d3a0f5 Mon Sep 17 00:00:00 2001 From: Christian Butterweck Date: Wed, 25 Oct 2023 00:26:42 +0200 Subject: [PATCH 58/59] Update main.yml --- .github/workflows/main.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8ec7f2c..65478e5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -86,7 +86,6 @@ jobs: run: /tmp/testing//data/etc/Spotmarket-Switcher/service/run env: DEBUG: 1 - BRANCH: "${{ steps.set-branch-name.outputs.branch_name }}" rc_local_file: /tmp/testing_rc.local - name: Execute Control-Script run: /tmp/testing//data/etc/Spotmarket-Switcher/controller.sh From da2cd0726c0b36a33ef4fe64f267ba9bf9442e1b Mon Sep 17 00:00:00 2001 From: Christian Butterweck Date: Wed, 25 Oct 2023 00:27:29 +0200 Subject: [PATCH 59/59] Update main.yml --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 65478e5..e174da5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -81,7 +81,7 @@ jobs: env: DEBUG: 1 DESTDIR: /tmp/testing - BRANCH: "${{ steps.set-branch-name.outputs.branch_name }}" + BRANCH: ${{ steps.set-branch-name.outputs.branch_name }} - name: Execute Run-Script run: /tmp/testing//data/etc/Spotmarket-Switcher/service/run env: