foxesscloud 2.5.5__py3-none-any.whl → 2.5.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  ##################################################################################################
2
2
  """
3
3
  Module: Fox ESS Cloud
4
- Updated: 26 September 2024
4
+ Updated: 27 September 2024
5
5
  By: Tony Matthews
6
6
  """
7
7
  ##################################################################################################
@@ -10,7 +10,7 @@ By: Tony Matthews
10
10
  # ALL RIGHTS ARE RESERVED © Tony Matthews 2023
11
11
  ##################################################################################################
12
12
 
13
- version = "1.6.7"
13
+ version = "1.6.8"
14
14
  print(f"FoxESS-Cloud version {version}")
15
15
 
16
16
  debug_setting = 1
@@ -2270,8 +2270,8 @@ tariff_config = {
2270
2270
  'region': "H", # region code to use for Octopus API
2271
2271
  'update_time': 16.5, # time in hours when tomrow's data can be fetched
2272
2272
  'weighting': None, # weights for weighted average
2273
- 'plunge_price': [1, 10], # plunge price in p/kWh inc VAT over 24 hours from 7am, 7pm
2274
- 'plunge_slots': 6, # number of 30 minute slots to use
2273
+ 'plunge_price': [3, 10], # plunge price in p/kWh inc VAT over 24 hours from 7am, 7pm
2274
+ 'plunge_slots': 8, # number of 30 minute slots to use
2275
2275
  'data_wrap': 6, # prices to show per line
2276
2276
  'show_data': 1, # show pricing data
2277
2277
  'show_plot': 1 # plot pricing data
@@ -2681,7 +2681,7 @@ charge_config = {
2681
2681
  'consumption_days': 3, # number of days to use for average consumption (1-7)
2682
2682
  'consumption_span': 'week', # 'week' = last n days or 'weekday' = last n weekdays
2683
2683
  'use_today': 21.0, # hour when todays consumption and generation can be used
2684
- 'min_hours': 0.25, # minimum charge time in decimal hours
2684
+ 'min_hours': 0.5, # minimum charge time in decimal hours
2685
2685
  'min_kwh': 0.5, # minimum to add in kwh
2686
2686
  'forecast_selection': 1, # 0 = use available forecast / generation, 1 only update settings with forecast
2687
2687
  'annual_consumption': None, # optional annual consumption in kWh
@@ -2778,7 +2778,7 @@ def charge_needed(forecast=None, update_settings=0, timed_mode=None, show_data=N
2778
2778
  for t in times:
2779
2779
  if hour_in(hour_now, t) and update_settings > 0:
2780
2780
  update_settings = 0
2781
- output(f"\nSettings will not be updated during a charge period")
2781
+ output(f"\nSettings will not be updated during a charge period {format_period(t)}")
2782
2782
  time_to_start = round_time(t['start'] - base_hour) * steps_per_hour
2783
2783
  time_to_start += hour_adjustment * steps_per_hour if time_to_start > time_change else 0
2784
2784
  charge_time = round_time(t['end'] - t['start'])
@@ -3069,7 +3069,7 @@ def charge_needed(forecast=None, update_settings=0, timed_mode=None, show_data=N
3069
3069
  kwh_needed = test_charge
3070
3070
  charge_message = "** test charge **"
3071
3071
  # work out charge needed
3072
- if kwh_min > (reserve + kwh_contingency) and kwh_needed < charge_config['min_kwh'] and test_charge is None:
3072
+ if kwh_min > reserve and kwh_needed < charge_config['min_kwh'] and test_charge is None:
3073
3073
  output(f"\nNo charging needed:")
3074
3074
  output(f" SoC now: {current_soc:.0f}% at {hours_time(hour_now)} on {today}")
3075
3075
  charge_message = "no charge needed"
@@ -3089,17 +3089,18 @@ def charge_needed(forecast=None, update_settings=0, timed_mode=None, show_data=N
3089
3089
  output(f" SoC now: {current_soc:.0f}% at {hours_time(hour_now)} on {today}")
3090
3090
  # work out time to add kwh_needed to battery
3091
3091
  charge_rate = charge_power * charge_loss
3092
- hours = round_time(kwh_needed / charge_rate)
3092
+ hours = kwh_needed / charge_rate
3093
3093
  # check if charge time exceeded or charge needed exceeds capacity
3094
- hours_to_full = round_time((capacity - start_residual) / (charge_rate) + 10)
3095
- if hours < charge_config['min_hours']:
3096
- hours = charge_config['min_hours']
3097
- elif hours > charge_time:
3094
+ hours_to_full = (capacity - start_residual) / charge_rate
3095
+ if hours > charge_time:
3098
3096
  hours = charge_time
3099
3097
  elif hours > hours_to_full:
3100
3098
  kwh_shortfall = (hours - hours_to_full) * charge_rate # amount of energy that won't be added
3101
- required = hours_to_full + charge_time * kwh_shortfall / (end_residual - start_residual) # time to recover energy not added
3102
- hours = required if required < charge_time else charge_time
3099
+ required = hours_to_full + charge_time * kwh_shortfall / abs(start_residual - end_residual) # time to recover energy not added
3100
+ hours = required if required > hours and required < charge_time else charge_time
3101
+ # round charge time
3102
+ min_hours = charge_config['min_hours']
3103
+ hours = int(hours / min_hours + 0.99) * min_hours
3103
3104
  # rework charge and discharge
3104
3105
  charge_period = get_best_charge_period(start_at, hours)
3105
3106
  charge_offset = round_time(charge_period['start'] - start_at) if charge_period is not None else 0
@@ -3109,7 +3110,7 @@ def charge_needed(forecast=None, update_settings=0, timed_mode=None, show_data=N
3109
3110
  start_residual = interpolate(start_timed, bat_timed)
3110
3111
  end_soc = min([int((start_residual + kwh_needed) / capacity * 100 + 0.5), 100])
3111
3112
  output(f" Start SoC: {start_residual / capacity * 100:.0f}% at {hours_time(adjusted_hour(start_timed, time_line))} ({start_residual:.2f}kWh)")
3112
- output(f" Charge to: {end_soc:.0f} {hours_time(adjusted_hour(start_timed, time_line))}-{hours_time(adjusted_hour(end_timed, time_line))}" + (f" at {price:5.2f}p/kWh" if price is not None else ""))
3113
+ output(f" Charge to: {end_soc:.0f}% {hours_time(adjusted_hour(start_timed, time_line))}-{hours_time(adjusted_hour(end_timed, time_line))}" + (f" at {price:5.2f}p/kWh" if price is not None else ""))
3113
3114
  for i in range(int(time_to_start), int(end_timed) + 1):
3114
3115
  j = i + 1
3115
3116
  # work out time (fraction of hour) when charging in hour from i to j
@@ -3141,11 +3142,11 @@ def charge_needed(forecast=None, update_settings=0, timed_mode=None, show_data=N
3141
3142
  if show_data > 0:
3142
3143
  data_wrap = charge_config['data_wrap'] if charge_config.get('data_wrap') is not None else 6
3143
3144
  s = f"\nBattery Energy kWh:" if show_data == 2 else f"\nBattery SoC:"
3144
- h = base_hour + 1
3145
- t = steps_per_hour
3145
+ h = base_hour
3146
+ t = 0
3146
3147
  while t < len(time_line) and bat_timed[t] is not None:
3147
3148
  col = h % data_wrap
3148
- s += (f"\n {hours_time(time_line[t])}" + " " * col * 6) if t == steps_per_hour or col == 0 else ""
3149
+ s += (f"\n {hours_time(time_line[t])}" + " " * col * 6) if t == 0 or col == 0 else ""
3149
3150
  s += f" {bat_timed[t]:5.2f}" if show_data == 2 else f" {bat_timed[t] / capacity * 100:3.0f}%"
3150
3151
  h += 1
3151
3152
  t += steps_per_hour
@@ -3153,8 +3154,8 @@ def charge_needed(forecast=None, update_settings=0, timed_mode=None, show_data=N
3153
3154
  if show_plot > 0:
3154
3155
  print()
3155
3156
  plt.figure(figsize=(figure_width, figure_width/2))
3156
- x_timed = [i for i in range(steps_per_hour, run_time)]
3157
- x_ticks = [i for i in range(steps_per_hour, run_time, steps_per_hour)]
3157
+ x_timed = [i for i in range(0, run_time)]
3158
+ x_ticks = [i for i in range(0, run_time, steps_per_hour)]
3158
3159
  plt.xticks(ticks=x_ticks, labels=[hours_time(time_line[x]) for x in x_ticks], rotation=90, fontsize=8, ha='center')
3159
3160
  if show_plot == 1:
3160
3161
  title = f"Predicted Battery SoC % at {base_time}({charge_message})"
@@ -3278,11 +3279,11 @@ def charge_compare(save=None, v=None, show_data=1, show_plot=3):
3278
3279
  if show_data > 0 and plots.get('SoC') is not None:
3279
3280
  data_wrap = charge_config['data_wrap'] if charge_config.get('data_wrap') is not None else 6
3280
3281
  s = f"\nBattery Energy kWh:" if show_data == 2 else f"\nBattery SoC:"
3281
- h = base_hour + 1
3282
- t = steps_per_hour
3282
+ h = base_hour
3283
+ t = 0
3283
3284
  while t < len(time_line) and bat_timed[t] is not None:
3284
3285
  col = h % data_wrap
3285
- s += (f"\n {hours_time(time_line[t])}" + " " * col * 6) if t == steps_per_hour or col == 0 else ""
3286
+ s += (f"\n {hours_time(time_line[t])}" + " " * col * 6) if t == 0 or col == 0 else ""
3286
3287
  s += f" {plots['SoC'][t]:5.2f}" if show_data == 2 else f" {plots['SoC'][t] / capacity * 100:3.0f}%"
3287
3288
  h += 1
3288
3289
  t += steps_per_hour
@@ -3290,8 +3291,8 @@ def charge_compare(save=None, v=None, show_data=1, show_plot=3):
3290
3291
  if show_plot > 0:
3291
3292
  print()
3292
3293
  plt.figure(figsize=(figure_width, figure_width/2))
3293
- x_timed = [i for i in range(steps_per_hour, run_time)]
3294
- x_ticks = [i for i in range(steps_per_hour, run_time, steps_per_hour)]
3294
+ x_timed = [i for i in range(0, run_time)]
3295
+ x_ticks = [i for i in range(0, run_time, steps_per_hour)]
3295
3296
  plt.xticks(ticks=x_ticks, labels=[hours_time(time_line[x]) for x in x_ticks], rotation=90, fontsize=8, ha='center')
3296
3297
  if show_plot == 1:
3297
3298
  title = f"Predicted Battery SoC % at {base_time}({charge_message})"
foxesscloud/openapi.py CHANGED
@@ -1,7 +1,7 @@
1
1
  ##################################################################################################
2
2
  """
3
3
  Module: Fox ESS Cloud using Open API
4
- Updated: 26 September 2024
4
+ Updated: 27 September 2024
5
5
  By: Tony Matthews
6
6
  """
7
7
  ##################################################################################################
@@ -10,7 +10,7 @@ By: Tony Matthews
10
10
  # ALL RIGHTS ARE RESERVED © Tony Matthews 2024
11
11
  ##################################################################################################
12
12
 
13
- version = "2.5.5"
13
+ version = "2.5.6"
14
14
  print(f"FoxESS-Cloud Open API version {version}")
15
15
 
16
16
  debug_setting = 1
@@ -2134,8 +2134,8 @@ tariff_config = {
2134
2134
  'region': "H", # region code to use for Octopus API
2135
2135
  'update_time': 16.5, # time in hours when tomrow's data can be fetched
2136
2136
  'weighting': None, # weights for weighted average
2137
- 'plunge_price': [1, 10], # plunge price in p/kWh inc VAT over 24 hours from 7am, 7pm
2138
- 'plunge_slots': 6, # number of 30 minute slots to use
2137
+ 'plunge_price': [3, 10], # plunge price in p/kWh inc VAT over 24 hours from 7am, 7pm
2138
+ 'plunge_slots': 8, # number of 30 minute slots to use
2139
2139
  'data_wrap': 6, # prices to show per line
2140
2140
  'show_data': 1, # show pricing data
2141
2141
  'show_plot': 1 # plot pricing data
@@ -2545,7 +2545,7 @@ charge_config = {
2545
2545
  'consumption_days': 3, # number of days to use for average consumption (1-7)
2546
2546
  'consumption_span': 'week', # 'week' = last n days or 'weekday' = last n weekdays
2547
2547
  'use_today': 21.0, # hour when todays consumption and generation can be used
2548
- 'min_hours': 0.25, # minimum charge time in decimal hours
2548
+ 'min_hours': 0.5, # minimum charge time in decimal hours
2549
2549
  'min_kwh': 0.5, # minimum to add in kwh
2550
2550
  'forecast_selection': 1, # 0 = use available forecast / generation, 1 only update settings with forecast
2551
2551
  'annual_consumption': None, # optional annual consumption in kWh
@@ -2642,7 +2642,7 @@ def charge_needed(forecast=None, update_settings=0, timed_mode=None, show_data=N
2642
2642
  for t in times:
2643
2643
  if hour_in(hour_now, t) and update_settings > 0:
2644
2644
  update_settings = 0
2645
- output(f"\nSettings will not be updated during a charge period")
2645
+ output(f"\nSettings will not be updated during a charge period {format_period(t)}")
2646
2646
  time_to_start = round_time(t['start'] - base_hour) * steps_per_hour
2647
2647
  time_to_start += hour_adjustment * steps_per_hour if time_to_start > time_change else 0
2648
2648
  charge_time = round_time(t['end'] - t['start'])
@@ -2933,7 +2933,7 @@ def charge_needed(forecast=None, update_settings=0, timed_mode=None, show_data=N
2933
2933
  kwh_needed = test_charge
2934
2934
  charge_message = "** test charge **"
2935
2935
  # work out charge needed
2936
- if kwh_min > (reserve + kwh_contingency) and kwh_needed < charge_config['min_kwh'] and test_charge is None:
2936
+ if kwh_min > reserve and kwh_needed < charge_config['min_kwh'] and test_charge is None:
2937
2937
  output(f"\nNo charging needed:")
2938
2938
  output(f" SoC now: {current_soc:.0f}% at {hours_time(hour_now)} on {today}")
2939
2939
  charge_message = "no charge needed"
@@ -2953,17 +2953,18 @@ def charge_needed(forecast=None, update_settings=0, timed_mode=None, show_data=N
2953
2953
  output(f" SoC now: {current_soc:.0f}% at {hours_time(hour_now)} on {today}")
2954
2954
  # work out time to add kwh_needed to battery
2955
2955
  charge_rate = charge_power * charge_loss
2956
- hours = round_time(kwh_needed / charge_rate)
2956
+ hours = kwh_needed / charge_rate
2957
2957
  # check if charge time exceeded or charge needed exceeds capacity
2958
- hours_to_full = round_time((capacity - start_residual) / (charge_rate) + 10)
2959
- if hours < charge_config['min_hours']:
2960
- hours = charge_config['min_hours']
2961
- elif hours > charge_time:
2958
+ hours_to_full = (capacity - start_residual) / charge_rate
2959
+ if hours > charge_time:
2962
2960
  hours = charge_time
2963
2961
  elif hours > hours_to_full:
2964
2962
  kwh_shortfall = (hours - hours_to_full) * charge_rate # amount of energy that won't be added
2965
- required = hours_to_full + charge_time * kwh_shortfall / (end_residual - start_residual) # time to recover energy not added
2966
- hours = required if required < charge_time else charge_time
2963
+ required = hours_to_full + charge_time * kwh_shortfall / abs(start_residual - end_residual) # hold time to recover energy not added
2964
+ hours = required if required > hours and required < charge_time else charge_time
2965
+ # round charge time
2966
+ min_hours = charge_config['min_hours']
2967
+ hours = int(hours / min_hours + 0.99) * min_hours
2967
2968
  # rework charge and discharge
2968
2969
  charge_period = get_best_charge_period(start_at, hours)
2969
2970
  charge_offset = round_time(charge_period['start'] - start_at) if charge_period is not None else 0
@@ -3005,11 +3006,11 @@ def charge_needed(forecast=None, update_settings=0, timed_mode=None, show_data=N
3005
3006
  if show_data > 0:
3006
3007
  data_wrap = charge_config['data_wrap'] if charge_config.get('data_wrap') is not None else 6
3007
3008
  s = f"\nBattery Energy kWh:" if show_data == 2 else f"\nBattery SoC:"
3008
- h = base_hour + 1
3009
- t = steps_per_hour
3009
+ h = base_hour
3010
+ t = 0
3010
3011
  while t < len(time_line) and bat_timed[t] is not None:
3011
3012
  col = h % data_wrap
3012
- s += (f"\n {hours_time(time_line[t])}" + " " * col * 6) if t == steps_per_hour or col == 0 else ""
3013
+ s += (f"\n {hours_time(time_line[t])}" + " " * col * 6) if t == 0 or col == 0 else ""
3013
3014
  s += f" {bat_timed[t]:5.2f}" if show_data == 2 else f" {bat_timed[t] / capacity * 100:3.0f}%"
3014
3015
  h += 1
3015
3016
  t += steps_per_hour
@@ -3017,8 +3018,8 @@ def charge_needed(forecast=None, update_settings=0, timed_mode=None, show_data=N
3017
3018
  if show_plot > 0:
3018
3019
  print()
3019
3020
  plt.figure(figsize=(figure_width, figure_width/2))
3020
- x_timed = [i for i in range(steps_per_hour, run_time)]
3021
- x_ticks = [i for i in range(steps_per_hour, run_time, steps_per_hour)]
3021
+ x_timed = [i for i in range(0, run_time)]
3022
+ x_ticks = [i for i in range(0, run_time, steps_per_hour)]
3022
3023
  plt.xticks(ticks=x_ticks, labels=[hours_time(time_line[x]) for x in x_ticks], rotation=90, fontsize=8, ha='center')
3023
3024
  if show_plot == 1:
3024
3025
  title = f"Predicted Battery SoC % at {base_time}({charge_message})"
@@ -3141,11 +3142,11 @@ def charge_compare(save=None, v=None, show_data=1, show_plot=3):
3141
3142
  if show_data > 0 and plots.get('SoC') is not None:
3142
3143
  data_wrap = charge_config['data_wrap'] if charge_config.get('data_wrap') is not None else 6
3143
3144
  s = f"\nBattery Energy kWh:" if show_data == 2 else f"\nBattery SoC:"
3144
- h = base_hour + 1
3145
- t = steps_per_hour
3145
+ h = base_hour
3146
+ t = 0
3146
3147
  while t < len(time_line) and bat_timed[t] is not None:
3147
3148
  col = h % data_wrap
3148
- s += (f"\n {hours_time(time_line[t])}" + " " * col * 6) if t == steps_per_hour or col == 0 else ""
3149
+ s += (f"\n {hours_time(time_line[t])}" + " " * col * 6) if t == 0 or col == 0 else ""
3149
3150
  s += f" {plots['SoC'][t]:5.2f}" if show_data == 2 else f" {plots['SoC'][t] / capacity * 100:3.0f}%"
3150
3151
  h += 1
3151
3152
  t += steps_per_hour
@@ -3153,8 +3154,8 @@ def charge_compare(save=None, v=None, show_data=1, show_plot=3):
3153
3154
  if show_plot > 0:
3154
3155
  print()
3155
3156
  plt.figure(figsize=(figure_width, figure_width/2))
3156
- x_timed = [i for i in range(steps_per_hour, run_time)]
3157
- x_ticks = [i for i in range(steps_per_hour, run_time, steps_per_hour)]
3157
+ x_timed = [i for i in range(0, run_time)]
3158
+ x_ticks = [i for i in range(0, run_time, steps_per_hour)]
3158
3159
  plt.xticks(ticks=x_ticks, labels=[hours_time(time_line[x]) for x in x_ticks], rotation=90, fontsize=8, ha='center')
3159
3160
  if show_plot == 1:
3160
3161
  title = f"Predicted Battery SoC % at {base_time}({charge_message})"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: foxesscloud
3
- Version: 2.5.5
3
+ Version: 2.5.6
4
4
  Summary: library for accessing Fox ESS cloud data using Open API
5
5
  Author-email: Tony Matthews <tony@quasair.co.uk>
6
6
  Project-URL: Homepage, https://github.com/TonyM1958/FoxESS-Cloud
@@ -783,12 +783,16 @@ This setting can be:
783
783
 
784
784
  # Version Info
785
785
 
786
+ 2.5.6<br>
787
+ Change plunge slots to 8 and plungs pricing to [3,10].
788
+ Change min_hours setting in charge_needed to 0.5 (30 minutes) and round up charge times to increments of this.
789
+ Show data and plot starting at t=0.
790
+
786
791
  2.5.5<br>
787
792
  Improve validation of plunge price periods so they don't repeat across days.
788
793
  Correct start and end soc times and values when charging using best Agile time periods.
789
794
  Extend charge times when charge needed exceeds battery capacity.
790
795
 
791
-
792
796
  2.5.4<br>
793
797
  Remove preset 'weighting' that were not used.
794
798
  Update weighting to apply the requested charge duration correctly.
@@ -0,0 +1,7 @@
1
+ foxesscloud/foxesscloud.py,sha256=pvRR5Wjbb0EIfXT59wtG5kGgGANxV-USjOZoPDzwaL4,210980
2
+ foxesscloud/openapi.py,sha256=vj0hP-8IsD30IK5ylCqp9BahvhtgYOIbkgrccbjcaLE,204620
3
+ foxesscloud-2.5.6.dist-info/LICENCE,sha256=-3xv8CElCJV8Bc8PbAsg3iyxMpAK8MoJneM3rXigxqI,1074
4
+ foxesscloud-2.5.6.dist-info/METADATA,sha256=o3Vxj9OGsJayuifK48DkVlv2W2l4Oht8-KIuzrIB6DU,55678
5
+ foxesscloud-2.5.6.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
6
+ foxesscloud-2.5.6.dist-info/top_level.txt,sha256=IWOrKSNZCLU6IDXSX_b4_bqCfbZoWAT4CC0w0Lg7PuU,12
7
+ foxesscloud-2.5.6.dist-info/RECORD,,
@@ -1,7 +0,0 @@
1
- foxesscloud/foxesscloud.py,sha256=biC30gvvQHFTu3CCDhuK6KRGNi2hFbgFxHjSykj-UpE,211062
2
- foxesscloud/openapi.py,sha256=MEzJPleAV8-2gR17e2qmQezVeotiqkHMzeCBDVJOfsY,204698
3
- foxesscloud-2.5.5.dist-info/LICENCE,sha256=-3xv8CElCJV8Bc8PbAsg3iyxMpAK8MoJneM3rXigxqI,1074
4
- foxesscloud-2.5.5.dist-info/METADATA,sha256=kvZCDYUyu7v1OkE_dik_f_4V4OXQTaZKwOYWYFTircU,55462
5
- foxesscloud-2.5.5.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
6
- foxesscloud-2.5.5.dist-info/top_level.txt,sha256=IWOrKSNZCLU6IDXSX_b4_bqCfbZoWAT4CC0w0Lg7PuU,12
7
- foxesscloud-2.5.5.dist-info/RECORD,,