foxesscloud 2.9.4__tar.gz → 2.9.6__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,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: foxesscloud
3
- Version: 2.9.4
3
+ Version: 2.9.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
@@ -139,6 +139,7 @@ get_settings() will return the battery settings and is equivalent to get_charge(
139
139
  get_peakshaving() will return the current peak shaving settings for inverters that support this work mode.
140
140
 
141
141
  get_flag() returns the current scheduler enable / support / maxsoc flags. By default support for Max Soc is set to False.
142
+ + f.max_periods / schedule['maxGroupCount'] gives the maximum number of time periods that are supported.
142
143
 
143
144
  get_schedule() returns the current work mode / soc schedule settings. The result is stored in f.schedule.
144
145
  + if the schedule returned contains any values for 'maxSoc', the f.schedule['maxsoc'] is set to True to indicate that the current inverter supports setting Max Soc in schedules and Max Soc values are set by set_schedule().
@@ -496,7 +497,7 @@ This is an example of the output from battery_info():
496
497
  ## Date Ranges
497
498
 
498
499
  ```
499
- f.date_list(s, e, limit, span, today)
500
+ f.date_list(s, e, limit, span, today, step)
500
501
  ```
501
502
 
502
503
  Returns a list of dates in the format 'YYYY-MM-DD'. This function will not return dates in the future. The last date will be yesterday or today (if today is True). All parameters are optional:
@@ -506,6 +507,7 @@ Returns a list of dates in the format 'YYYY-MM-DD'. This function will not retur
506
507
  + limit: maximum number of days. The default is 200
507
508
  + span: the range of dates. One of 'day', 'week', 'month' or 'year', '2days' or 'weekday'
508
509
  + today: 1 allows today to be included, 2 allows future dates to be included. Default is 0, date list will stop at yesterday
510
+ + step: optional days to add (default is 1)
509
511
 
510
512
  You can use 'span' as follows:
511
513
  + 'day' provides a single day
@@ -830,6 +832,17 @@ This setting can be:
830
832
 
831
833
  # Version Info
832
834
 
835
+ 2.9.6 - 2026/03/10<br>
836
+ Fix problem setting fdsoc and fdpwr from segment for set_period().
837
+ Correctly identify H1-G2 model inverter.
838
+ Add maxChargeCurrent and maxDischargeCurrent to f.battery.
839
+ Update battery charge derating with temperature so charge current is not 0.
840
+
841
+
842
+ 2.9.5 - 2026/02/27<br>
843
+ Add step parameter to date_list().
844
+ Get maxGroupCount from v3 scheduler and update f.max_periods with maximum number of time periods supported.
845
+
833
846
  2.9.4 - 2026/01/22<br>
834
847
  Rename get_battery_heating(), set_battery_heating() to get_heating() and set_heating().
835
848
  Update get_heating() to return a dictionary to simplify parameter access.
@@ -1,17 +1,3 @@
1
- Metadata-Version: 2.1
2
- Name: foxesscloud
3
- Version: 2.9.4
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>
@@ -139,6 +125,7 @@ get_settings() will return the battery settings and is equivalent to get_charge(
139
125
  get_peakshaving() will return the current peak shaving settings for inverters that support this work mode.
140
126
 
141
127
  get_flag() returns the current scheduler enable / support / maxsoc flags. By default support for Max Soc is set to False.
128
+ + f.max_periods / schedule['maxGroupCount'] gives the maximum number of time periods that are supported.
142
129
 
143
130
  get_schedule() returns the current work mode / soc schedule settings. The result is stored in f.schedule.
144
131
  + if the schedule returned contains any values for 'maxSoc', the f.schedule['maxsoc'] is set to True to indicate that the current inverter supports setting Max Soc in schedules and Max Soc values are set by set_schedule().
@@ -496,7 +483,7 @@ This is an example of the output from battery_info():
496
483
  ## Date Ranges
497
484
 
498
485
  ```
499
- f.date_list(s, e, limit, span, today)
486
+ f.date_list(s, e, limit, span, today, step)
500
487
  ```
501
488
 
502
489
  Returns a list of dates in the format 'YYYY-MM-DD'. This function will not return dates in the future. The last date will be yesterday or today (if today is True). All parameters are optional:
@@ -506,6 +493,7 @@ Returns a list of dates in the format 'YYYY-MM-DD'. This function will not retur
506
493
  + limit: maximum number of days. The default is 200
507
494
  + span: the range of dates. One of 'day', 'week', 'month' or 'year', '2days' or 'weekday'
508
495
  + today: 1 allows today to be included, 2 allows future dates to be included. Default is 0, date list will stop at yesterday
496
+ + step: optional days to add (default is 1)
509
497
 
510
498
  You can use 'span' as follows:
511
499
  + 'day' provides a single day
@@ -830,6 +818,17 @@ This setting can be:
830
818
 
831
819
  # Version Info
832
820
 
821
+ 2.9.6 - 2026/03/10<br>
822
+ Fix problem setting fdsoc and fdpwr from segment for set_period().
823
+ Correctly identify H1-G2 model inverter.
824
+ Add maxChargeCurrent and maxDischargeCurrent to f.battery.
825
+ Update battery charge derating with temperature so charge current is not 0.
826
+
827
+
828
+ 2.9.5 - 2026/02/27<br>
829
+ Add step parameter to date_list().
830
+ Get maxGroupCount from v3 scheduler and update f.max_periods with maximum number of time periods supported.
831
+
833
832
  2.9.4 - 2026/01/22<br>
834
833
  Rename get_battery_heating(), set_battery_heating() to get_heating() and set_heating().
835
834
  Update get_heating() to return a dictionary to simplify parameter access.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "foxesscloud"
7
- version = "2.9.4"
7
+ version = "2.9.6"
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: 18 January 2026
4
+ Updated: 05 March 2026
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.11.0"
13
+ version = "1.11.2"
14
14
  print(f"FoxESS-Cloud version {version}")
15
15
 
16
16
  debug_setting = 1
@@ -502,6 +502,9 @@ def get_device(sn=None, device_type=None):
502
502
  model_code = 'EVO-' + model_code[4:]
503
503
  parts = model_code.split('-')
504
504
  model = parts[0]
505
+ if parts[-1] == 'G2':
506
+ parts[0] += 'G2'
507
+ del parts[-1]
505
508
  device['eps'] = ('E' in parts[-1]) or (model == 'EVO' and 'H' in parts[-1])
506
509
  if model not in ['F1', 'G1', 'R3', 'S1', 'T3', 'KH', 'H1', 'AC1', 'H3', 'AC3', 'AIOH1', 'AIOH3', 'EVO']:
507
510
  output(f"** device model not recognised for deviceType: {device['deviceType']}")
@@ -3821,7 +3824,7 @@ def battery_monitor(interval=30, run=48, log=1, count=None, save=None, overwrite
3821
3824
  # span: 'week', 'month' or 'year' generated dates that span a week, month or year
3822
3825
  # quiet: do not print results if True
3823
3826
 
3824
- def date_list(s = None, e = None, limit = None, span = None, today = 0, quiet = True):
3827
+ def date_list(s = None, e = None, limit = None, span = None, today = 0, quiet = True, step=1):
3825
3828
  global debug_setting
3826
3829
  latest_date = datetime.date(datetime.now())
3827
3830
  today = 0 if today == False else 1 if today == True else today
@@ -3830,7 +3833,6 @@ def date_list(s = None, e = None, limit = None, span = None, today = 0, quiet =
3830
3833
  first = datetime.date(datetime.strptime(s, '%Y-%m-%d')) if type(s) is str else s.date() if s is not None else None
3831
3834
  last = datetime.date(datetime.strptime(e, '%Y-%m-%d')) if type(e) is str else e.date() if e is not None else None
3832
3835
  last = latest_date if last is not None and last > latest_date and today != 2 else last
3833
- step = 1
3834
3836
  if first is None and last is None:
3835
3837
  last = latest_date
3836
3838
  if span is not None:
@@ -1,7 +1,7 @@
1
1
  ##################################################################################################
2
2
  """
3
3
  Module: Fox ESS Cloud using Open API
4
- Updated: 19 January 2025
4
+ Updated: 10 March 2026
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.9.4"
13
+ version = "2.9.6"
14
14
  print(f"FoxESS-Cloud Open API version {version}")
15
15
 
16
16
  debug_setting = 1
@@ -515,6 +515,9 @@ def get_device(sn=None, device_type=None):
515
515
  model_code = 'EVO-' + model_code[4:]
516
516
  parts = model_code.split('-')
517
517
  model = parts[0]
518
+ if parts[-1] == 'G2':
519
+ parts[0] += 'G2'
520
+ del parts[-1]
518
521
  device['eps'] = ('E' in parts[-1]) or (model == 'EVO' and 'H' in parts[-1])
519
522
  if model not in ['F1', 'G1', 'R3', 'S1', 'T3', 'KH', 'H1', 'AC1', 'H3', 'AC3', 'AIOH1', 'AIOH3', 'EVO']:
520
523
  output(f"** device model not recognised for deviceType: {device['deviceType']}")
@@ -574,8 +577,8 @@ def get_generation(update=1):
574
577
  battery = None
575
578
  batteries = None
576
579
  battery_settings = None
577
- battery_vars = ['SoC', 'invBatVolt', 'invBatCurrent', 'invBatPower', 'batTemperature', 'ResidualEnergy','SOH','energyThroughput' ]
578
- battery_data = ['soc', 'volt', 'current', 'power', 'temperature', 'residual', 'soh', 'throughput']
580
+ battery_vars = ['SoC', 'invBatVolt', 'invBatCurrent', 'invBatPower', 'batTemperature', 'ResidualEnergy','SOH','energyThroughput', 'maxChargeCurrent', 'maxDischargeCurrent' ]
581
+ battery_data = ['soc', 'volt', 'current', 'power', 'temperature', 'residual', 'soh', 'throughput', 'maxChargeCurrent', 'maxDischargeCurrent']
579
582
 
580
583
  # 1 = Residual Energy, 2 = Residual Capacity (HV), 3 = Residual Capacity per battery (Mira)
581
584
  residual_handling = 0
@@ -584,7 +587,7 @@ residual_handling = 0
584
587
  battery_params = {
585
588
  # bms temp 5 10 15 20 25 30 35 40 45 50 55 60 65
586
589
  # cell temp -5 0 5 10 15 20 25 30 35 40 45 50 55
587
- 1: {'table': [ 0, 2, 10, 15, 25, 50, 50, 50, 50, 50, 30, 20, 0],
590
+ 1: {'table': [ 1, 5, 10, 15, 25, 50, 50, 50, 50, 50, 30, 20, 1],
588
591
  'step': 5,
589
592
  'offset': 5,
590
593
  'charge_loss': 0.974,
@@ -592,7 +595,7 @@ battery_params = {
592
595
  # HV BMS v2 with firmware 1.014 or later
593
596
  # bms temp 10 15 20 25 30 35 40 45 50 55 60 65 70
594
597
  # cell temp 0 5 10 15 20 25 30 35 40 45 50 55 60
595
- 2: {'table': [ 0, 5, 10, 15, 25, 50, 50, 50, 50, 25, 20, 3, 0],
598
+ 2: {'table': [ 1, 5, 10, 15, 25, 50, 50, 50, 50, 25, 20, 3, 1],
596
599
  'step': 5,
597
600
  'offset': 11,
598
601
  'charge_loss': 1.08,
@@ -600,7 +603,7 @@ battery_params = {
600
603
  # Mira BMS with firmware 1.014 or later
601
604
  # bms temp 10 15 20 25 30 35 40 45 50 55 60 65 70
602
605
  # cell temp 0 5 10 15 20 25 30 35 40 45 50 55 60
603
- 3: {'table': [ 0, 5, 10, 15, 25, 50, 50, 50, 50, 25, 20, 3, 0],
606
+ 3: {'table': [ 1, 5, 10, 15, 25, 50, 50, 50, 50, 25, 20, 3, 1],
604
607
  'step': 5,
605
608
  'offset': 11,
606
609
  'charge_loss': 0.974,
@@ -1151,7 +1154,7 @@ max_periods = 8
1151
1154
 
1152
1155
  # get the current switch status
1153
1156
  def get_flag():
1154
- global device_sn, schedule, debug_setting
1157
+ global device_sn, schedule, debug_setting, max_periods
1155
1158
  if get_device() is None:
1156
1159
  return None
1157
1160
  output(f"getting flag", 2)
@@ -1169,6 +1172,17 @@ def get_flag():
1169
1172
  schedule['support'] = result.get('support')
1170
1173
  if device.get('function') is not None and device['function'].get('scheduler') is not None:
1171
1174
  device['function']['scheduler'] = schedule['support']
1175
+ if schedule.get('maxGroupCount') is None:
1176
+ output(f"getting maxGroupCount", 2)
1177
+ body = {'deviceSN': device_sn}
1178
+ response = signed_post(path="/op/v3/device/scheduler/get", body=body)
1179
+ if response.status_code != 200:
1180
+ output(f"** get_flag() got response code getting maxGroupCount {response.status_code}: {response.reason}")
1181
+ return None
1182
+ result = response.json().get('result')
1183
+ if result is not None:
1184
+ schedule['maxGroupCount'] = result.get('maxGroupCount')
1185
+ max_periods = schedule['maxGroupCount']
1172
1186
  return schedule
1173
1187
 
1174
1188
  ##################################################################################################
@@ -1242,8 +1256,8 @@ def set_period(start=None, end=None, mode=None, min_soc=None, max_soc=None, fdso
1242
1256
  mode = segment.get('mode')
1243
1257
  min_soc = segment.get('min_soc')
1244
1258
  max_soc = segment.get('max_soc')
1245
- fdsoc = segment.get('fdSoc')
1246
- fdpwr = segment.get('fdPwr')
1259
+ fdsoc = segment.get('fdsoc')
1260
+ fdpwr = segment.get('fdpwr')
1247
1261
  import_limit = segment.get('import_limit')
1248
1262
  export_limit = segment.get('export_limit')
1249
1263
  price = segment.get('price')
@@ -2944,7 +2958,7 @@ def charge_needed(forecast=None, consumption=None, update_settings=0, timed_mode
2944
2958
  reserve = capacity * min_soc / 100
2945
2959
  # charge current may be derated based on temperature
2946
2960
  charge_current = device_current if charge_config['charge_current'] is None else charge_config['charge_current']
2947
- if bms_charge_current is not None and charge_current > bms_charge_current:
2961
+ if bms_charge_current is not None and bms_charge_current < charge_current:
2948
2962
  charge_current = bms_charge_current
2949
2963
  volt_curve = charge_config['volt_curve']
2950
2964
  nominal_soc = charge_config['nominal_soc']
@@ -2965,10 +2979,6 @@ def charge_needed(forecast=None, consumption=None, update_settings=0, timed_mode
2965
2979
  output(f" Resistance: {bat_resistance:.2f} ohms")
2966
2980
  output(f" Nominal OCV: {bat_ocv:.1f}V at {nominal_soc}% SoC")
2967
2981
  output(f" Losses: {charge_loss * 100:.1f}% charge / {discharge_loss * 100:.1f}% discharge", 2)
2968
- # charge current may be derated based on temperature
2969
- charge_current = device_current if charge_config['charge_current'] is None else charge_config['charge_current']
2970
- if charge_current > bms_charge_current:
2971
- charge_current = bms_charge_current
2972
2982
  # inverter losses
2973
2983
  inverter_power = charge_config['inverter_power'] if charge_config['inverter_power'] is not None else round(device_power, 0) * 25
2974
2984
  operating_loss = inverter_power / 1000
@@ -3620,7 +3630,7 @@ def battery_monitor(interval=30, run=48, log=1, count=None, save=None, overwrite
3620
3630
  # span: 'week', 'month' or 'year' generated dates that span a week, month or year
3621
3631
  # quiet: do not print results if True
3622
3632
 
3623
- def date_list(s = None, e = None, limit = None, span = None, today = 0, quiet = True):
3633
+ def date_list(s = None, e = None, limit = None, span = None, today = 0, quiet = True, step=1):
3624
3634
  global debug_setting
3625
3635
  latest_date = datetime.date(datetime.now())
3626
3636
  today = 0 if today == False else 1 if today == True else today
@@ -3629,7 +3639,6 @@ def date_list(s = None, e = None, limit = None, span = None, today = 0, quiet =
3629
3639
  first = datetime.date(datetime.strptime(s, '%Y-%m-%d')) if type(s) is str else s.date() if s is not None else None
3630
3640
  last = datetime.date(datetime.strptime(e, '%Y-%m-%d')) if type(e) is str else e.date() if e is not None else None
3631
3641
  last = latest_date if last is not None and last > latest_date and today != 2 else last
3632
- step = 1
3633
3642
  if first is None and last is None:
3634
3643
  last = latest_date
3635
3644
  if span is not None:
@@ -1,3 +1,17 @@
1
+ Metadata-Version: 2.1
2
+ Name: foxesscloud
3
+ Version: 2.9.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
+
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>
@@ -125,6 +139,7 @@ get_settings() will return the battery settings and is equivalent to get_charge(
125
139
  get_peakshaving() will return the current peak shaving settings for inverters that support this work mode.
126
140
 
127
141
  get_flag() returns the current scheduler enable / support / maxsoc flags. By default support for Max Soc is set to False.
142
+ + f.max_periods / schedule['maxGroupCount'] gives the maximum number of time periods that are supported.
128
143
 
129
144
  get_schedule() returns the current work mode / soc schedule settings. The result is stored in f.schedule.
130
145
  + if the schedule returned contains any values for 'maxSoc', the f.schedule['maxsoc'] is set to True to indicate that the current inverter supports setting Max Soc in schedules and Max Soc values are set by set_schedule().
@@ -482,7 +497,7 @@ This is an example of the output from battery_info():
482
497
  ## Date Ranges
483
498
 
484
499
  ```
485
- f.date_list(s, e, limit, span, today)
500
+ f.date_list(s, e, limit, span, today, step)
486
501
  ```
487
502
 
488
503
  Returns a list of dates in the format 'YYYY-MM-DD'. This function will not return dates in the future. The last date will be yesterday or today (if today is True). All parameters are optional:
@@ -492,6 +507,7 @@ Returns a list of dates in the format 'YYYY-MM-DD'. This function will not retur
492
507
  + limit: maximum number of days. The default is 200
493
508
  + span: the range of dates. One of 'day', 'week', 'month' or 'year', '2days' or 'weekday'
494
509
  + today: 1 allows today to be included, 2 allows future dates to be included. Default is 0, date list will stop at yesterday
510
+ + step: optional days to add (default is 1)
495
511
 
496
512
  You can use 'span' as follows:
497
513
  + 'day' provides a single day
@@ -816,6 +832,17 @@ This setting can be:
816
832
 
817
833
  # Version Info
818
834
 
835
+ 2.9.6 - 2026/03/10<br>
836
+ Fix problem setting fdsoc and fdpwr from segment for set_period().
837
+ Correctly identify H1-G2 model inverter.
838
+ Add maxChargeCurrent and maxDischargeCurrent to f.battery.
839
+ Update battery charge derating with temperature so charge current is not 0.
840
+
841
+
842
+ 2.9.5 - 2026/02/27<br>
843
+ Add step parameter to date_list().
844
+ Get maxGroupCount from v3 scheduler and update f.max_periods with maximum number of time periods supported.
845
+
819
846
  2.9.4 - 2026/01/22<br>
820
847
  Rename get_battery_heating(), set_battery_heating() to get_heating() and set_heating().
821
848
  Update get_heating() to return a dictionary to simplify parameter access.
File without changes
File without changes