foxesscloud 2.6.4__py3-none-any.whl → 2.6.5__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 +15 -15
- foxesscloud/openapi.py +66 -157
- {foxesscloud-2.6.4.dist-info → foxesscloud-2.6.5.dist-info}/METADATA +17 -1
- foxesscloud-2.6.5.dist-info/RECORD +7 -0
- foxesscloud-2.6.4.dist-info/RECORD +0 -7
- {foxesscloud-2.6.4.dist-info → foxesscloud-2.6.5.dist-info}/LICENCE +0 -0
- {foxesscloud-2.6.4.dist-info → foxesscloud-2.6.5.dist-info}/WHEEL +0 -0
- {foxesscloud-2.6.4.dist-info → foxesscloud-2.6.5.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: 43 October 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.7.
|
13
|
+
version = "1.7.7"
|
14
14
|
print(f"FoxESS-Cloud version {version}")
|
15
15
|
|
16
16
|
debug_setting = 1
|
@@ -593,13 +593,13 @@ battery_params = {
|
|
593
593
|
1: {'table': [ 0, 2, 10, 15, 25, 50, 50, 50, 50, 50, 30, 20, 0],
|
594
594
|
'step': 5,
|
595
595
|
'offset': 5,
|
596
|
-
'charge_loss': 0.
|
597
|
-
'discharge_loss': 0.
|
596
|
+
'charge_loss': 0.974,
|
597
|
+
'discharge_loss': 0.974},
|
598
598
|
2: {'table': [ 0, 2, 10, 10, 15, 15, 25, 50, 50, 50, 30, 20, 0],
|
599
599
|
'step': 5,
|
600
600
|
'offset': 5,
|
601
601
|
'charge_loss': 1.08,
|
602
|
-
'discharge_loss': 0.
|
602
|
+
'discharge_loss': 0.95},
|
603
603
|
}
|
604
604
|
|
605
605
|
def get_battery(info=1):
|
@@ -1022,7 +1022,7 @@ def get_remote_settings(key):
|
|
1022
1022
|
result = response.json().get('result')
|
1023
1023
|
if result is None:
|
1024
1024
|
errno = response.json().get('errno')
|
1025
|
-
output(f"** get_remote_settings(), no result data, {errno_message(
|
1025
|
+
output(f"** get_remote_settings(), no result data, {errno_message(response)}")
|
1026
1026
|
return None
|
1027
1027
|
values = result.get('values')
|
1028
1028
|
if values is None:
|
@@ -1436,7 +1436,7 @@ def set_schedule(periods=None, template=None, enable=True):
|
|
1436
1436
|
# d = day 'YYYY-MM-DD'. Can also include 'HH:MM' in 'hour' mode
|
1437
1437
|
# v = list of variables to get
|
1438
1438
|
# summary = 0: raw data, 1: add max, min, sum, 2: summarise and drop raw data, 3: calculate state
|
1439
|
-
# save = "xxxxx": save the raw results to
|
1439
|
+
# save = "xxxxx": save the raw results to xxxxx_history_<time_span>_<d>.json
|
1440
1440
|
# load = "<file>": load the raw results from <file>
|
1441
1441
|
# plot = 0: no plot, 1: plot variables separately, 2: combine variables
|
1442
1442
|
# station = 0: use device_id, 1: use station_id
|
@@ -1504,7 +1504,7 @@ def get_raw(time_span='hour', d=None, v=None, summary=1, save=None, load=None, p
|
|
1504
1504
|
result = json.load(file)
|
1505
1505
|
file.close()
|
1506
1506
|
if save is not None:
|
1507
|
-
file_name = save + "
|
1507
|
+
file_name = save + "_history_" + time_span + "_" + d[0:10].replace('-','') + ".txt"
|
1508
1508
|
file = open(storage + file_name, 'w', encoding='utf-8')
|
1509
1509
|
json.dump(result, file, indent=4, ensure_ascii= False)
|
1510
1510
|
file.close()
|
@@ -1709,7 +1709,7 @@ get_history = get_raw
|
|
1709
1709
|
# d = day 'YYYY-MM-DD'
|
1710
1710
|
# v = list of report variables to get
|
1711
1711
|
# summary = 0, 1, 2: do a quick total energy report for a day
|
1712
|
-
# save = "xxxxx": save the report results to
|
1712
|
+
# save = "xxxxx": save the report results to xxxxx_report_<time_span>_<d>.json
|
1713
1713
|
# load = "<file>": load the report results from <file>
|
1714
1714
|
# plot = 0: no plot, 1 = plot variables separately, 2 = combine variables
|
1715
1715
|
# station = 0: use device_id, 1 = use station_id
|
@@ -1839,7 +1839,7 @@ def get_report(report_type='day', d=None, v=None, summary=1, save=None, load=Non
|
|
1839
1839
|
result = json.load(file)
|
1840
1840
|
file.close()
|
1841
1841
|
elif save is not None:
|
1842
|
-
file_name = save + "
|
1842
|
+
file_name = save + "_report_" + report_type + "_" + d.replace('-','') + ".txt"
|
1843
1843
|
file = open(storage + file_name, 'w', encoding='utf-8')
|
1844
1844
|
json.dump(result, file, indent=4, ensure_ascii= False)
|
1845
1845
|
file.close()
|
@@ -2838,7 +2838,7 @@ charge_needed_app_key = "awcr5gro2v13oher3v1qu6hwnovp28"
|
|
2838
2838
|
def charge_needed(forecast=None, update_settings=0, timed_mode=None, show_data=None, show_plot=None, run_after=None, reload=2,
|
2839
2839
|
forecast_times=None, force_charge=0, test_time=None, test_soc=None, test_charge=None, **settings):
|
2840
2840
|
global device, seasonality, solcast_api_key, debug_setting, tariff, solar_arrays, legend_location, time_shift, charge_needed_app_key
|
2841
|
-
global timed_strategy, steps_per_hour, base_time, storage, battery,
|
2841
|
+
global timed_strategy, steps_per_hour, base_time, storage, battery, battery_params
|
2842
2842
|
print(f"\n---------------- charge_needed ----------------")
|
2843
2843
|
# validate parameters
|
2844
2844
|
args = locals()
|
@@ -2945,8 +2945,8 @@ def charge_needed(forecast=None, update_settings=0, timed_mode=None, show_data=N
|
|
2945
2945
|
bat_power = 0.0
|
2946
2946
|
temperature = 30
|
2947
2947
|
bms_charge_current = 15
|
2948
|
-
charge_loss =
|
2949
|
-
discharge_loss =
|
2948
|
+
charge_loss = battery_params[2]['charge_loss']
|
2949
|
+
discharge_loss = battery_params[2]['discharge_loss']
|
2950
2950
|
bat_current = 0.0
|
2951
2951
|
device_power = 6.0
|
2952
2952
|
device_current = 35
|
@@ -2968,8 +2968,8 @@ def charge_needed(forecast=None, update_settings=0, timed_mode=None, show_data=N
|
|
2968
2968
|
output(f"Battery capacity could not be estimated. Please add the parameter 'capacity=xx' in kWh")
|
2969
2969
|
return None
|
2970
2970
|
bms_charge_current = battery.get('charge_rate')
|
2971
|
-
charge_loss = battery['charge_loss'] if battery.get('charge_loss') is not None else
|
2972
|
-
discharge_loss = battery['discharge_loss'] if battery.get('discharge_loss') is not None else
|
2971
|
+
charge_loss = battery['charge_loss'] if battery.get('charge_loss') is not None else 0.974
|
2972
|
+
discharge_loss = battery['discharge_loss'] if battery.get('discharge_loss') is not None else 0.974
|
2973
2973
|
device_power = device.get('power')
|
2974
2974
|
device_current = device.get('max_charge_current')
|
2975
2975
|
model = device.get('deviceType')
|
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: 14 October 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.6.
|
13
|
+
version = "2.6.5"
|
14
14
|
print(f"FoxESS-Cloud Open API version {version}")
|
15
15
|
|
16
16
|
debug_setting = 1
|
@@ -281,7 +281,7 @@ var_table = None
|
|
281
281
|
var_list = None
|
282
282
|
|
283
283
|
def get_vars():
|
284
|
-
global var_table, var_list, debug_setting, messages, lang
|
284
|
+
global var_table, var_list, debug_setting, messages, lang
|
285
285
|
if api_key is None:
|
286
286
|
output(f"** please generate an API Key at foxesscloud.com and provide this (f.api_key='your API key')")
|
287
287
|
return None
|
@@ -558,13 +558,13 @@ battery_params = {
|
|
558
558
|
1: {'table': [ 0, 2, 10, 15, 25, 50, 50, 50, 50, 50, 30, 20, 0],
|
559
559
|
'step': 5,
|
560
560
|
'offset': 5,
|
561
|
-
'charge_loss': 0.
|
562
|
-
'discharge_loss': 0.
|
561
|
+
'charge_loss': 0.974,
|
562
|
+
'discharge_loss': 0.974},
|
563
563
|
2: {'table': [ 0, 2, 10, 10, 15, 15, 25, 50, 50, 50, 30, 20, 0],
|
564
564
|
'step': 5,
|
565
565
|
'offset': 5,
|
566
566
|
'charge_loss': 1.080,
|
567
|
-
'discharge_loss': 0.
|
567
|
+
'discharge_loss': 0.95},
|
568
568
|
}
|
569
569
|
|
570
570
|
def get_battery(info=0, v=None):
|
@@ -615,7 +615,7 @@ def get_batteries(info=0):
|
|
615
615
|
##################################################################################################
|
616
616
|
|
617
617
|
def get_charge():
|
618
|
-
global
|
618
|
+
global device_sn, battery_settings, debug_setting
|
619
619
|
if get_device() is None:
|
620
620
|
return None
|
621
621
|
if battery_settings is None:
|
@@ -647,7 +647,7 @@ def time_period(t, n):
|
|
647
647
|
return result
|
648
648
|
|
649
649
|
def set_charge(ch1=None, st1=None, en1=None, ch2=None, st2=None, en2=None, force = 0, enable=1):
|
650
|
-
global
|
650
|
+
global device_sn, battery_settings, debug_setting, time_period_vars
|
651
651
|
if get_device() is None:
|
652
652
|
return None
|
653
653
|
if battery_settings is None:
|
@@ -723,7 +723,7 @@ def set_charge(ch1=None, st1=None, en1=None, ch2=None, st2=None, en2=None, force
|
|
723
723
|
##################################################################################################
|
724
724
|
|
725
725
|
def get_min():
|
726
|
-
global
|
726
|
+
global device_sn, battery_settings, debug_setting
|
727
727
|
if get_device() is None:
|
728
728
|
return None
|
729
729
|
if battery_settings is None:
|
@@ -747,7 +747,7 @@ def get_min():
|
|
747
747
|
##################################################################################################
|
748
748
|
|
749
749
|
def set_min(minSocOnGrid = None, minSoc = None, force = 0):
|
750
|
-
global
|
750
|
+
global device_sn, schedule, battery_settings, debug_setting
|
751
751
|
if get_device() is None:
|
752
752
|
return None
|
753
753
|
if schedule['enable'] == True:
|
@@ -801,160 +801,71 @@ def get_settings():
|
|
801
801
|
# get remote settings
|
802
802
|
##################################################################################################
|
803
803
|
|
804
|
-
|
805
|
-
named_settings =
|
806
|
-
merge_settings = { # keys to add
|
807
|
-
'WorkMode': {'keys': {
|
808
|
-
'h115__': 'operation_mode__work_mode',
|
809
|
-
'h116__': 'operation_mode__work_mode',
|
810
|
-
'h117__': 'operation_mode__work_mode',
|
811
|
-
# 'k106__': 'operation_mode__work_mode',
|
812
|
-
},
|
813
|
-
'values': ['SelfUse', 'Feedin', 'Backup']},
|
814
|
-
'BatteryVolt': {'keys': {
|
815
|
-
'h115__': ['h115__14', 'h115__15', 'h115__16'],
|
816
|
-
'h116__': ['h116__15', 'h116__16', 'h116__17'],
|
817
|
-
'h117__': ['h117__15', 'h117__16', 'h117__17'],
|
818
|
-
# 'k106__': ['k106__xx', 'k106__xx', 'k106__xx'],
|
819
|
-
},
|
820
|
-
'type': 'list',
|
821
|
-
'valueType': 'float',
|
822
|
-
'unit': 'V'},
|
823
|
-
'BatteryTemp': {'keys': {
|
824
|
-
'h115__': 'h115__17',
|
825
|
-
'h116__': 'h116__18',
|
826
|
-
'h117__': 'h117__18',
|
827
|
-
# 'k106__': 'k106__xx',
|
828
|
-
},
|
829
|
-
'type': 'list',
|
830
|
-
'valueType': 'int',
|
831
|
-
'unit': '℃'},
|
832
|
-
}
|
804
|
+
# store for named settings info
|
805
|
+
named_settings = {}
|
833
806
|
|
834
|
-
def
|
835
|
-
global
|
836
|
-
if get_device() is None:
|
837
|
-
return None
|
838
|
-
if remote_settings is None:
|
839
|
-
output(f"getting ui settings", 2)
|
840
|
-
params = {'id': device_id}
|
841
|
-
response = signed_get(path="/generic/v0/device/setting/ui", params=params)
|
842
|
-
if response.status_code != 200:
|
843
|
-
output(f"** get_ui() got response code {response.status_code}: {response.reason}")
|
844
|
-
return None
|
845
|
-
result = response.json().get('result')
|
846
|
-
if result is None:
|
847
|
-
errno = response.json().get('errno')
|
848
|
-
output(f"** get_ui(), no result data, {errno_message(errno)}")
|
849
|
-
return None
|
850
|
-
remote_settings = result
|
851
|
-
protocol = remote_settings['protocol'].lower().replace('xx','__')
|
852
|
-
named_settings = {'_protocol': protocol}
|
853
|
-
volt_n = 0
|
854
|
-
volt_keys = []
|
855
|
-
for p in remote_settings['parameters']:
|
856
|
-
if p['name'][:11] == 'BatteryVolt': # merge BatteryVolts
|
857
|
-
output(f" found {p['name']} with key {p['key']}", 2)
|
858
|
-
volt_n += 1
|
859
|
-
volt_keys.append(p['key'])
|
860
|
-
if volt_n == 3:
|
861
|
-
named_settings['BatteryVolt'] = {'keys': volt_keys, 'type': 'list', 'valueType': 'float', 'unit': p['properties'][0]['unit']}
|
862
|
-
elif volt_n > 3:
|
863
|
-
print(f"** get_ui(): more than 3 groups found for BatteryVolt, n={volt_n}")
|
864
|
-
elif p['name'][:11] == 'BatteryTemp':
|
865
|
-
output(f" found {p['name']} with key {p['key']}", 2)
|
866
|
-
named_settings['BatteryTemp'] = {'keys': p['key'], 'type': 'list', 'valueType': 'int', 'unit': p['properties'][0]['unit']}
|
867
|
-
else:
|
868
|
-
items = []
|
869
|
-
block = p['block'] and len(p['properties']) > 1
|
870
|
-
for e in p['properties']:
|
871
|
-
valueType = e['elemType']['valueType']
|
872
|
-
item = {'name': e['key'].replace(protocol,'')} if block else {'key': e['key']} #, 'group': p['name']}
|
873
|
-
if e['elemType'].get('uiItems') is not None:
|
874
|
-
item['values'] = e['elemType']['uiItems']
|
875
|
-
elif e.get('range') is not None:
|
876
|
-
item['range'] = e['range']
|
877
|
-
item['valueType'] = 'float' if type(e['range']['hi']) is float else 'int'
|
878
|
-
else:
|
879
|
-
item['type'] = valueType
|
880
|
-
if e.get('unit') is not None and len(e['unit']) > 0:
|
881
|
-
item['unit'] = e['unit']
|
882
|
-
if block:
|
883
|
-
items.append(item)
|
884
|
-
else:
|
885
|
-
named_settings[e['name']] = item
|
886
|
-
if block:
|
887
|
-
named_settings[p['name']] = {'key': p['key'], 'type': 'block', 'items': items}
|
888
|
-
for name in merge_settings.keys():
|
889
|
-
if named_settings.get(name) is None and merge_settings[name]['keys'].get(protocol) is not None:
|
890
|
-
named_settings[name] = {'keys': merge_settings[name]['keys'][protocol]}
|
891
|
-
for k in merge_settings[name].keys():
|
892
|
-
if k != 'keys':
|
893
|
-
named_settings[name][k] = merge_settings[name][k]
|
894
|
-
return remote_settings
|
895
|
-
|
896
|
-
def get_remote_settings(key):
|
897
|
-
global token, device_id, debug_setting, messages
|
807
|
+
def get_remote_settings(name):
|
808
|
+
global device_sn, debug_setting, messages, name_data
|
898
809
|
if get_device() is None:
|
899
810
|
return None
|
900
811
|
output(f"getting remote settings", 2)
|
901
|
-
if
|
812
|
+
if name is None:
|
902
813
|
return None
|
903
|
-
if type(
|
814
|
+
if type(name) is list:
|
904
815
|
values = {}
|
905
|
-
for
|
906
|
-
v = get_remote_settings(
|
816
|
+
for n in name:
|
817
|
+
v = get_remote_settings(n)
|
907
818
|
if v is None:
|
908
|
-
|
819
|
+
continue
|
909
820
|
for x in v.keys():
|
910
821
|
values[x] = v[x]
|
911
822
|
return values
|
912
|
-
|
913
|
-
response =
|
823
|
+
body = {'sn': device_sn, 'key': name}
|
824
|
+
response = signed_post(path="/op/v0/device/setting/get", body=body)
|
914
825
|
if response.status_code != 200:
|
915
826
|
output(f"** get_remote_settings() got response code {response.status_code}: {response.reason}")
|
916
827
|
return None
|
917
828
|
result = response.json().get('result')
|
918
829
|
if result is None:
|
919
830
|
errno = response.json().get('errno')
|
920
|
-
output(f"** get_remote_settings(), no result data, {errno_message(
|
831
|
+
output(f"** get_remote_settings(), no result data, {errno_message(response)}")
|
921
832
|
return None
|
922
|
-
|
923
|
-
|
924
|
-
|
833
|
+
named_settings[name] = result
|
834
|
+
value = result.get('value')
|
835
|
+
if value is None:
|
836
|
+
output(f"** get_remote_settings(), no value for {name}")
|
925
837
|
return None
|
926
|
-
return
|
838
|
+
return value
|
927
839
|
|
928
840
|
def get_named_settings(name):
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
return result
|
935
|
-
if named_settings is None or named_settings.get(name) is None:
|
936
|
-
output(f"** get_named_settings(): {name} was not recognised")
|
841
|
+
return get_remote_settings(name)
|
842
|
+
|
843
|
+
def set_named_setting(name, value):
|
844
|
+
global device_sn, debug_setting
|
845
|
+
if get_device() is None:
|
937
846
|
return None
|
938
|
-
|
939
|
-
|
940
|
-
|
847
|
+
if type(name) is list:
|
848
|
+
count = 0
|
849
|
+
for (n, v) in name:
|
850
|
+
result = set_named_settings(name=n, value=v)
|
851
|
+
if result is not None:
|
852
|
+
count += 1
|
853
|
+
return count
|
854
|
+
output(f"\nSetting {name} to {value}", 1)
|
855
|
+
body = {'sn': device_sn, 'key': name, 'value': f"{value}"}
|
856
|
+
setting_delay()
|
857
|
+
response = signed_post(path="/op/v0/device/setting/set", body=body)
|
858
|
+
if response.status_code != 200:
|
859
|
+
output(f"** set_named_settings(): ({name}, {value}) got response code {response.status_code}: {response.reason}")
|
941
860
|
return None
|
942
|
-
|
943
|
-
|
944
|
-
|
945
|
-
|
861
|
+
errno = response.json().get('errno')
|
862
|
+
if errno != 0:
|
863
|
+
if errno == 44096:
|
864
|
+
output(f"** cannot update settings when schedule is active")
|
865
|
+
else:
|
866
|
+
output(f"** set_named_settings(): ({name}, {value}) {errno_message(response)}")
|
946
867
|
return None
|
947
|
-
|
948
|
-
value_type = named_settings[name].get('valueType')
|
949
|
-
if result_type is None:
|
950
|
-
v = result.get([k for k in result.keys()][0])
|
951
|
-
return v if value_type is None else c_float(v) if value_type == 'float' else c_int(v)
|
952
|
-
if result_type == 'list':
|
953
|
-
values = []
|
954
|
-
for k in sorted(result.keys()):
|
955
|
-
values.append(result[k] if value_type is None else c_float(result[k]) if value_type == 'float' else c_int(result[k]))
|
956
|
-
return values
|
957
|
-
return result
|
868
|
+
return 1
|
958
869
|
|
959
870
|
##################################################################################################
|
960
871
|
# wrappers for named settings
|
@@ -964,8 +875,6 @@ work_mode = None
|
|
964
875
|
|
965
876
|
def get_work_mode():
|
966
877
|
global work_mode
|
967
|
-
# print(f"** get_work_mode(): not available via Open API")
|
968
|
-
return None
|
969
878
|
if get_device() is None:
|
970
879
|
return None
|
971
880
|
work_mode = get_named_settings('WorkMode')
|
@@ -983,6 +892,8 @@ temp_slots_per_battery = 8
|
|
983
892
|
|
984
893
|
def get_cell_temps(nbat=8):
|
985
894
|
global temp_slots_per_battery
|
895
|
+
print(f"** get_cell_temps(): not available via Open API")
|
896
|
+
return None
|
986
897
|
values = get_named_settings('BatteryTemp')
|
987
898
|
if values is None:
|
988
899
|
return None
|
@@ -1008,9 +919,7 @@ work_modes = ['SelfUse', 'Feedin', 'Backup', 'ForceCharge', 'ForceDischarge']
|
|
1008
919
|
settable_modes = work_modes[:3]
|
1009
920
|
|
1010
921
|
def set_work_mode(mode, force = 0):
|
1011
|
-
global
|
1012
|
-
print(f"** set_work_mode(): not available via Open API")
|
1013
|
-
return None
|
922
|
+
global device_sn, work_modes, work_mode, debug_setting
|
1014
923
|
if get_device() is None:
|
1015
924
|
return None
|
1016
925
|
if mode not in settable_modes:
|
@@ -1022,7 +931,7 @@ def set_work_mode(mode, force = 0):
|
|
1022
931
|
return None
|
1023
932
|
set_schedule(enable=0)
|
1024
933
|
output(f"\nSetting work mode: {mode}", 1)
|
1025
|
-
body = {'sn': device_sn, 'key': '
|
934
|
+
body = {'sn': device_sn, 'key': 'WorkMode', 'value': mode}
|
1026
935
|
setting_delay()
|
1027
936
|
response = signed_post(path="/op/v0/device/setting/set", body=body)
|
1028
937
|
if response.status_code != 200:
|
@@ -1180,7 +1089,7 @@ def set_period(start=None, end=None, mode=None, min_soc=None, max_soc=None, fdso
|
|
1180
1089
|
|
1181
1090
|
# set a schedule from a period or list of time segment periods
|
1182
1091
|
def set_schedule(periods=None, enable=True):
|
1183
|
-
global
|
1092
|
+
global device_sn, debug_setting, schedule
|
1184
1093
|
if get_flag() is None:
|
1185
1094
|
return None
|
1186
1095
|
if schedule.get('support') == False:
|
@@ -1279,7 +1188,7 @@ def get_real(v = None):
|
|
1279
1188
|
# d = day 'YYYY-MM-DD'. Can also include 'HH:MM' in 'hour' mode
|
1280
1189
|
# v = list of variables to get
|
1281
1190
|
# summary = 0: raw data, 1: add max, min, sum, 2: summarise and drop raw data, 3: calculate state
|
1282
|
-
# save = "xxxxx": save the raw results to
|
1191
|
+
# save = "xxxxx": save the raw results to xxxxx_history_<time_span>_<d>.json
|
1283
1192
|
# load = "<file>": load the raw results from <file>
|
1284
1193
|
# plot = 0: no plot, 1: plot variables separately, 2: combine variables
|
1285
1194
|
##################################################################################################
|
@@ -1294,7 +1203,7 @@ sample_time = 5.0 # 5 minutes default
|
|
1294
1203
|
sample_rounding = 2 # round to 30 seconds
|
1295
1204
|
|
1296
1205
|
def get_history(time_span='hour', d=None, v=None, summary=1, save=None, load=None, plot=0):
|
1297
|
-
global
|
1206
|
+
global device_sn, debug_setting, var_list, invert_ct2, tariff, max_power_kw, sample_rounding, sample_time, residual_scale, storage
|
1298
1207
|
if get_device() is None:
|
1299
1208
|
return None
|
1300
1209
|
time_span = time_span.lower()
|
@@ -1546,7 +1455,7 @@ def report_value_profile(result):
|
|
1546
1455
|
# d = day 'YYYY-MM-DD'
|
1547
1456
|
# v = list of report variables to get
|
1548
1457
|
# summary = 0, 1, 2: do a quick total energy report for a day
|
1549
|
-
# save = "xxxxx": save the report results to
|
1458
|
+
# save = "xxxxx": save the report results to xxxxx_report_<time_span>_<d>.json
|
1550
1459
|
# load = "<file>": load the report results from <file>
|
1551
1460
|
# plot = 0: no plot, 1 = plot variables separately, 2 = combine variables
|
1552
1461
|
##################################################################################################
|
@@ -1560,7 +1469,7 @@ fix_value_threshold = 200000000.0
|
|
1560
1469
|
fix_value_mask = 0x0000FFFF
|
1561
1470
|
|
1562
1471
|
def get_report(dimension='day', d=None, v=None, summary=1, save=None, load=None, plot=0):
|
1563
|
-
global
|
1472
|
+
global device_sn, var_list, debug_setting, report_vars, storage
|
1564
1473
|
if get_device() is None:
|
1565
1474
|
return None
|
1566
1475
|
# process list of days
|
@@ -2642,7 +2551,7 @@ charge_needed_app_key = "awcr5gro2v13oher3v1qu6hwnovp28"
|
|
2642
2551
|
def charge_needed(forecast=None, update_settings=0, timed_mode=None, show_data=None, show_plot=None, run_after=None, reload=2,
|
2643
2552
|
forecast_times=None, force_charge=0, test_time=None, test_soc=None, test_charge=None, **settings):
|
2644
2553
|
global device, seasonality, solcast_api_key, debug_setting, tariff, solar_arrays, legend_location, time_shift, charge_needed_app_key
|
2645
|
-
global timed_strategy, steps_per_hour, base_time, storage, battery,
|
2554
|
+
global timed_strategy, steps_per_hour, base_time, storage, battery, battery_params
|
2646
2555
|
print(f"\n---------------- charge_needed ----------------")
|
2647
2556
|
# validate parameters
|
2648
2557
|
args = locals()
|
@@ -2749,8 +2658,8 @@ def charge_needed(forecast=None, update_settings=0, timed_mode=None, show_data=N
|
|
2749
2658
|
bat_power = 0.0
|
2750
2659
|
temperature = 30
|
2751
2660
|
bms_charge_current = 15
|
2752
|
-
charge_loss =
|
2753
|
-
discharge_loss =
|
2661
|
+
charge_loss = battery_params[2]['charge_loss']
|
2662
|
+
discharge_loss = battery_params[2]['discharge_loss']
|
2754
2663
|
bat_current = 0.0
|
2755
2664
|
device_power = 6.0
|
2756
2665
|
device_current = 35
|
@@ -2772,8 +2681,8 @@ def charge_needed(forecast=None, update_settings=0, timed_mode=None, show_data=N
|
|
2772
2681
|
output(f"Battery capacity could not be estimated. Please add the parameter 'capacity=xx' in kWh")
|
2773
2682
|
return None
|
2774
2683
|
bms_charge_current = battery.get('charge_rate')
|
2775
|
-
charge_loss = battery['charge_loss'] if battery.get('charge_loss') is not None else
|
2776
|
-
discharge_loss = battery['discharge_loss'] if battery.get('discharge_loss') is not None else
|
2684
|
+
charge_loss = battery['charge_loss'] if battery.get('charge_loss') is not None else 0.974
|
2685
|
+
discharge_loss = battery['discharge_loss'] if battery.get('discharge_loss') is not None else 0.974
|
2777
2686
|
device_power = device.get('power')
|
2778
2687
|
device_current = device.get('max_charge_current')
|
2779
2688
|
model = device.get('deviceType')
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: foxesscloud
|
3
|
-
Version: 2.6.
|
3
|
+
Version: 2.6.5
|
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
|
@@ -111,6 +111,7 @@ f.get_charge()
|
|
111
111
|
f.get_min()
|
112
112
|
f.get_flag()
|
113
113
|
f.get_schedule()
|
114
|
+
f.get_named_settings(name)
|
114
115
|
|
115
116
|
```
|
116
117
|
Each of these calls will return a dictionary or list containing the relevant information.
|
@@ -130,6 +131,10 @@ get_flag() returns the current scheduler enable / support / maxsoc flags
|
|
130
131
|
|
131
132
|
get_schedule() returns the current work mode / soc schedule settings. The result is stored in f.schedule.
|
132
133
|
|
134
|
+
get_named_settings() returns the value of a named setting. If 'name' is a list, it returns a list of values.
|
135
|
+
+ f.named_settings is updated. This is dictionary of information and current value, indexed by 'name.
|
136
|
+
+ the only name currently supported by Fox is 'ExportLimit' and this is only available for H3 inverters.
|
137
|
+
|
133
138
|
|
134
139
|
## Inverter Settings
|
135
140
|
You can change inverter settings using:
|
@@ -140,6 +145,7 @@ f.set_charge(ch1, st1, en1, ch2, st2, en2, enable)
|
|
140
145
|
f.set_period(start, end, mode, min_soc, max_soc, fdsoc, fdpwr, price, segment)
|
141
146
|
f.charge_periods(st0, en0, st1, en1, st2, en2, min_soc, target_soc, start_soc)
|
142
147
|
f.set_schedule(periods, enable)
|
148
|
+
f.set_named_settings(name, value)
|
143
149
|
```
|
144
150
|
|
145
151
|
set_min() applies new SoC settings to the inverter. The parameters update battery_settings:
|
@@ -180,6 +186,10 @@ set_schedule() configures a list of scheduled work mode / soc changes with enabl
|
|
180
186
|
+ periods: a time segment or list of time segments created using f.set_period().
|
181
187
|
+ enable: 1 to enable schedules, 0 to disable schedules. The default is 1.
|
182
188
|
|
189
|
+
set_named_settings() sets the 'name' setting to 'value'.
|
190
|
+
+ 'name' may also be a list of (name, value) pairs.
|
191
|
+
+ the only 'name' currently supported by Fox is 'ExportLimit' on H3 inverters
|
192
|
+
|
183
193
|
|
184
194
|
## Real Time Data
|
185
195
|
Real time data reports the latest values for inverter variables, collected every 5 minutes:
|
@@ -787,6 +797,12 @@ This setting can be:
|
|
787
797
|
|
788
798
|
# Version Info
|
789
799
|
|
800
|
+
2.6.5<br>
|
801
|
+
Add get_named_settings() and set_named_settings().
|
802
|
+
Update get_work_mode() and set_work_mode() to use named settings (still doesn't work though as blocked by Fox)
|
803
|
+
Updated get_history() and get_report() saved filenames to use _history_ and _report_ for consistency.
|
804
|
+
Update calibration of 'charge_loss' and 'discharge_loss'.
|
805
|
+
|
790
806
|
2.6.4<br>
|
791
807
|
Increase default plungs_slots from 6 to 8.
|
792
808
|
Correct battery capacity in get_batteries().
|
@@ -0,0 +1,7 @@
|
|
1
|
+
foxesscloud/foxesscloud.py,sha256=I9zb9WVDwV68G-UDrWipB9jYRLSj6dWst-grFdV0KiQ,217574
|
2
|
+
foxesscloud/openapi.py,sha256=ZK9GQmOSI44_Dm59-3A0-vtOzOLHmwqeBlzDT9RBl5A,202864
|
3
|
+
foxesscloud-2.6.5.dist-info/LICENCE,sha256=-3xv8CElCJV8Bc8PbAsg3iyxMpAK8MoJneM3rXigxqI,1074
|
4
|
+
foxesscloud-2.6.5.dist-info/METADATA,sha256=S2q4LFhzJOBaFhNjqpEaJboKya9SIKvp67fyTvarGz8,58769
|
5
|
+
foxesscloud-2.6.5.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
6
|
+
foxesscloud-2.6.5.dist-info/top_level.txt,sha256=IWOrKSNZCLU6IDXSX_b4_bqCfbZoWAT4CC0w0Lg7PuU,12
|
7
|
+
foxesscloud-2.6.5.dist-info/RECORD,,
|
@@ -1,7 +0,0 @@
|
|
1
|
-
foxesscloud/foxesscloud.py,sha256=kfb3Ard_s3LZ1hN53RfwcRUyEeMGd6TviHtoFmQyYDY,217547
|
2
|
-
foxesscloud/openapi.py,sha256=jFWMAlbhHAFqlKZlg8rALgU-jziUdrTjM0b4I0eTyAQ,207518
|
3
|
-
foxesscloud-2.6.4.dist-info/LICENCE,sha256=-3xv8CElCJV8Bc8PbAsg3iyxMpAK8MoJneM3rXigxqI,1074
|
4
|
-
foxesscloud-2.6.4.dist-info/METADATA,sha256=uNjXzpjeHGt65jOvDoLApHmb8-awuWtRg9BRfJKnzFU,57854
|
5
|
-
foxesscloud-2.6.4.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
6
|
-
foxesscloud-2.6.4.dist-info/top_level.txt,sha256=IWOrKSNZCLU6IDXSX_b4_bqCfbZoWAT4CC0w0Lg7PuU,12
|
7
|
-
foxesscloud-2.6.4.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|