|
1 | 1 | #!/bin/bash |
2 | 2 |
|
3 | | -VERSION="2.5.4" |
| 3 | +VERSION="2.5.5-DEV" |
4 | 4 |
|
5 | 5 | if [ -z "$LANG" ]; then |
6 | 6 | export LANG="C" |
@@ -120,7 +120,7 @@ parse_and_validate_config() { |
120 | 120 | else |
121 | 121 | # Advanced validation for Bash > 4 |
122 | 122 | declare -A valid_vars=( |
123 | | - ["config_version"]="12" # Updated to 12 for the new version |
| 123 | + ["config_version"]="13" |
124 | 124 | ["use_fritz_dect_sockets"]="0|1" |
125 | 125 | ["fbox"]="^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$" |
126 | 126 | ["user"]="string" |
@@ -749,6 +749,58 @@ manage_discharging() { |
749 | 749 | fi |
750 | 750 | } |
751 | 751 |
|
| 752 | +get_dynamic_target_hours() { |
| 753 | + local current_soc=$1 |
| 754 | + local matrix_name=$2 |
| 755 | + local -n matrix_ref="${matrix_name}" # Use nameref to access the specified global array |
| 756 | + |
| 757 | + # Check if the matrix is defined and not empty |
| 758 | + if [ -z "${matrix_ref+x}" ] || [ ${#matrix_ref[@]} -eq 0 ]; then |
| 759 | + # log_message >&2 "W: Matrix '$matrix_name' is not defined or empty. Returning 0 hours." |
| 760 | + echo "0.0" |
| 761 | + return |
| 762 | + fi |
| 763 | + |
| 764 | + local result_hours="" |
| 765 | + |
| 766 | + # Sort the matrix by SoC to ensure correct interpolation |
| 767 | + IFS=$'\n' sorted_matrix=($(sort -n -k1 <<<"${matrix_ref[*]}")) |
| 768 | + unset IFS |
| 769 | + |
| 770 | + # Handle edge case: SoC is lower than the lowest defined point |
| 771 | + IFS=' ' read -r min_soc min_hours <<< "${sorted_matrix[0]}" |
| 772 | + if (( $(awk -v current="$current_soc" -v min="$min_soc" 'BEGIN {print (current <= min)}') )); then |
| 773 | + echo "$min_hours" |
| 774 | + return |
| 775 | + fi |
| 776 | + |
| 777 | + # Handle edge case: SoC is higher than the highest defined point |
| 778 | + IFS=' ' read -r max_soc max_hours <<< "${sorted_matrix[-1]}" |
| 779 | + if (( $(awk -v current="$current_soc" -v max="$max_soc" 'BEGIN {print (current >= max)}') )); then |
| 780 | + echo "$max_hours" |
| 781 | + return |
| 782 | + fi |
| 783 | + |
| 784 | + # Find the two points to interpolate between |
| 785 | + for ((i = 0; i < ${#sorted_matrix[@]} - 1; i++)); do |
| 786 | + IFS=' ' read -r lower_soc lower_hours <<< "${sorted_matrix[$i]}" |
| 787 | + IFS=' ' read -r upper_soc upper_hours <<< "${sorted_matrix[$((i + 1))]}" |
| 788 | + |
| 789 | + if (( $(awk -v current="$current_soc" -v lower="$lower_soc" -v upper="$upper_soc" 'BEGIN {print (current >= lower && current < upper)}') )); then |
| 790 | + # Perform linear interpolation |
| 791 | + result_hours=$(awk -v x="$current_soc" \ |
| 792 | + -v x1="$lower_soc" -v y1="$lower_hours" \ |
| 793 | + -v x2="$upper_soc" -v y2="$upper_hours" \ |
| 794 | + 'BEGIN {printf "%.2f", y1 + (x - x1) * (y2 - y1) / (x2 - x1)}') |
| 795 | + echo "$result_hours" |
| 796 | + return |
| 797 | + fi |
| 798 | + done |
| 799 | + |
| 800 | + # Fallback to the minimum hours if no range is found (should not happen with sorted array) |
| 801 | + echo "$min_hours" |
| 802 | +} |
| 803 | + |
752 | 804 | manage_fritz_sockets() { |
753 | 805 | local action=$1 |
754 | 806 | [ "$action" != "off" ] && action=$([ "$execute_fritzsocket_on" == "1" ] && echo "on" || echo "off") |
@@ -1818,24 +1870,83 @@ shelly_sockets_state="unknown" |
1818 | 1870 | fritz_sockets_state="unknown" |
1819 | 1871 | fritz_switchable_sockets_table="" |
1820 | 1872 | shelly_switchable_sockets_table="" |
| 1873 | + |
| 1874 | +# 1. Calculate Discharge Hours |
| 1875 | +discharge_hours="0.0" |
| 1876 | +if [ "$discharge_strategy" == "dynamic" ]; then |
| 1877 | + discharge_hours=$(get_dynamic_target_hours "$SOC_percent" "discharge_hours_matrix") |
| 1878 | + log_message >&2 "I: Dynamic Discharge: SoC is $SOC_percent%. Targeting the most expensive $discharge_hours hours." |
| 1879 | +fi |
| 1880 | + |
| 1881 | +# 2. Calculate Fritz Socket ON-Hours |
| 1882 | +fritz_hours="0.0" |
| 1883 | +if (( use_fritz_dect_sockets == 1 )) && [ "$fritz_socket_strategy" == "dynamic" ]; then |
| 1884 | + fritz_hours=$(get_dynamic_target_hours "$SOC_percent" "fritz_socket_hours_matrix") |
| 1885 | + log_message >&2 "I: Dynamic Fritz Sockets: SoC is $SOC_percent%. Targeting $fritz_hours ON-hours." |
| 1886 | +fi |
| 1887 | + |
| 1888 | +# 3. Calculate Shelly Socket ON-Hours |
| 1889 | +shelly_hours="0.0" |
| 1890 | +if (( use_shelly_wlan_sockets == 1 )) && [ "$shelly_socket_strategy" == "dynamic" ]; then |
| 1891 | + shelly_hours=$(get_dynamic_target_hours "$SOC_percent" "shelly_socket_hours_matrix") |
| 1892 | + log_message >&2 "I: Dynamic Shelly Sockets: SoC is $SOC_percent%. Targeting $shelly_hours ON-hours." |
| 1893 | +fi |
| 1894 | + |
1821 | 1895 | for idx in "${!sorted_prices[@]}"; do |
1822 | 1896 | i=$((idx + 1)) |
1823 | 1897 | charge_value="${charge_array[$idx]}" |
1824 | | - discharge_value="${discharge_array[$idx]}" |
1825 | | - fritzsocket_value="${fritzsocket_array[$idx]}" |
1826 | | - shellysocket_value="${shellysocket_array[$idx]}" |
| 1898 | + discharge_value="${discharge_array[$idx]}" # For static mode |
| 1899 | + fritzsocket_value="${fritzsocket_array[$idx]}" # For static mode |
| 1900 | + shellysocket_value="${shellysocket_array[$idx]}" # For static mode |
1827 | 1901 |
|
| 1902 | + # --- Charge Logic (unchanged) --- |
1828 | 1903 | if [ "$charge_value" -eq 1 ]; then |
1829 | 1904 | charge_table="$charge_table $i" |
1830 | 1905 | fi |
1831 | | - if [ "$use_charger" -ne 0 ] && [ "$SOC_percent" -ge "$discharge_value" ]; then |
1832 | | - discharge_table="$discharge_table $i" |
| 1906 | + |
| 1907 | + # --- Universal Dynamic/Static Logic for all components --- |
| 1908 | + |
| 1909 | + # 1. Discharge Table Population |
| 1910 | + if [ "$discharge_strategy" == "dynamic" ]; then |
| 1911 | + num_slots=$(awk -v h="$discharge_hours" 'BEGIN {printf "%.0f", h * 4}') |
| 1912 | + start_idx=$(( ${#sorted_prices[@]} - num_slots )) |
| 1913 | + if (( idx >= start_idx && SOC_percent > sonnen_minimum_SoC )); then |
| 1914 | + discharge_table="$discharge_table $i" |
| 1915 | + fi |
| 1916 | + else # Static |
| 1917 | + if [ "$use_charger" -ne 0 ] && [ "$SOC_percent" -ge "$discharge_value" ]; then |
| 1918 | + discharge_table="$discharge_table $i" |
| 1919 | + fi |
1833 | 1920 | fi |
1834 | | - if [ "$fritzsocket_value" -eq 1 ]; then |
1835 | | - fritz_switchable_sockets_table="$fritz_switchable_sockets_table $i" |
| 1921 | + |
| 1922 | + # 2. Fritz Socket Table Population |
| 1923 | + if (( use_fritz_dect_sockets == 1 )); then |
| 1924 | + if [ "$fritz_socket_strategy" == "dynamic" ]; then |
| 1925 | + num_slots=$(awk -v h="$fritz_hours" 'BEGIN {printf "%.0f", h * 4}') |
| 1926 | + start_idx=$(( ${#sorted_prices[@]} - num_slots )) |
| 1927 | + if (( idx >= start_idx )); then |
| 1928 | + fritz_switchable_sockets_table="$fritz_switchable_sockets_table $i" |
| 1929 | + fi |
| 1930 | + else # Static |
| 1931 | + if [ "$fritzsocket_value" -eq 1 ]; then |
| 1932 | + fritz_switchable_sockets_table="$fritz_switchable_sockets_table $i" |
| 1933 | + fi |
| 1934 | + fi |
1836 | 1935 | fi |
1837 | | - if [ "$shellysocket_value" -eq 1 ]; then |
1838 | | - shelly_switchable_sockets_table="$shelly_switchable_sockets_table $i" |
| 1936 | + |
| 1937 | + # 3. Shelly Socket Table Population |
| 1938 | + if (( use_shelly_wlan_sockets == 1 )); then |
| 1939 | + if [ "$shelly_socket_strategy" == "dynamic" ]; then |
| 1940 | + num_slots=$(awk -v h="$shelly_hours" 'BEGIN {printf "%.0f", h * 4}') |
| 1941 | + start_idx=$(( ${#sorted_prices[@]} - num_slots )) |
| 1942 | + if (( idx >= start_idx )); then |
| 1943 | + shelly_switchable_sockets_table="$shelly_switchable_sockets_table $i" |
| 1944 | + fi |
| 1945 | + else # Static |
| 1946 | + if [ "$shellysocket_value" -eq 1 ]; then |
| 1947 | + shelly_switchable_sockets_table="$shelly_switchable_sockets_table $i" |
| 1948 | + fi |
| 1949 | + fi |
1839 | 1950 | fi |
1840 | 1951 | done |
1841 | 1952 |
|
|
0 commit comments