foxesscloud 2.9.5__tar.gz → 2.9.7__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.5
3
+ Version: 2.9.7
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
@@ -182,8 +182,9 @@ set_period() returns a period structure that can be used to build a list for set
182
182
  + max_soc: optional, default is 100
183
183
  + fdsoc: optional, default is 10. Used when setting a period with ForceDischarge mode
184
184
  + fdpwr: optional, default is 0. Used when setting a period with ForceDischarge mode
185
- + import_limit: optional, default is None (not set). Used when setting a period with ForceCharge mode
186
- + export_limit: optional, default is None (not set). Used when setting a period with ForceDischarge mode
185
+ + import_limit: optional, default is None (not set) but set to 0 if the mode is force discharge
186
+ + export_limit: optional, default is None (not set).
187
+ + pv_limit: optional, default is 1.5 x inverter rating.
187
188
  + price: optional, default None. Used to display plunge pricing for time period.
188
189
  + enable: sets whether this time segment is enable (1) or disabled (0). The default is enabled.
189
190
  + segment: optional, allows the parameters for the period to be passed as a dictionary instead of individual values.
@@ -832,6 +833,16 @@ This setting can be:
832
833
 
833
834
  # Version Info
834
835
 
836
+ 2.9.7 - 2026/03/18<br>
837
+ Add pv_limit to set_period().
838
+ Default import_linit to 0 for force discharge in set_period()
839
+
840
+ 2.9.6 - 2026/03/10<br>
841
+ Fix problem setting fdsoc and fdpwr from segment for set_period().
842
+ Correctly identify H1-G2 model inverter.
843
+ Add maxChargeCurrent and maxDischargeCurrent to f.battery.
844
+ Update battery charge derating with temperature so charge current is not 0.
845
+
835
846
  2.9.5 - 2026/02/27<br>
836
847
  Add step parameter to date_list().
837
848
  Get maxGroupCount from v3 scheduler and update f.max_periods with maximum number of time periods supported.
@@ -168,8 +168,9 @@ set_period() returns a period structure that can be used to build a list for set
168
168
  + max_soc: optional, default is 100
169
169
  + fdsoc: optional, default is 10. Used when setting a period with ForceDischarge mode
170
170
  + fdpwr: optional, default is 0. Used when setting a period with ForceDischarge mode
171
- + import_limit: optional, default is None (not set). Used when setting a period with ForceCharge mode
172
- + export_limit: optional, default is None (not set). Used when setting a period with ForceDischarge mode
171
+ + import_limit: optional, default is None (not set) but set to 0 if the mode is force discharge
172
+ + export_limit: optional, default is None (not set).
173
+ + pv_limit: optional, default is 1.5 x inverter rating.
173
174
  + price: optional, default None. Used to display plunge pricing for time period.
174
175
  + enable: sets whether this time segment is enable (1) or disabled (0). The default is enabled.
175
176
  + segment: optional, allows the parameters for the period to be passed as a dictionary instead of individual values.
@@ -818,6 +819,16 @@ This setting can be:
818
819
 
819
820
  # Version Info
820
821
 
822
+ 2.9.7 - 2026/03/18<br>
823
+ Add pv_limit to set_period().
824
+ Default import_linit to 0 for force discharge in set_period()
825
+
826
+ 2.9.6 - 2026/03/10<br>
827
+ Fix problem setting fdsoc and fdpwr from segment for set_period().
828
+ Correctly identify H1-G2 model inverter.
829
+ Add maxChargeCurrent and maxDischargeCurrent to f.battery.
830
+ Update battery charge derating with temperature so charge current is not 0.
831
+
821
832
  2.9.5 - 2026/02/27<br>
822
833
  Add step parameter to date_list().
823
834
  Get maxGroupCount from v3 scheduler and update f.max_periods with maximum number of time periods supported.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "foxesscloud"
7
- version = "2.9.5"
7
+ version = "2.9.7"
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: 11 February 2026
4
+ Updated: 18 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.1"
13
+ version = "1.11.3"
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']}")
@@ -1475,7 +1478,7 @@ def set_period(start=None, end=None, mode=None, min_soc=None, max_soc=None, fdso
1475
1478
  if quiet == 0:
1476
1479
  s = f" {hours_time(start)}-{hours_time(end)} {mode}, minsoc {min_soc}%"
1477
1480
  s += f", maxsoc {max_soc}%" if max_soc is not None and 'ForceCharge' in mode else ""
1478
- s += f", fdPwr {fdpwr}W, fdSoC {fdsoc}%" if ('ForceCharge' in mod or 'ForceDischarge' in mode) else ""
1481
+ s += f", fdPwr {fdpwr}W, fdSoC {fdsoc}%" if ('ForceCharge' in mode or 'ForceDischarge' in mode) else ""
1479
1482
  s += f", {price:.2f}p/kWh" if price is not None else ""
1480
1483
  output(s, 1)
1481
1484
  start_h, start_m = split_hours(start)
@@ -1,7 +1,7 @@
1
1
  ##################################################################################################
2
2
  """
3
3
  Module: Fox ESS Cloud using Open API
4
- Updated: 27 February 2026
4
+ Updated: 18 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.5"
13
+ version = "2.9.7"
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,
@@ -1243,7 +1246,7 @@ def build_strategy_from_schedule():
1243
1246
  ##################################################################################################
1244
1247
 
1245
1248
  # create time segment structure. Note: end time is exclusive.
1246
- def set_period(start=None, end=None, mode=None, min_soc=None, max_soc=None, fdsoc=None, fdpwr=None, import_limit=None, export_limit=None, price=None, segment=None, enable=1, quiet=1):
1249
+ def set_period(start=None, end=None, mode=None, min_soc=None, max_soc=None, fdsoc=None, fdpwr=None, import_limit=None, export_limit=None, pv_limit=None, price=None, segment=None, enable=1, quiet=1):
1247
1250
  global schedule, device
1248
1251
  if schedule is None or schedule.get('maxsoc') is None:
1249
1252
  get_schedule()
@@ -1253,10 +1256,11 @@ def set_period(start=None, end=None, mode=None, min_soc=None, max_soc=None, fdso
1253
1256
  mode = segment.get('mode')
1254
1257
  min_soc = segment.get('min_soc')
1255
1258
  max_soc = segment.get('max_soc')
1256
- fdsoc = segment.get('fdSoc')
1257
- fdpwr = segment.get('fdPwr')
1259
+ fdsoc = segment.get('fdsoc')
1260
+ fdpwr = segment.get('fdpwr')
1258
1261
  import_limit = segment.get('import_limit')
1259
1262
  export_limit = segment.get('export_limit')
1263
+ pv_limit = segment.get('pv_limit')
1260
1264
  price = segment.get('price')
1261
1265
  start = time_hours(start)
1262
1266
  # adjust exclusive time to inclusive
@@ -1276,6 +1280,8 @@ def set_period(start=None, end=None, mode=None, min_soc=None, max_soc=None, fdso
1276
1280
  fdsoc = min_soc if fdsoc is None else fdsoc
1277
1281
  power = (device['power'] * 1000) if device.get('power') is not None else None
1278
1282
  fdpwr = power if fdpwr is None and device.get('power') is not None and ('ForceCharge' in mode or 'ForceDischarge' in mode) else fdpwr
1283
+ pv_limit = int(1.5 * power) if pv_limit is None and device.get('power') is not None and ('ForceCharge' in mode or 'ForceDischarge' in mode) else pv_limit
1284
+ import_limit = 0 if import_limit is None and 'ForceDischarge' in mode else import_limit
1279
1285
  fdpwr = 0 if fdpwr is None else fdpwr
1280
1286
  if min_soc < 0 or min_soc > 100:
1281
1287
  output(f"set_period(): ** min_soc must be between 0 and 100")
@@ -1292,14 +1298,17 @@ def set_period(start=None, end=None, mode=None, min_soc=None, max_soc=None, fdso
1292
1298
  if quiet == 0:
1293
1299
  s = f" {hours_time(start)}-{hours_time(end)} {mode}, minsoc {min_soc}%"
1294
1300
  s += f", maxsoc {max_soc}%" if max_soc is not None and 'ForceCharge' in mode else ""
1295
- s += f", fdPwr {fdpwr}W, fdSoC {fdsoc}%" if ('ForceCharge' in mode or 'ForceDischarge' in mode) else ""
1301
+ s += f", fdPwr {int(fdpwr)}W, fdSoC {fdsoc}%" if ('ForceCharge' in mode or 'ForceDischarge' in mode) else ""
1302
+ s += f", exportLimit {int(export_limit)}W" if export_limit is not None else ""
1303
+ s += f", importLimit {int(import_limit)}W" if import_limit is not None else ""
1304
+ s += f", pvLimit {int(pv_limit)}W" if pv_limit is not None else ""
1296
1305
  s += f", {price:.2f}p/kWh" if price is not None else ""
1297
1306
  output(s, 1)
1298
1307
  start_hour, start_minute = split_hours(start)
1299
1308
  end_hour, end_minute = split_hours(end)
1300
1309
  period = {'enable': enable, 'startHour': start_hour, 'startMinute': start_minute, 'endHour': end_hour, 'endMinute': end_minute, 'workMode': mode,
1301
1310
  'extraParam': {'minSocOnGrid': int(min_soc), 'fdSoc': int(fdsoc), 'fdPwr': int(fdpwr), 'maxSoc': max_soc, 'importLimit': import_limit,
1302
- 'exportLimit': export_limit }}
1311
+ 'exportLimit': export_limit, 'pvLimit': pv_limit}}
1303
1312
  return period
1304
1313
 
1305
1314
  # set a schedule from a period or list of time segment periods
@@ -2955,7 +2964,7 @@ def charge_needed(forecast=None, consumption=None, update_settings=0, timed_mode
2955
2964
  reserve = capacity * min_soc / 100
2956
2965
  # charge current may be derated based on temperature
2957
2966
  charge_current = device_current if charge_config['charge_current'] is None else charge_config['charge_current']
2958
- if bms_charge_current is not None and charge_current > bms_charge_current:
2967
+ if bms_charge_current is not None and bms_charge_current < charge_current:
2959
2968
  charge_current = bms_charge_current
2960
2969
  volt_curve = charge_config['volt_curve']
2961
2970
  nominal_soc = charge_config['nominal_soc']
@@ -2976,10 +2985,6 @@ def charge_needed(forecast=None, consumption=None, update_settings=0, timed_mode
2976
2985
  output(f" Resistance: {bat_resistance:.2f} ohms")
2977
2986
  output(f" Nominal OCV: {bat_ocv:.1f}V at {nominal_soc}% SoC")
2978
2987
  output(f" Losses: {charge_loss * 100:.1f}% charge / {discharge_loss * 100:.1f}% discharge", 2)
2979
- # charge current may be derated based on temperature
2980
- charge_current = device_current if charge_config['charge_current'] is None else charge_config['charge_current']
2981
- if charge_current > bms_charge_current:
2982
- charge_current = bms_charge_current
2983
2988
  # inverter losses
2984
2989
  inverter_power = charge_config['inverter_power'] if charge_config['inverter_power'] is not None else round(device_power, 0) * 25
2985
2990
  operating_loss = inverter_power / 1000
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: foxesscloud
3
- Version: 2.9.5
3
+ Version: 2.9.7
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
@@ -182,8 +182,9 @@ set_period() returns a period structure that can be used to build a list for set
182
182
  + max_soc: optional, default is 100
183
183
  + fdsoc: optional, default is 10. Used when setting a period with ForceDischarge mode
184
184
  + fdpwr: optional, default is 0. Used when setting a period with ForceDischarge mode
185
- + import_limit: optional, default is None (not set). Used when setting a period with ForceCharge mode
186
- + export_limit: optional, default is None (not set). Used when setting a period with ForceDischarge mode
185
+ + import_limit: optional, default is None (not set) but set to 0 if the mode is force discharge
186
+ + export_limit: optional, default is None (not set).
187
+ + pv_limit: optional, default is 1.5 x inverter rating.
187
188
  + price: optional, default None. Used to display plunge pricing for time period.
188
189
  + enable: sets whether this time segment is enable (1) or disabled (0). The default is enabled.
189
190
  + segment: optional, allows the parameters for the period to be passed as a dictionary instead of individual values.
@@ -832,6 +833,16 @@ This setting can be:
832
833
 
833
834
  # Version Info
834
835
 
836
+ 2.9.7 - 2026/03/18<br>
837
+ Add pv_limit to set_period().
838
+ Default import_linit to 0 for force discharge in set_period()
839
+
840
+ 2.9.6 - 2026/03/10<br>
841
+ Fix problem setting fdsoc and fdpwr from segment for set_period().
842
+ Correctly identify H1-G2 model inverter.
843
+ Add maxChargeCurrent and maxDischargeCurrent to f.battery.
844
+ Update battery charge derating with temperature so charge current is not 0.
845
+
835
846
  2.9.5 - 2026/02/27<br>
836
847
  Add step parameter to date_list().
837
848
  Get maxGroupCount from v3 scheduler and update f.max_periods with maximum number of time periods supported.
File without changes
File without changes