foxesscloud 2.5.3__py3-none-any.whl → 2.5.4__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.
- foxesscloud/foxesscloud.py +31 -34
- foxesscloud/openapi.py +31 -34
- {foxesscloud-2.5.3.dist-info → foxesscloud-2.5.4.dist-info}/METADATA +10 -6
- foxesscloud-2.5.4.dist-info/RECORD +7 -0
- foxesscloud-2.5.3.dist-info/RECORD +0 -7
- {foxesscloud-2.5.3.dist-info → foxesscloud-2.5.4.dist-info}/LICENCE +0 -0
- {foxesscloud-2.5.3.dist-info → foxesscloud-2.5.4.dist-info}/WHEEL +0 -0
- {foxesscloud-2.5.3.dist-info → foxesscloud-2.5.4.dist-info}/top_level.txt +0 -0
foxesscloud/foxesscloud.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
##################################################################################################
|
2
2
|
"""
|
3
3
|
Module: Fox ESS Cloud
|
4
|
-
Updated:
|
4
|
+
Updated: 25 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.
|
13
|
+
version = "1.6.6"
|
14
14
|
print(f"FoxESS-Cloud version {version}")
|
15
15
|
|
16
16
|
debug_setting = 1
|
@@ -2264,11 +2264,6 @@ regions = {'A':'Eastern England', 'B':'East Midlands', 'C':'London', 'D':'Mersey
|
|
2264
2264
|
'J':'South Eastern England', 'K':'Southern Wales', 'L':'South Western England', 'M':'Yorkshire', 'N':'Southern Scotland', 'P':'Northern Scotland'}
|
2265
2265
|
|
2266
2266
|
|
2267
|
-
# preset weightings for average 30 minute pricing over charging duration:
|
2268
|
-
front_loaded = [1.0, 0.9, 0.8, 0.7, 0.6, 0.5] # 3 hour average, front loaded
|
2269
|
-
first_hour = [1.0, 1.0] # lowest average price for first hour
|
2270
|
-
|
2271
|
-
|
2272
2267
|
tariff_config = {
|
2273
2268
|
'product': "AGILE-24-04-03", # product code to use for Octopus API
|
2274
2269
|
'region': "H", # region code to use for Octopus API
|
@@ -2277,7 +2272,7 @@ tariff_config = {
|
|
2277
2272
|
'plunge_price': [1, 10], # plunge price in p/kWh inc VAT over 24 hours from 7am, 7pm
|
2278
2273
|
'plunge_slots': 6, # number of 30 minute slots to use
|
2279
2274
|
'data_wrap': 6, # prices to show per line
|
2280
|
-
'show_data':
|
2275
|
+
'show_data': 1, # show pricing data
|
2281
2276
|
'show_plot': 1 # plot pricing data
|
2282
2277
|
}
|
2283
2278
|
|
@@ -2361,13 +2356,13 @@ def get_agile_times(tariff=agile_octopus, d=None):
|
|
2361
2356
|
# show the results
|
2362
2357
|
if tariff_config['show_data'] > 0:
|
2363
2358
|
data_wrap = tariff_config['data_wrap'] if tariff_config.get('data_wrap') is not None else 6
|
2364
|
-
|
2365
|
-
s = f"\
|
2359
|
+
col = (now.hour * 2) % data_wrap
|
2360
|
+
s = f"\nPrice p/kWh inc VAT on {today}:"
|
2366
2361
|
for i in range(0, len(prices)):
|
2367
|
-
s += "\n" if i
|
2368
|
-
s += f" {prices[i]['
|
2369
|
-
|
2370
|
-
output(s
|
2362
|
+
s += (f"\n {prices[i]['time']} " + " " * col * 6) if i == 0 or col == 0 else ""
|
2363
|
+
s += f" {prices[i]['price']:4.1f}"
|
2364
|
+
col = (col + 1) % data_wrap
|
2365
|
+
output(s)
|
2371
2366
|
if tariff_config['show_plot'] > 0:
|
2372
2367
|
plt.figure(figsize=(figure_width, figure_width/2))
|
2373
2368
|
x_timed = [i for i in range(0, len(prices))]
|
@@ -2394,7 +2389,8 @@ def get_best_charge_period(start, duration):
|
|
2394
2389
|
key = [k for k in ['off_peak1', 'off_peak2', 'off_peak3', 'off_peak4'] if hour_in(start, tariff.get(k))]
|
2395
2390
|
key = key[0] if len(key) > 0 else None
|
2396
2391
|
end = tariff[key]['end'] if key is not None else round_time(start + duration)
|
2397
|
-
span = int(duration * 2 + 0.99)
|
2392
|
+
span = int(duration * 2 + 0.99) # number of slots needed for charging
|
2393
|
+
last = (duration * 2) % 1 # amount of last slot used for charging
|
2398
2394
|
coverage = max([round_time(end - start), duration])
|
2399
2395
|
period = {'start': start, 'end': round_time(start + coverage)}
|
2400
2396
|
prices = tariff['agile']['prices']
|
@@ -2403,13 +2399,14 @@ def get_best_charge_period(start, duration):
|
|
2403
2399
|
return None
|
2404
2400
|
elif len(slots) == 1:
|
2405
2401
|
best = slots
|
2406
|
-
price = prices[slots[0]]['price']
|
2407
2402
|
best_start = start
|
2403
|
+
price = prices[best[0]]['price']
|
2408
2404
|
else:
|
2409
2405
|
# best charge time for duration
|
2410
2406
|
weighting = tariff_config.get('weighting')
|
2411
2407
|
times = []
|
2412
|
-
weights = ([1.0] * span) if weighting is None else (weighting + [1.0] * span)[:span]
|
2408
|
+
weights = ([1.0] * (span)) if weighting is None else (weighting + [1.0] * span)[:span]
|
2409
|
+
weights[-1] *= last if last > 0.0 else 1.0
|
2413
2410
|
best = None
|
2414
2411
|
price = None
|
2415
2412
|
for i in range(0, len(slots) - span + 1):
|
@@ -2964,7 +2961,7 @@ def charge_needed(forecast=None, update_settings=0, timed_mode=None, show_data=N
|
|
2964
2961
|
solcast_value = fsolcast.daily[forecast_day]['kwh']
|
2965
2962
|
solcast_timed = forecast_value_timed(fsolcast, today, tomorrow, base_hour, run_time, time_offset)
|
2966
2963
|
solcast_from = time_hours(fsolcast.daily[today]['from']) if fsolcast.daily[today].get('from') is not None else 0
|
2967
|
-
output(f"\nSolcast
|
2964
|
+
output(f"\nSolcast: {tomorrow} {fsolcast.daily[tomorrow]['kwh']:.1f}kWh") # get forecast.solar data and produce time line
|
2968
2965
|
solar_value = None
|
2969
2966
|
solar_profile = None
|
2970
2967
|
if forecast is None and solar_arrays is not None and (system_time.hour in forecast_times or run_after == 0):
|
@@ -2972,7 +2969,7 @@ def charge_needed(forecast=None, update_settings=0, timed_mode=None, show_data=N
|
|
2972
2969
|
if fsolar is not None and hasattr(fsolar, 'daily') and fsolar.daily.get(forecast_day) is not None:
|
2973
2970
|
solar_value = fsolar.daily[forecast_day]['kwh']
|
2974
2971
|
solar_timed = forecast_value_timed(fsolar, today, tomorrow, base_hour, run_time, 0)
|
2975
|
-
output(f"\nSolar
|
2972
|
+
output(f"\nSolar: {tomorrow} {fsolar.daily[tomorrow]['kwh']:.1f}kWh")
|
2976
2973
|
if solcast_value is None and solar_value is None and debug_setting > 1:
|
2977
2974
|
output(f"\nNo forecasts available at this time")
|
2978
2975
|
# get generation data
|
@@ -3073,7 +3070,8 @@ def charge_needed(forecast=None, update_settings=0, timed_mode=None, show_data=N
|
|
3073
3070
|
charge_message = "** test charge **"
|
3074
3071
|
# work out charge needed
|
3075
3072
|
if kwh_min > (reserve + kwh_contingency) and kwh_needed < charge_config['min_kwh']:
|
3076
|
-
output(f"\nNo charging needed
|
3073
|
+
output(f"\nNo charging needed:")
|
3074
|
+
output(f" SoC now: {current_soc:.0f}% at {hours_time(hour_now)} on {today}")
|
3077
3075
|
charge_message = "no charge needed"
|
3078
3076
|
kwh_needed = 0.0
|
3079
3077
|
hours = 0.0
|
@@ -3086,8 +3084,9 @@ def charge_needed(forecast=None, update_settings=0, timed_mode=None, show_data=N
|
|
3086
3084
|
work_mode_timed[t]['min_soc'] = start_soc
|
3087
3085
|
else:
|
3088
3086
|
if test_charge is None:
|
3089
|
-
output(f"\nCharge needed {kwh_needed:.2f}kWh
|
3087
|
+
output(f"\nCharge needed {kwh_needed:.2f}kWh:")
|
3090
3088
|
charge_message = "with charge added"
|
3089
|
+
output(f" SoC now: {current_soc:.0f}% at {hours_time(hour_now)} on {today}")
|
3091
3090
|
output(f" Start SoC: {start_residual / capacity * 100:.0f}% at {hours_time(adjusted_hour(time_to_start, time_line))} ({start_residual:.2f}kWh)")
|
3092
3091
|
# work out time to add kwh_needed to battery
|
3093
3092
|
taper_time = 10/60 if (start_residual + kwh_needed) >= (capacity * 0.95) else 0
|
@@ -3136,17 +3135,16 @@ def charge_needed(forecast=None, update_settings=0, timed_mode=None, show_data=N
|
|
3136
3135
|
output(f" PV cover: {expected / consumption * 100:.0f}% ({expected:.1f}/{consumption:.1f})")
|
3137
3136
|
if show_data > 0:
|
3138
3137
|
data_wrap = charge_config['data_wrap'] if charge_config.get('data_wrap') is not None else 6
|
3139
|
-
s = f"\nBattery Energy kWh
|
3138
|
+
s = f"\nBattery Energy kWh:" if show_data == 2 else f"\nBattery SoC:"
|
3140
3139
|
h = base_hour + 1
|
3141
3140
|
t = steps_per_hour
|
3142
|
-
s += " " * (14 if show_data == 2 else 13) * (h % data_wrap)
|
3143
3141
|
while t < len(time_line) and bat_timed[t] is not None:
|
3144
|
-
|
3145
|
-
s += f" {hours_time(time_line[t])}"
|
3146
|
-
s += f" {bat_timed[t]:5.2f}" if show_data == 2 else f"
|
3142
|
+
col = h % data_wrap
|
3143
|
+
s += (f"\n {hours_time(time_line[t])}" + " " * col * 6) if t == steps_per_hour or col == 0 else ""
|
3144
|
+
s += f" {bat_timed[t]:5.2f}" if show_data == 2 else f" {bat_timed[t] / capacity * 100:3.0f}%"
|
3147
3145
|
h += 1
|
3148
3146
|
t += steps_per_hour
|
3149
|
-
output(s
|
3147
|
+
output(s)
|
3150
3148
|
if show_plot > 0:
|
3151
3149
|
print()
|
3152
3150
|
plt.figure(figsize=(figure_width, figure_width/2))
|
@@ -3274,17 +3272,16 @@ def charge_compare(save=None, v=None, show_data=1, show_plot=3):
|
|
3274
3272
|
plots[v][i] = plots[v][i] / count[v][i] if count[v][i] > 0 else None
|
3275
3273
|
if show_data > 0 and plots.get('SoC') is not None:
|
3276
3274
|
data_wrap = charge_config['data_wrap'] if charge_config.get('data_wrap') is not None else 6
|
3277
|
-
s = f"\nBattery Energy kWh
|
3275
|
+
s = f"\nBattery Energy kWh:" if show_data == 2 else f"\nBattery SoC:"
|
3278
3276
|
h = base_hour + 1
|
3279
3277
|
t = steps_per_hour
|
3280
|
-
|
3281
|
-
|
3282
|
-
s += "\n" if t
|
3283
|
-
s += f" {
|
3284
|
-
s += f" {plots['SoC'][t]:5.2f}" if show_data == 2 else f" {plots['SoC'][t] / capacity * 100:3.0f}%,"
|
3278
|
+
while t < len(time_line) and bat_timed[t] is not None:
|
3279
|
+
col = h % data_wrap
|
3280
|
+
s += (f"\n {hours_time(time_line[t])}" + " " * col * 6) if t == steps_per_hour or col == 0 else ""
|
3281
|
+
s += f" {plots['SoC'][t]:5.2f}" if show_data == 2 else f" {plots['SoC'][t] / capacity * 100:3.0f}%"
|
3285
3282
|
h += 1
|
3286
3283
|
t += steps_per_hour
|
3287
|
-
print(s
|
3284
|
+
print(s)
|
3288
3285
|
if show_plot > 0:
|
3289
3286
|
print()
|
3290
3287
|
plt.figure(figsize=(figure_width, figure_width/2))
|
foxesscloud/openapi.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
##################################################################################################
|
2
2
|
"""
|
3
3
|
Module: Fox ESS Cloud using Open API
|
4
|
-
Updated:
|
4
|
+
Updated: 25 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.
|
13
|
+
version = "2.5.4"
|
14
14
|
print(f"FoxESS-Cloud Open API version {version}")
|
15
15
|
|
16
16
|
debug_setting = 1
|
@@ -2128,11 +2128,6 @@ regions = {'A':'Eastern England', 'B':'East Midlands', 'C':'London', 'D':'Mersey
|
|
2128
2128
|
'J':'South Eastern England', 'K':'Southern Wales', 'L':'South Western England', 'M':'Yorkshire', 'N':'Southern Scotland', 'P':'Northern Scotland'}
|
2129
2129
|
|
2130
2130
|
|
2131
|
-
# preset weightings for average 30 minute pricing over charging duration:
|
2132
|
-
front_loaded = [1.0, 0.9, 0.8, 0.7, 0.6, 0.5] # 3 hour average, front loaded
|
2133
|
-
first_hour = [1.0, 1.0] # lowest average price for first hour
|
2134
|
-
|
2135
|
-
|
2136
2131
|
tariff_config = {
|
2137
2132
|
'product': "AGILE-24-04-03", # product code to use for Octopus API
|
2138
2133
|
'region': "H", # region code to use for Octopus API
|
@@ -2141,7 +2136,7 @@ tariff_config = {
|
|
2141
2136
|
'plunge_price': [1, 10], # plunge price in p/kWh inc VAT over 24 hours from 7am, 7pm
|
2142
2137
|
'plunge_slots': 6, # number of 30 minute slots to use
|
2143
2138
|
'data_wrap': 6, # prices to show per line
|
2144
|
-
'show_data':
|
2139
|
+
'show_data': 1, # show pricing data
|
2145
2140
|
'show_plot': 1 # plot pricing data
|
2146
2141
|
}
|
2147
2142
|
|
@@ -2225,13 +2220,13 @@ def get_agile_times(tariff=agile_octopus, d=None):
|
|
2225
2220
|
# show the results
|
2226
2221
|
if tariff_config['show_data'] > 0:
|
2227
2222
|
data_wrap = tariff_config['data_wrap'] if tariff_config.get('data_wrap') is not None else 6
|
2228
|
-
|
2229
|
-
s = f"\
|
2223
|
+
col = (now.hour * 2) % data_wrap
|
2224
|
+
s = f"\nPrice p/kWh inc VAT on {today}:"
|
2230
2225
|
for i in range(0, len(prices)):
|
2231
|
-
s += "\n" if i
|
2232
|
-
s += f" {prices[i]['
|
2233
|
-
|
2234
|
-
output(s
|
2226
|
+
s += (f"\n {prices[i]['time']} " + " " * col * 6) if i == 0 or col == 0 else ""
|
2227
|
+
s += f" {prices[i]['price']:4.1f}"
|
2228
|
+
col = (col + 1) % data_wrap
|
2229
|
+
output(s)
|
2235
2230
|
if tariff_config['show_plot'] > 0:
|
2236
2231
|
plt.figure(figsize=(figure_width, figure_width/2))
|
2237
2232
|
x_timed = [i for i in range(0, len(prices))]
|
@@ -2258,7 +2253,8 @@ def get_best_charge_period(start, duration):
|
|
2258
2253
|
key = [k for k in ['off_peak1', 'off_peak2', 'off_peak3', 'off_peak4'] if hour_in(start, tariff.get(k))]
|
2259
2254
|
key = key[0] if len(key) > 0 else None
|
2260
2255
|
end = tariff[key]['end'] if key is not None else round_time(start + duration)
|
2261
|
-
span = int(duration * 2 + 0.99)
|
2256
|
+
span = int(duration * 2 + 0.99) # number of slots needed for charging
|
2257
|
+
last = (duration * 2) % 1 # amount of last slot used for charging
|
2262
2258
|
coverage = max([round_time(end - start), duration])
|
2263
2259
|
period = {'start': start, 'end': round_time(start + coverage)}
|
2264
2260
|
prices = tariff['agile']['prices']
|
@@ -2267,13 +2263,14 @@ def get_best_charge_period(start, duration):
|
|
2267
2263
|
return None
|
2268
2264
|
elif len(slots) == 1:
|
2269
2265
|
best = slots
|
2270
|
-
price = prices[slots[0]]['price']
|
2271
2266
|
best_start = start
|
2267
|
+
price = prices[best[0]]['price']
|
2272
2268
|
else:
|
2273
2269
|
# best charge time for duration
|
2274
2270
|
weighting = tariff_config.get('weighting')
|
2275
2271
|
times = []
|
2276
|
-
weights = ([1.0] * span) if weighting is None else (weighting + [1.0] * span)[:span]
|
2272
|
+
weights = ([1.0] * (span)) if weighting is None else (weighting + [1.0] * span)[:span]
|
2273
|
+
weights[-1] *= last if last > 0.0 else 1.0
|
2277
2274
|
best = None
|
2278
2275
|
price = None
|
2279
2276
|
for i in range(0, len(slots) - span + 1):
|
@@ -2827,7 +2824,7 @@ def charge_needed(forecast=None, update_settings=0, timed_mode=None, show_data=N
|
|
2827
2824
|
if fsolcast is not None and hasattr(fsolcast, 'daily') and fsolcast.daily.get(forecast_day) is not None:
|
2828
2825
|
solcast_value = fsolcast.daily[forecast_day]['kwh']
|
2829
2826
|
solcast_timed = forecast_value_timed(fsolcast, today, tomorrow, base_hour, run_time, time_offset)
|
2830
|
-
output(f"\nSolcast
|
2827
|
+
output(f"\nSolcast: {tomorrow} {fsolcast.daily[tomorrow]['kwh']:.1f}kWh")
|
2831
2828
|
# get forecast.solar data and produce time line
|
2832
2829
|
solar_value = None
|
2833
2830
|
solar_profile = None
|
@@ -2836,7 +2833,7 @@ def charge_needed(forecast=None, update_settings=0, timed_mode=None, show_data=N
|
|
2836
2833
|
if fsolar is not None and hasattr(fsolar, 'daily') and fsolar.daily.get(forecast_day) is not None:
|
2837
2834
|
solar_value = fsolar.daily[forecast_day]['kwh']
|
2838
2835
|
solar_timed = forecast_value_timed(fsolar, today, tomorrow, base_hour, run_time, 0)
|
2839
|
-
output(f"\nSolar
|
2836
|
+
output(f"\nSolar: {tomorrow} {fsolar.daily[tomorrow]['kwh']:.1f}kWh")
|
2840
2837
|
if solcast_value is None and solar_value is None and debug_setting > 1:
|
2841
2838
|
output(f"\nNo forecasts available at this time")
|
2842
2839
|
# get generation data
|
@@ -2937,7 +2934,8 @@ def charge_needed(forecast=None, update_settings=0, timed_mode=None, show_data=N
|
|
2937
2934
|
charge_message = "** test charge **"
|
2938
2935
|
# work out charge needed
|
2939
2936
|
if kwh_min > (reserve + kwh_contingency) and kwh_needed < charge_config['min_kwh'] and full_charge is None and test_charge is None and force_charge != 2:
|
2940
|
-
output(f"\nNo charging needed
|
2937
|
+
output(f"\nNo charging needed:")
|
2938
|
+
output(f" SoC now: {current_soc:.0f}% at {hours_time(hour_now)} on {today}")
|
2941
2939
|
charge_message = "no charge needed"
|
2942
2940
|
kwh_needed = 0.0
|
2943
2941
|
hours = 0.0
|
@@ -2950,8 +2948,9 @@ def charge_needed(forecast=None, update_settings=0, timed_mode=None, show_data=N
|
|
2950
2948
|
work_mode_timed[t]['min_soc'] = start_soc
|
2951
2949
|
else:
|
2952
2950
|
if test_charge is None:
|
2953
|
-
output(f"\nCharge needed {kwh_needed:.2f}kWh
|
2951
|
+
output(f"\nCharge needed {kwh_needed:.2f}kWh:")
|
2954
2952
|
charge_message = "with charge added"
|
2953
|
+
output(f" SoC now: {current_soc:.0f}% at {hours_time(hour_now)} on {today}")
|
2955
2954
|
output(f" Start SoC: {start_residual / capacity * 100:.0f}% at {hours_time(adjusted_hour(time_to_start, time_line))} ({start_residual:.2f}kWh)")
|
2956
2955
|
# work out time to add kwh_needed to battery
|
2957
2956
|
taper_time = 10/60 if (start_residual + kwh_needed) >= (capacity * 0.95) else 0
|
@@ -3000,17 +2999,16 @@ def charge_needed(forecast=None, update_settings=0, timed_mode=None, show_data=N
|
|
3000
2999
|
output(f" PV cover: {expected / consumption * 100:.0f}% ({expected:.1f}/{consumption:.1f})")
|
3001
3000
|
if show_data > 0:
|
3002
3001
|
data_wrap = charge_config['data_wrap'] if charge_config.get('data_wrap') is not None else 6
|
3003
|
-
s = f"\nBattery Energy kWh
|
3002
|
+
s = f"\nBattery Energy kWh:" if show_data == 2 else f"\nBattery SoC:"
|
3004
3003
|
h = base_hour + 1
|
3005
3004
|
t = steps_per_hour
|
3006
|
-
s += " " * (14 if show_data == 2 else 13) * (h % data_wrap)
|
3007
3005
|
while t < len(time_line) and bat_timed[t] is not None:
|
3008
|
-
|
3009
|
-
s += f" {hours_time(time_line[t])}"
|
3010
|
-
s += f" {bat_timed[t]:5.2f}" if show_data == 2 else f"
|
3006
|
+
col = h % data_wrap
|
3007
|
+
s += (f"\n {hours_time(time_line[t])}" + " " * col * 6) if t == steps_per_hour or col == 0 else ""
|
3008
|
+
s += f" {bat_timed[t]:5.2f}" if show_data == 2 else f" {bat_timed[t] / capacity * 100:3.0f}%"
|
3011
3009
|
h += 1
|
3012
3010
|
t += steps_per_hour
|
3013
|
-
output(s
|
3011
|
+
output(s)
|
3014
3012
|
if show_plot > 0:
|
3015
3013
|
print()
|
3016
3014
|
plt.figure(figsize=(figure_width, figure_width/2))
|
@@ -3137,17 +3135,16 @@ def charge_compare(save=None, v=None, show_data=1, show_plot=3):
|
|
3137
3135
|
plots[v][i] = plots[v][i] / count[v][i] if count[v][i] > 0 else None
|
3138
3136
|
if show_data > 0 and plots.get('SoC') is not None:
|
3139
3137
|
data_wrap = charge_config['data_wrap'] if charge_config.get('data_wrap') is not None else 6
|
3140
|
-
s = f"\nBattery Energy kWh
|
3138
|
+
s = f"\nBattery Energy kWh:" if show_data == 2 else f"\nBattery SoC:"
|
3141
3139
|
h = base_hour + 1
|
3142
3140
|
t = steps_per_hour
|
3143
|
-
|
3144
|
-
|
3145
|
-
s += "\n" if t
|
3146
|
-
s += f" {
|
3147
|
-
s += f" {plots['SoC'][t]:5.2f}" if show_data == 2 else f" {plots['SoC'][t] / capacity * 100:3.0f}%,"
|
3141
|
+
while t < len(time_line) and bat_timed[t] is not None:
|
3142
|
+
col = h % data_wrap
|
3143
|
+
s += (f"\n {hours_time(time_line[t])}" + " " * col * 6) if t == steps_per_hour or col == 0 else ""
|
3144
|
+
s += f" {plots['SoC'][t]:5.2f}" if show_data == 2 else f" {plots['SoC'][t] / capacity * 100:3.0f}%"
|
3148
3145
|
h += 1
|
3149
3146
|
t += steps_per_hour
|
3150
|
-
print(s
|
3147
|
+
print(s)
|
3151
3148
|
if show_plot > 0:
|
3152
3149
|
print()
|
3153
3150
|
plt.figure(figsize=(figure_width, figure_width/2))
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: foxesscloud
|
3
|
-
Version: 2.5.
|
3
|
+
Version: 2.5.4
|
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
|
@@ -528,11 +528,11 @@ This gets the latest 30 minute pricing and uses this to work out the best off pe
|
|
528
528
|
+ forecast_times: a list of times when a forecast can be obtained from Solcast / forecast.solar, aligned with the host system time
|
529
529
|
+ strategy: an optional list of times and work modes (see below)
|
530
530
|
+ update: optional, 1 (the default) sets the current tariff to Agile Octopus. Setting to 0 does not change the current tariff
|
531
|
-
+ weighting: optional, default is None (see below)
|
531
|
+
+ weighting: optional, default is None / flat (see below)
|
532
532
|
+ time_shift: optional system time shift in hours. The default is for system time to be UTC and to apply the current day light saving time (e.g. GMT/BST)
|
533
533
|
+ plunge_price: list of prices in p/kWh when plunge pricing is used (see below). The default is [0, 5].
|
534
534
|
+ plunge_slots: the number of 30 minute slots to use for plunge pricing. The default is 6, allowing up to 3 hours.
|
535
|
-
+ show_data: show 30 minute Agile pricing data. Default is
|
535
|
+
+ show_data: show 30 minute Agile pricing data. Default is 1.
|
536
536
|
+ show_plot: plot 30 minute Agile pricing data. Default is 1.
|
537
537
|
|
538
538
|
Product codes include:
|
@@ -562,9 +562,7 @@ Region codes include:
|
|
562
562
|
Pricing for tomorrow is updated around 5pm each day. If run before this time, prices from yesterday are used. By default, prices for tomorrow are fetched after 5pm. The setting for this is:
|
563
563
|
+ f.agile_update_time = 17
|
564
564
|
|
565
|
-
The best charging period is determined based on the weighted average of the 30 minute prices over the duration. The default is flat (all prices are weighted equally). You can
|
566
|
-
+ f.front_loaded: [1.0, 0.9, 0.8, 0.7, 0.6, 0.5]
|
567
|
-
+ f.first_hour: [1.0, 1.0]
|
565
|
+
The best charging period is determined based on the weighted average of the 30 minute prices over the duration. The default is flat (all prices are weighted equally, except the last slot, which is pro rata to the charge duration used). You can over-ride the default weighting by providing a list of 30 minute values to apply.
|
568
566
|
|
569
567
|
set_tariff() can configure multiple off-peak and peak periods for any tariff using the 'times' parameter. Times is a list of tuples:
|
570
568
|
+ containing values for key, 'start', 'end' and optional 'force'.
|
@@ -785,6 +783,12 @@ This setting can be:
|
|
785
783
|
|
786
784
|
# Version Info
|
787
785
|
|
786
|
+
2.5.4<br>
|
787
|
+
Remove preset 'weighting' that were not used.
|
788
|
+
Update weighting to apply the requested charge duration correctly.
|
789
|
+
Reformat price and SoC tables to reduce wrapping and make them easier to read on small screens.
|
790
|
+
Change default for set_tariff() to show Agile 30 minute prices.
|
791
|
+
|
788
792
|
2.5.3<br>
|
789
793
|
Reverted change to allow updates during a charge period to avoid removing charge in progress.
|
790
794
|
Update contingency and show how this relates to battery SoC.
|
@@ -0,0 +1,7 @@
|
|
1
|
+
foxesscloud/foxesscloud.py,sha256=elLQc40Zt8QkjmiOhZ2gNe-Wqp5NE38LgnflHpUQMmc,210693
|
2
|
+
foxesscloud/openapi.py,sha256=aBVMx8eyh5WRbUQTiAFHh6SDNiKeksBplODIO1x3L-k,204407
|
3
|
+
foxesscloud-2.5.4.dist-info/LICENCE,sha256=-3xv8CElCJV8Bc8PbAsg3iyxMpAK8MoJneM3rXigxqI,1074
|
4
|
+
foxesscloud-2.5.4.dist-info/METADATA,sha256=KXfCjbfLt8t5jjQbLW_-7UJyoKBvskga5X0sxMkzxmk,55214
|
5
|
+
foxesscloud-2.5.4.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
6
|
+
foxesscloud-2.5.4.dist-info/top_level.txt,sha256=IWOrKSNZCLU6IDXSX_b4_bqCfbZoWAT4CC0w0Lg7PuU,12
|
7
|
+
foxesscloud-2.5.4.dist-info/RECORD,,
|
@@ -1,7 +0,0 @@
|
|
1
|
-
foxesscloud/foxesscloud.py,sha256=LkRglTcW4sAVPRgXxkhqyt7fcDcDk00ui_nnsdSU51Y,210817
|
2
|
-
foxesscloud/openapi.py,sha256=2hyHrGpYNUQybmKCLRZmujW8CfnyS0bjKdxZwPpkjCU,204531
|
3
|
-
foxesscloud-2.5.3.dist-info/LICENCE,sha256=-3xv8CElCJV8Bc8PbAsg3iyxMpAK8MoJneM3rXigxqI,1074
|
4
|
-
foxesscloud-2.5.3.dist-info/METADATA,sha256=iXU6pABWNwPSUXW4-NrJWCiy3ZecYsiJPIKNAamHR-g,54937
|
5
|
-
foxesscloud-2.5.3.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
6
|
-
foxesscloud-2.5.3.dist-info/top_level.txt,sha256=IWOrKSNZCLU6IDXSX_b4_bqCfbZoWAT4CC0w0Lg7PuU,12
|
7
|
-
foxesscloud-2.5.3.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|