foxesscloud 2.7.6__tar.gz → 2.7.9__tar.gz

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,4 +1,4 @@
1
- Copyright (c) 2023 Tony Matthews
1
+ Copyright (c) 2023-2025 Tony Matthews
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  of this software and associated documentation files (the "Software"), to deal
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: foxesscloud
3
- Version: 2.7.6
3
+ Version: 2.7.9
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
@@ -358,7 +358,9 @@ All the parameters are optional:
358
358
 
359
359
  charge_needed() uses a number of models to better estimate the state of the battery.
360
360
 
361
- **Manual Consumption:** You can provide your 'annual_consumption' in kWh e.g. 5500. This figure is factored down to a daily consumption by dividing by 365 and applying **f.seasonality**. This normally decreases consumption in the summer and increases it in winter. Seasonality is a list of weightings by month for Jan, Feb, Mar, Apr etc. Preset lists are 'f.high_seasonality' (recommend where electric heating is ued), 'f.medium_seasonality' (default) amd 'f.no_seasonality' (all months the same). The daily consumption is profiled by hour using **f.daily_consumption**. This maps your consumption for a day to the hours when more or less energy is consumed. It is a list of 24 values for the times 00, 01, 02, 03 .. 23. Preset lists are 'f.high_profile' (larger peaks at 8am and 6pm), 'f.medium_profile' (default, more balanced) and 'f.no_profile' (flat).
361
+ **Manual Consumption:** You can provide your expected 'consumption' in kWh e.g. 15. This figure is used directly.
362
+
363
+ **Annual Consumption:** You can provide your 'annual_consumption' in kWh e.g. 5500. This figure is factored down to a daily consumption by dividing by 365 and applying **f.seasonality**. This normally decreases consumption in the summer and increases it in winter. Seasonality is a list of weightings by month for Jan, Feb, Mar, Apr etc. Preset lists are 'f.high_seasonality' (recommend where electric heating is ued), 'f.medium_seasonality' (default) amd 'f.no_seasonality' (all months the same). The daily consumption is profiled by hour using **f.daily_consumption**. This maps your consumption for a day to the hours when more or less energy is consumed. It is a list of 24 values for the times 00, 01, 02, 03 .. 23. Preset lists are 'f.high_profile' (larger peaks at 8am and 6pm), 'f.medium_profile' (default, more balanced) and 'f.no_profile' (flat).
362
364
 
363
365
  **Historic Consumption:** If annual_consumption is not provided, your consumption history is used. By default, this looks at your average consumption for the last 3 days using the load power reported by your inverter. For systems with multiple inverters where CT2 is not connected, the load power may not be correct. For this and other cases where you want to set your consumption, provide your annual_consumption.
364
366
 
@@ -629,7 +631,7 @@ f.get_pvoutput(d, tou)
629
631
  ```
630
632
 
631
633
  + d is the date or a list of dates, to get data for. The default is yesterday.
632
- + tou: optional, setting tou=1 uploads data with time of use. The default, tou=0 does not split data and is more accurate.
634
+ + tou: the default, tou=0, does not split data and is more accurate. Setting tou=1 uploads data with time of use. Setting tou=2 integrates all values and allows set_pvoutput() to work with pv inverters that do not provide energy stats.
633
635
  + setting integrate_load_power to 1 will calculate load energy by integrating the load power instead of using data from Fox. This tries to overcome the limitation where the inverter does not track load power / energy correctly when there is secondary generation. When set to 0 (default), the Fox load energy is used.
634
636
 
635
637
  You can copy and paste the output data to the pvoutput data CSV Loader, using the following settings:
@@ -652,7 +654,7 @@ f.set_pvoutput(d, system_id, tou, push, run_after)
652
654
 
653
655
  + d is optional and is the date, or a list of dates, to upload
654
656
  + system_id is optional and allow you to select where data is uploaded to (where you have more than 1 registered system)
655
- + tou: optional, setting tou=1 uploads data with time of use. The default, tou=0 does not split data and is more accurate
657
+ + tou: the default, tou=0, does not split data and is more accurate. Setting tou=1 uploads data with time of use. Setting tou=2 integrates all values and allows set_pvoutput() to work with pv inverters that do not provide energy stats.
656
658
  + push: optional. 0 = do not sent to pushover, 1 = send summary to pushover, 2 = send first day summary only
657
659
  + run_after: optional. Only generate data on or after this hour. Default 0.
658
660
 
@@ -807,6 +809,17 @@ This setting can be:
807
809
 
808
810
  # Version Info
809
811
 
812
+ 2.7.9<br>
813
+ Add 'consumption' input for charge_needed().
814
+ Avoid throwing exception in battery_info() if there is no capacity info.
815
+
816
+ 2.7.8<br>
817
+ Update the battery_params charge rate table to 2025 values.
818
+
819
+ 2.7.7<br>
820
+ Updates to get_pvoutput() to support solar inverters that don't provide grid energy by setting tou=2.
821
+ Default tariff changed to None.
822
+
810
823
  2.7.6<br>
811
824
  Updates to support F, G, R and S series inverters.
812
825
  Updates to set_named_settings() to load metadata if not already done and save new value.
@@ -1,17 +1,3 @@
1
- Metadata-Version: 2.1
2
- Name: foxesscloud
3
- Version: 2.7.6
4
- Summary: library for accessing Fox ESS cloud data using Open API
5
- Author-email: Tony Matthews <tony@quasair.co.uk>
6
- Project-URL: Homepage, https://github.com/TonyM1958/FoxESS-Cloud
7
- Project-URL: Bug Tracker, https://github.com/TonyM1958/FoxESS-Cloud/issues
8
- Classifier: Programming Language :: Python :: 3
9
- Classifier: License :: OSI Approved :: MIT License
10
- Classifier: Operating System :: OS Independent
11
- Requires-Python: >=3.7
12
- Description-Content-Type: text/markdown
13
- License-File: LICENCE
14
-
15
1
  # FoxESS-Cloud
16
2
 
17
3
  <a href="https://www.buymeacoffee.com/tonym1958" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee" height="41" width="174" align="right"></a>
@@ -358,7 +344,9 @@ All the parameters are optional:
358
344
 
359
345
  charge_needed() uses a number of models to better estimate the state of the battery.
360
346
 
361
- **Manual Consumption:** You can provide your 'annual_consumption' in kWh e.g. 5500. This figure is factored down to a daily consumption by dividing by 365 and applying **f.seasonality**. This normally decreases consumption in the summer and increases it in winter. Seasonality is a list of weightings by month for Jan, Feb, Mar, Apr etc. Preset lists are 'f.high_seasonality' (recommend where electric heating is ued), 'f.medium_seasonality' (default) amd 'f.no_seasonality' (all months the same). The daily consumption is profiled by hour using **f.daily_consumption**. This maps your consumption for a day to the hours when more or less energy is consumed. It is a list of 24 values for the times 00, 01, 02, 03 .. 23. Preset lists are 'f.high_profile' (larger peaks at 8am and 6pm), 'f.medium_profile' (default, more balanced) and 'f.no_profile' (flat).
347
+ **Manual Consumption:** You can provide your expected 'consumption' in kWh e.g. 15. This figure is used directly.
348
+
349
+ **Annual Consumption:** You can provide your 'annual_consumption' in kWh e.g. 5500. This figure is factored down to a daily consumption by dividing by 365 and applying **f.seasonality**. This normally decreases consumption in the summer and increases it in winter. Seasonality is a list of weightings by month for Jan, Feb, Mar, Apr etc. Preset lists are 'f.high_seasonality' (recommend where electric heating is ued), 'f.medium_seasonality' (default) amd 'f.no_seasonality' (all months the same). The daily consumption is profiled by hour using **f.daily_consumption**. This maps your consumption for a day to the hours when more or less energy is consumed. It is a list of 24 values for the times 00, 01, 02, 03 .. 23. Preset lists are 'f.high_profile' (larger peaks at 8am and 6pm), 'f.medium_profile' (default, more balanced) and 'f.no_profile' (flat).
362
350
 
363
351
  **Historic Consumption:** If annual_consumption is not provided, your consumption history is used. By default, this looks at your average consumption for the last 3 days using the load power reported by your inverter. For systems with multiple inverters where CT2 is not connected, the load power may not be correct. For this and other cases where you want to set your consumption, provide your annual_consumption.
364
352
 
@@ -629,7 +617,7 @@ f.get_pvoutput(d, tou)
629
617
  ```
630
618
 
631
619
  + d is the date or a list of dates, to get data for. The default is yesterday.
632
- + tou: optional, setting tou=1 uploads data with time of use. The default, tou=0 does not split data and is more accurate.
620
+ + tou: the default, tou=0, does not split data and is more accurate. Setting tou=1 uploads data with time of use. Setting tou=2 integrates all values and allows set_pvoutput() to work with pv inverters that do not provide energy stats.
633
621
  + setting integrate_load_power to 1 will calculate load energy by integrating the load power instead of using data from Fox. This tries to overcome the limitation where the inverter does not track load power / energy correctly when there is secondary generation. When set to 0 (default), the Fox load energy is used.
634
622
 
635
623
  You can copy and paste the output data to the pvoutput data CSV Loader, using the following settings:
@@ -652,7 +640,7 @@ f.set_pvoutput(d, system_id, tou, push, run_after)
652
640
 
653
641
  + d is optional and is the date, or a list of dates, to upload
654
642
  + system_id is optional and allow you to select where data is uploaded to (where you have more than 1 registered system)
655
- + tou: optional, setting tou=1 uploads data with time of use. The default, tou=0 does not split data and is more accurate
643
+ + tou: the default, tou=0, does not split data and is more accurate. Setting tou=1 uploads data with time of use. Setting tou=2 integrates all values and allows set_pvoutput() to work with pv inverters that do not provide energy stats.
656
644
  + push: optional. 0 = do not sent to pushover, 1 = send summary to pushover, 2 = send first day summary only
657
645
  + run_after: optional. Only generate data on or after this hour. Default 0.
658
646
 
@@ -807,6 +795,17 @@ This setting can be:
807
795
 
808
796
  # Version Info
809
797
 
798
+ 2.7.9<br>
799
+ Add 'consumption' input for charge_needed().
800
+ Avoid throwing exception in battery_info() if there is no capacity info.
801
+
802
+ 2.7.8<br>
803
+ Update the battery_params charge rate table to 2025 values.
804
+
805
+ 2.7.7<br>
806
+ Updates to get_pvoutput() to support solar inverters that don't provide grid energy by setting tou=2.
807
+ Default tariff changed to None.
808
+
810
809
  2.7.6<br>
811
810
  Updates to support F, G, R and S series inverters.
812
811
  Updates to set_named_settings() to load metadata if not already done and save new value.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "foxesscloud"
7
- version = "2.7.6"
7
+ version = "2.7.9"
8
8
  authors = [
9
9
  {name="Tony Matthews", email="tony@quasair.co.uk"},
10
10
  ]
@@ -1,7 +1,7 @@
1
1
  ##################################################################################################
2
2
  """
3
3
  Module: Fox ESS Cloud
4
- Updated: 13 December 2024
4
+ Updated: 28 January 2025
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.8.7"
13
+ version = "1.9.0"
14
14
  print(f"FoxESS-Cloud version {version}")
15
15
 
16
16
  debug_setting = 1
@@ -591,25 +591,29 @@ battery_settings = None
591
591
  # 1 = Residual Energy, 2 = Residual Capacity (HV), 3 = Residual Capacity per battery (Mira)
592
592
  residual_handling = 1
593
593
 
594
- # charge rates based on residual_handling
594
+ # charge rates based on residual_handling. Index is bms temperature
595
595
  battery_params = {
596
- # cell temp -5 0 5 10 15 20 25 30 35 40 45 50 55
597
596
  # bms temp 5 10 15 20 25 30 35 40 45 50 55 60 65
597
+ # cell temp -5 0 5 10 15 20 25 30 35 40 45 50 55
598
598
  1: {'table': [ 0, 2, 10, 15, 25, 50, 50, 50, 50, 50, 30, 20, 0],
599
599
  'step': 5,
600
600
  'offset': 5,
601
601
  'charge_loss': 0.974,
602
602
  'discharge_loss': 0.974},
603
- # HV BMS v2 with firmware 1.014 or later
604
- 2: {'table': [ 0, 2, 10, 10, 15, 15, 25, 50, 50, 50, 30, 20, 0],
603
+ # HV BMS v2 with firmware 1.014 or later
604
+ # bms temp 10 15 20 25 30 35 40 45 50 55 60 65 70
605
+ # cell temp 0 5 10 15 20 25 30 35 40 45 50 55 60
606
+ 2: {'table': [ 0, 5, 10, 15, 25, 50, 50, 50, 50, 25, 20, 3, 0],
605
607
  'step': 5,
606
- 'offset': 5,
608
+ 'offset': 11,
607
609
  'charge_loss': 1.08,
608
610
  'discharge_loss': 0.95},
609
- # Mira BMS with firmware 1.014 or later
610
- 3: {'table': [ 0, 2, 10, 10, 15, 15, 25, 50, 50, 50, 30, 20, 0],
611
+ # Mira BMS with firmware 1.014 or later
612
+ # bms temp 10 15 20 25 30 35 40 45 50 55 60 65 70
613
+ # cell temp 0 5 10 15 20 25 30 35 40 45 50 55 60
614
+ 3: {'table': [ 0, 5, 10, 15, 25, 50, 50, 50, 50, 25, 20, 3, 0],
611
615
  'step': 5,
612
- 'offset': 5,
616
+ 'offset': 11,
613
617
  'charge_loss': 0.974,
614
618
  'discharge_loss': 0.974},
615
619
  }
@@ -2380,7 +2384,7 @@ custom_periods = {'name': 'Custom',
2380
2384
  }
2381
2385
 
2382
2386
  tariff_list = [octopus_flux, intelligent_octopus, octopus_cosy, octopus_go, agile_octopus, bg_driver, eon_drive, economy_7, custom_periods]
2383
- tariff = octopus_flux
2387
+ tariff = None
2384
2388
 
2385
2389
  ##################################################################################################
2386
2390
  # Strategy - schedule templates
@@ -2953,6 +2957,7 @@ charge_needed_app_key = "awcr5gro2v13oher3v1qu6hwnovp28"
2953
2957
 
2954
2958
  # work out the charge times to set using the parameters:
2955
2959
  # forecast: the kWh expected tomorrow. If none, forecast data is loaded from solcast etc
2960
+ # consumption: the kWh consumed. If none, consumption is loaded from history
2956
2961
  # update_settings: 0 no updates, 1 update charge settings. The default is 0
2957
2962
  # show_data: 1 shows battery SoC, 2 shows battery residual. Default = 0
2958
2963
  # show_plot: 1 plots battery SoC, 2 plots battery residual. Default = 1
@@ -2960,7 +2965,7 @@ charge_needed_app_key = "awcr5gro2v13oher3v1qu6hwnovp28"
2960
2965
  # forecast_times: list of hours when forecast can be fetched (UTC)
2961
2966
  # force_charge: 1 = hold battery, 2 = charge for whole period
2962
2967
 
2963
- def charge_needed(forecast=None, update_settings=0, timed_mode=None, show_data=None, show_plot=None, run_after=None, reload=2,
2968
+ def charge_needed(forecast=None, consumption=None, update_settings=0, timed_mode=None, show_data=None, show_plot=None, run_after=None, reload=2,
2964
2969
  forecast_times=None, force_charge=0, test_time=None, test_soc=None, test_charge=None, **settings):
2965
2970
  global device, seasonality, solcast_api_key, debug_setting, tariff, solar_arrays, legend_location, time_shift, charge_needed_app_key
2966
2971
  global timed_strategy, steps_per_hour, base_time, storage, battery, battery_params
@@ -3172,6 +3177,9 @@ def charge_needed(forecast=None, update_settings=0, timed_mode=None, show_data=N
3172
3177
  consumption = annual_consumption / 365 * seasonality[now.month - 1] / sum(seasonality) * 12
3173
3178
  consumption_by_hour = daily_consumption
3174
3179
  output(f"\nEstimated consumption: {consumption:.1f}kWh")
3180
+ elif consumption is not None:
3181
+ consumption_by_hour = daily_consumption
3182
+ output(f"\nConsumption: {consumption:.1f}kWh")
3175
3183
  else:
3176
3184
  consumption_days = charge_config['consumption_days']
3177
3185
  consumption_days = 3 if consumption_days > 7 or consumption_days < 1 else consumption_days
@@ -3633,7 +3641,7 @@ def battery_info(log=0, plot=1, rated=None, count=None, info=1, bat=None):
3633
3641
  bat_current = bat['current']
3634
3642
  bat_power = bat['power']
3635
3643
  bms_temperature = bat['temperature']
3636
- capacity = bat['capacity']
3644
+ capacity = bat.get('capacity')
3637
3645
  cell_volts = get_cell_volts()
3638
3646
  if cell_volts is None:
3639
3647
  output_close()
@@ -3678,7 +3686,8 @@ def battery_info(log=0, plot=1, rated=None, count=None, info=1, bat=None):
3678
3686
  s +=f",{v:.0f}"
3679
3687
  return s
3680
3688
  output(f"Current SoC: {current_soc}%")
3681
- output(f"Capacity: {capacity:.2f}kWh" + (" (calculated)" if bat['residual_handling'] in [1,3] else ""))
3689
+ if capacity is not None:
3690
+ output(f"Capacity: {capacity:.2f}kWh" + (" (calculated)" if bat['residual_handling'] in [1,3] else ""))
3682
3691
  output(f"Residual: {residual:.2f}kWh" + (" (calculated)" if bat['residual_handling'] in [2,3] else ""))
3683
3692
  if rated_capacity is not None and bat_soh is not None:
3684
3693
  output(f"Rated Capacity: {rated_capacity / 1000:.2f}kWh")
@@ -3857,18 +3866,19 @@ integrate_load_power = 0
3857
3866
  ##################################################################################################
3858
3867
 
3859
3868
  # get pvoutput data for upload to pvoutput api or via Bulk Loader
3860
- # tou: 0 = no time of use, 1 = use time of use periods if available
3869
+ # tou: 0 = no time of use, 1 = use time of use periods if available, 2 = integrate all values
3861
3870
 
3862
3871
  def get_pvoutput(d = None, tou = 0):
3863
3872
  global tariff, pv_calibration, ct2_calibration, integrate_load_power
3864
3873
  if d is None:
3865
3874
  d = date_list()[0]
3866
- tou = 0 if tariff is None else 1 if tou == 1 or tou == True else 0
3867
3875
  if type(d) is list:
3868
3876
  print(f"---------------- get_pvoutput ------------------")
3869
3877
  print(f"Date range {d[0]} to {d[-1]} has {len(d)} days")
3870
3878
  if tou == 1:
3871
3879
  print(f"Time of use: {tariff['name']}")
3880
+ elif tou == 2:
3881
+ print(f"All values integrated from power")
3872
3882
  if integrate_load_power == 1:
3873
3883
  print(f"Consumption integrated from Load Power")
3874
3884
  print(f"------------------------------------------------")
@@ -3882,12 +3892,15 @@ def get_pvoutput(d = None, tou = 0):
3882
3892
  v = ['feedin', 'gridConsumption']
3883
3893
  if integrate_load_power == 0:
3884
3894
  v.append('loads')
3885
- report_data = get_report('day', d=d, v=v, summary=2)
3886
- if report_data is None:
3887
- return None
3895
+ if tou == 2:
3896
+ report_data = []
3897
+ else:
3898
+ report_data = get_report('day', d=d, v=v, summary=2)
3899
+ if report_data is None:
3900
+ return None
3888
3901
  # get raw power data for the day
3889
- v = ['pvPower', 'meterPower2', 'feedinPower', 'gridConsumptionPower'] if tou == 1 else ['pvPower', 'meterPower2']
3890
- if integrate_load_power == 1:
3902
+ v = ['pvPower', 'meterPower2', 'feedinPower', 'gridConsumptionPower'] if tou > 0 else ['pvPower', 'meterPower2']
3903
+ if integrate_load_power == 1 or tou == 2:
3891
3904
  v.append('loadsPower')
3892
3905
  raw_data = get_raw('day', d=d + ' 00:00:00', v=v , summary=1)
3893
3906
  if raw_data is None or len(raw_data) == 0 or raw_data[0].get('kwh') is None or raw_data[0].get('max') is None:
@@ -3937,10 +3950,12 @@ def get_pvoutput(d = None, tou = 0):
3937
3950
  generate = f"{wh},"
3938
3951
  power = f"{int(var['max'] * 1000)},{var['max_time']},"
3939
3952
  elif var['variable'] == 'feedinPower':
3953
+ export_wh = wh if tou == 2 else export_wh
3940
3954
  calibrate = export_wh / wh if wh > 0.0 else 1.0
3941
3955
  export = f","
3942
3956
  export_tou = f"{int(peak * calibrate)},{int(off_peak * calibrate)},{int((wh - peak - off_peak) * calibrate)},0"
3943
3957
  elif var['variable'] == 'gridConsumptionPower':
3958
+ grid_wh = wh if tou == 2 else grid_wh
3944
3959
  calibrate = grid_wh / wh if wh > 0.0 else 1.0
3945
3960
  grid = f"{int(peak * calibrate)},{int(off_peak * calibrate)},{int((wh - peak - off_peak) * calibrate)},0,"
3946
3961
  elif var['variable'] == 'loadsPower':
@@ -1,7 +1,7 @@
1
1
  ##################################################################################################
2
2
  """
3
3
  Module: Fox ESS Cloud using Open API
4
- Updated: 13 December 2024
4
+ Updated: 27 January 2025
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.7.6"
13
+ version = "2.7.9"
14
14
  print(f"FoxESS-Cloud Open API version {version}")
15
15
 
16
16
  debug_setting = 1
@@ -555,25 +555,29 @@ battery_data = ['soc', 'volt', 'current', 'power', 'temperature', 'residual']
555
555
  # 1 = Residual Energy, 2 = Residual Capacity (HV), 3 = Residual Capacity per battery (Mira)
556
556
  residual_handling = 1
557
557
 
558
- # charge rates based on residual_handling
558
+ # charge rates based on residual_handling. Index is bms temperature
559
559
  battery_params = {
560
- # cell temp -5 0 5 10 15 20 25 30 35 40 45 50 55
561
560
  # bms temp 5 10 15 20 25 30 35 40 45 50 55 60 65
561
+ # cell temp -5 0 5 10 15 20 25 30 35 40 45 50 55
562
562
  1: {'table': [ 0, 2, 10, 15, 25, 50, 50, 50, 50, 50, 30, 20, 0],
563
563
  'step': 5,
564
564
  'offset': 5,
565
565
  'charge_loss': 0.974,
566
566
  'discharge_loss': 0.974},
567
- # HV BMS v2 with firmware 1.014 or later
568
- 2: {'table': [ 0, 2, 10, 10, 15, 15, 25, 50, 50, 50, 30, 20, 0],
567
+ # HV BMS v2 with firmware 1.014 or later
568
+ # bms temp 10 15 20 25 30 35 40 45 50 55 60 65 70
569
+ # cell temp 0 5 10 15 20 25 30 35 40 45 50 55 60
570
+ 2: {'table': [ 0, 5, 10, 15, 25, 50, 50, 50, 50, 25, 20, 3, 0],
569
571
  'step': 5,
570
- 'offset': 5,
572
+ 'offset': 11,
571
573
  'charge_loss': 1.08,
572
574
  'discharge_loss': 0.95},
573
- # Mira BMS with firmware 1.014 or later
574
- 3: {'table': [ 0, 2, 10, 10, 15, 15, 25, 50, 50, 50, 30, 20, 0],
575
+ # Mira BMS with firmware 1.014 or later
576
+ # bms temp 10 15 20 25 30 35 40 45 50 55 60 65 70
577
+ # cell temp 0 5 10 15 20 25 30 35 40 45 50 55 60
578
+ 3: {'table': [ 0, 5, 10, 15, 25, 50, 50, 50, 50, 25, 20, 3, 0],
575
579
  'step': 5,
576
- 'offset': 5,
580
+ 'offset': 11,
577
581
  'charge_loss': 0.974,
578
582
  'discharge_loss': 0.974},
579
583
  }
@@ -2052,7 +2056,7 @@ custom_periods = {'name': 'Custom',
2052
2056
  }
2053
2057
 
2054
2058
  tariff_list = [octopus_flux, intelligent_octopus, octopus_cosy, octopus_go, agile_octopus, bg_driver, eon_drive, economy_7, custom_periods]
2055
- tariff = octopus_flux
2059
+ tariff = None
2056
2060
 
2057
2061
  ##################################################################################################
2058
2062
  # Strategy - schedule templates
@@ -2624,6 +2628,7 @@ charge_needed_app_key = "awcr5gro2v13oher3v1qu6hwnovp28"
2624
2628
 
2625
2629
  # work out the charge times to set using the parameters:
2626
2630
  # forecast: the kWh expected tomorrow. If none, forecast data is loaded from solcast etc
2631
+ # consumption: the kWh consumed. If none, consumption is loaded from history
2627
2632
  # update_settings: 0 no updates, 1 update charge settings. The default is 0
2628
2633
  # show_data: 1 shows battery SoC, 2 shows battery residual. Default = 0
2629
2634
  # show_plot: 1 plots battery SoC, 2 plots battery residual. Default = 1
@@ -2631,7 +2636,7 @@ charge_needed_app_key = "awcr5gro2v13oher3v1qu6hwnovp28"
2631
2636
  # forecast_times: list of hours when forecast can be fetched (UTC)
2632
2637
  # force_charge: 1 = hold battery, 2 = charge for whole period
2633
2638
 
2634
- def charge_needed(forecast=None, update_settings=0, timed_mode=None, show_data=None, show_plot=None, run_after=None, reload=2,
2639
+ def charge_needed(forecast=None, consumption=None, update_settings=0, timed_mode=None, show_data=None, show_plot=None, run_after=None, reload=2,
2635
2640
  forecast_times=None, force_charge=0, test_time=None, test_soc=None, test_charge=None, **settings):
2636
2641
  global device, seasonality, solcast_api_key, debug_setting, tariff, solar_arrays, legend_location, time_shift, charge_needed_app_key
2637
2642
  global timed_strategy, steps_per_hour, base_time, storage, battery, battery_params
@@ -2847,6 +2852,9 @@ def charge_needed(forecast=None, update_settings=0, timed_mode=None, show_data=N
2847
2852
  consumption = annual_consumption / 365 * seasonality[now.month - 1] / sum(seasonality) * 12
2848
2853
  consumption_by_hour = daily_consumption
2849
2854
  output(f"\nEstimated consumption: {consumption:.1f}kWh")
2855
+ elif consumption is not None:
2856
+ consumption_by_hour = daily_consumption
2857
+ output(f"\nConsumption: {consumption:.1f}kWh")
2850
2858
  else:
2851
2859
  consumption_days = charge_config['consumption_days']
2852
2860
  consumption_days = 3 if consumption_days > 7 or consumption_days < 1 else consumption_days
@@ -3305,7 +3313,7 @@ def battery_info(log=0, plot=1, rated=None, count=None, info=1, bat=None):
3305
3313
  bat_current = bat['current']
3306
3314
  bat_power = bat['power']
3307
3315
  bms_temperature = bat['temperature']
3308
- capacity = bat['capacity']
3316
+ capacity = bat.get('capacity')
3309
3317
  cell_volts = get_cell_volts()
3310
3318
  if cell_volts is None:
3311
3319
  output_close()
@@ -3350,7 +3358,8 @@ def battery_info(log=0, plot=1, rated=None, count=None, info=1, bat=None):
3350
3358
  s +=f",{v:.0f}"
3351
3359
  return s
3352
3360
  output(f"Current SoC: {current_soc}%")
3353
- output(f"Capacity: {capacity:.2f}kWh" + (" (calculated)" if bat['residual_handling'] in [1,3] else ""))
3361
+ if capacity is not None:
3362
+ output(f"Capacity: {capacity:.2f}kWh" + (" (calculated)" if bat['residual_handling'] in [1,3] else ""))
3354
3363
  output(f"Residual: {residual:.2f}kWh" + (" (calculated)" if bat['residual_handling'] in [2,3] else ""))
3355
3364
  if rated_capacity is not None and bat_soh is not None:
3356
3365
  output(f"Rated Capacity: {rated_capacity / 1000:.2f}kWh")
@@ -3529,18 +3538,19 @@ integrate_load_power = 0
3529
3538
  ##################################################################################################
3530
3539
 
3531
3540
  # get pvoutput data for upload to pvoutput api or via Bulk Loader
3532
- # tou: 0 = no time of use, 1 = use time of use periods if available
3541
+ # tou: 0 = no time of use, 1 = use time of use periods if available, 2 = integrate all values
3533
3542
 
3534
3543
  def get_pvoutput(d = None, tou = 0):
3535
3544
  global tariff, pv_calibration, ct2_calibration, integrate_load_power
3536
3545
  if d is None:
3537
3546
  d = date_list()[0]
3538
- tou = 0 if tariff is None else 1 if tou == 1 or tou == True else 0
3539
3547
  if type(d) is list:
3540
3548
  print(f"---------------- get_pvoutput ------------------")
3541
3549
  print(f"Date range {d[0]} to {d[-1]} has {len(d)} days")
3542
3550
  if tou == 1:
3543
3551
  print(f"Time of use: {tariff['name']}")
3552
+ elif tou == 2:
3553
+ print(f"All values integrated from power")
3544
3554
  if integrate_load_power == 1:
3545
3555
  print(f"Consumption integrated from Load Power")
3546
3556
  print(f"------------------------------------------------")
@@ -3554,12 +3564,15 @@ def get_pvoutput(d = None, tou = 0):
3554
3564
  v = ['feedin', 'gridConsumption']
3555
3565
  if integrate_load_power == 0:
3556
3566
  v.append('loads')
3557
- report_data = get_report('day', d=d, v=v, summary=2)
3558
- if report_data is None:
3559
- return None
3567
+ if tou == 2:
3568
+ report_data = []
3569
+ else:
3570
+ report_data = get_report('day', d=d, v=v, summary=2)
3571
+ if report_data is None:
3572
+ return None
3560
3573
  # get raw power data for the day
3561
- v = ['pvPower', 'meterPower2', 'feedinPower', 'gridConsumptionPower'] if tou == 1 else ['pvPower', 'meterPower2']
3562
- if integrate_load_power == 1:
3574
+ v = ['pvPower', 'meterPower2', 'feedinPower', 'gridConsumptionPower'] if tou > 0 else ['pvPower', 'meterPower2']
3575
+ if integrate_load_power == 1 or tou == 2:
3563
3576
  v.append('loadsPower')
3564
3577
  raw_data = get_raw('day', d=d + ' 00:00:00', v=v , summary=1)
3565
3578
  if raw_data is None or len(raw_data) == 0 or raw_data[0].get('kwh') is None or raw_data[0].get('max') is None:
@@ -3609,10 +3622,12 @@ def get_pvoutput(d = None, tou = 0):
3609
3622
  generate = f"{wh},"
3610
3623
  power = f"{int(var['max'] * 1000)},{var['max_time']},"
3611
3624
  elif var['variable'] == 'feedinPower':
3625
+ export_wh = wh if tou == 2 else export_wh
3612
3626
  calibrate = export_wh / wh if wh > 0.0 else 1.0
3613
3627
  export = f","
3614
3628
  export_tou = f"{int(peak * calibrate)},{int(off_peak * calibrate)},{int((wh - peak - off_peak) * calibrate)},0"
3615
3629
  elif var['variable'] == 'gridConsumptionPower':
3630
+ grid_wh = wh if tou == 2 else grid_wh
3616
3631
  calibrate = grid_wh / wh if wh > 0.0 else 1.0
3617
3632
  grid = f"{int(peak * calibrate)},{int(off_peak * calibrate)},{int((wh - peak - off_peak) * calibrate)},0,"
3618
3633
  elif var['variable'] == 'loadsPower':
@@ -1,3 +1,17 @@
1
+ Metadata-Version: 2.1
2
+ Name: foxesscloud
3
+ Version: 2.7.9
4
+ Summary: library for accessing Fox ESS cloud data using Open API
5
+ Author-email: Tony Matthews <tony@quasair.co.uk>
6
+ Project-URL: Homepage, https://github.com/TonyM1958/FoxESS-Cloud
7
+ Project-URL: Bug Tracker, https://github.com/TonyM1958/FoxESS-Cloud/issues
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.7
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENCE
14
+
1
15
  # FoxESS-Cloud
2
16
 
3
17
  <a href="https://www.buymeacoffee.com/tonym1958" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee" height="41" width="174" align="right"></a>
@@ -344,7 +358,9 @@ All the parameters are optional:
344
358
 
345
359
  charge_needed() uses a number of models to better estimate the state of the battery.
346
360
 
347
- **Manual Consumption:** You can provide your 'annual_consumption' in kWh e.g. 5500. This figure is factored down to a daily consumption by dividing by 365 and applying **f.seasonality**. This normally decreases consumption in the summer and increases it in winter. Seasonality is a list of weightings by month for Jan, Feb, Mar, Apr etc. Preset lists are 'f.high_seasonality' (recommend where electric heating is ued), 'f.medium_seasonality' (default) amd 'f.no_seasonality' (all months the same). The daily consumption is profiled by hour using **f.daily_consumption**. This maps your consumption for a day to the hours when more or less energy is consumed. It is a list of 24 values for the times 00, 01, 02, 03 .. 23. Preset lists are 'f.high_profile' (larger peaks at 8am and 6pm), 'f.medium_profile' (default, more balanced) and 'f.no_profile' (flat).
361
+ **Manual Consumption:** You can provide your expected 'consumption' in kWh e.g. 15. This figure is used directly.
362
+
363
+ **Annual Consumption:** You can provide your 'annual_consumption' in kWh e.g. 5500. This figure is factored down to a daily consumption by dividing by 365 and applying **f.seasonality**. This normally decreases consumption in the summer and increases it in winter. Seasonality is a list of weightings by month for Jan, Feb, Mar, Apr etc. Preset lists are 'f.high_seasonality' (recommend where electric heating is ued), 'f.medium_seasonality' (default) amd 'f.no_seasonality' (all months the same). The daily consumption is profiled by hour using **f.daily_consumption**. This maps your consumption for a day to the hours when more or less energy is consumed. It is a list of 24 values for the times 00, 01, 02, 03 .. 23. Preset lists are 'f.high_profile' (larger peaks at 8am and 6pm), 'f.medium_profile' (default, more balanced) and 'f.no_profile' (flat).
348
364
 
349
365
  **Historic Consumption:** If annual_consumption is not provided, your consumption history is used. By default, this looks at your average consumption for the last 3 days using the load power reported by your inverter. For systems with multiple inverters where CT2 is not connected, the load power may not be correct. For this and other cases where you want to set your consumption, provide your annual_consumption.
350
366
 
@@ -615,7 +631,7 @@ f.get_pvoutput(d, tou)
615
631
  ```
616
632
 
617
633
  + d is the date or a list of dates, to get data for. The default is yesterday.
618
- + tou: optional, setting tou=1 uploads data with time of use. The default, tou=0 does not split data and is more accurate.
634
+ + tou: the default, tou=0, does not split data and is more accurate. Setting tou=1 uploads data with time of use. Setting tou=2 integrates all values and allows set_pvoutput() to work with pv inverters that do not provide energy stats.
619
635
  + setting integrate_load_power to 1 will calculate load energy by integrating the load power instead of using data from Fox. This tries to overcome the limitation where the inverter does not track load power / energy correctly when there is secondary generation. When set to 0 (default), the Fox load energy is used.
620
636
 
621
637
  You can copy and paste the output data to the pvoutput data CSV Loader, using the following settings:
@@ -638,7 +654,7 @@ f.set_pvoutput(d, system_id, tou, push, run_after)
638
654
 
639
655
  + d is optional and is the date, or a list of dates, to upload
640
656
  + system_id is optional and allow you to select where data is uploaded to (where you have more than 1 registered system)
641
- + tou: optional, setting tou=1 uploads data with time of use. The default, tou=0 does not split data and is more accurate
657
+ + tou: the default, tou=0, does not split data and is more accurate. Setting tou=1 uploads data with time of use. Setting tou=2 integrates all values and allows set_pvoutput() to work with pv inverters that do not provide energy stats.
642
658
  + push: optional. 0 = do not sent to pushover, 1 = send summary to pushover, 2 = send first day summary only
643
659
  + run_after: optional. Only generate data on or after this hour. Default 0.
644
660
 
@@ -793,6 +809,17 @@ This setting can be:
793
809
 
794
810
  # Version Info
795
811
 
812
+ 2.7.9<br>
813
+ Add 'consumption' input for charge_needed().
814
+ Avoid throwing exception in battery_info() if there is no capacity info.
815
+
816
+ 2.7.8<br>
817
+ Update the battery_params charge rate table to 2025 values.
818
+
819
+ 2.7.7<br>
820
+ Updates to get_pvoutput() to support solar inverters that don't provide grid energy by setting tou=2.
821
+ Default tariff changed to None.
822
+
796
823
  2.7.6<br>
797
824
  Updates to support F, G, R and S series inverters.
798
825
  Updates to set_named_settings() to load metadata if not already done and save new value.
File without changes