5959_ENDPOINT_OA_REPORT = "/op/v0/device/report/query"
6060_ENDPOINT_OA_DEVICE_DETAIL = "/op/v0/device/detail?sn="
6161_ENDPOINT_OA_DEVICE_VARIABLES = "/op/v0/device/real/query"
62+ _ENDPOINT_OA_DEVICE_VARIABLES_V1 = "/op/v1/device/real/query"
6263_ENDPOINT_OA_DAILY_GENERATION = "/op/v0/device/generation?sn="
6364
6465METHOD_POST = "POST"
8687CONF_EXTPV = "extendPV"
8788CONF_XTZONE = "xtZone"
8889CONF_GET_VARIABLES = "Restrict"
90+ CONF_V1_API = "Use_V1_Api"
8991RETRY_NEXT_SLOT = - 1
9092
9193DEFAULT_NAME = "FoxESS"
105107 vol .Optional (CONF_EXTPV ): cv .boolean ,
106108 vol .Optional (CONF_XTZONE ): cv .boolean ,
107109 vol .Optional (CONF_GET_VARIABLES ): cv .boolean ,
110+ vol .Optional (CONF_V1_API ): cv .boolean ,
108111 }
109112)
110113
113116
114117async def async_setup_platform (hass , config , async_add_entities , discovery_info = None ):
115118 """Set up the FoxESS sensor."""
116- global LastHour , timeslice , last_api , RestrictGetVar , xtzone
119+ global LastHour , timeslice , last_api , RestrictGetVar , xtzone , V1_Api
117120 name = config .get (CONF_NAME )
118121 deviceID = config .get (CONF_DEVICEID )
119122 devicesn = config .get (CONF_DEVICESN )
120123 apiKey = config .get (CONF_APIKEY )
121124 ExtPV = config .get (CONF_EXTPV )
122125 xtzone = config .get (CONF_XTZONE )
123126 RestrictGetVar = config .get (CONF_GET_VARIABLES )
127+ V1_Api = config .get (CONF_V1_API )
124128 _LOGGER .debug ("API Key: %s" , apiKey )
125129 _LOGGER .debug ("Device SN: %s" , devicesn )
126130 _LOGGER .debug ("Device ID: %s" , deviceID )
127131 _LOGGER .debug ("FoxESS Scan Interval: %s minutes" , SCAN_MINUTES )
128132 _LOGGER .debug ("Cross Time Zone: %s" , xtzone )
129133 _LOGGER .debug ("Extended PV: %s" , ExtPV )
134+ _LOGGER .debug ("V1 Api Calls: %s" , V1_Api )
135+ if V1_Api is not True :
136+ V1_Api = False
130137 if ExtPV is not True :
131138 ExtPV = False
132139 else :
@@ -765,7 +772,7 @@ async def getOADeviceDetail(hass, allData, devicesn, apiKey):
765772 return True
766773 else :
767774 response = json .loads (restOADeviceDetail .data )
768- if response ["errno" ] == 0 and response ["msg" ] == "success" :
775+ if response ["errno" ] == 0 and ( response ["msg" ]== 'success' or response [ "msg" ] == 'Operation successful' ) :
769776 ResponseTime = round (time .time () * 1000 ) - timestamp
770777 if ResponseTime > 0 :
771778 allData ["raw" ]["ResponseTime" ] = ResponseTime
@@ -824,7 +831,7 @@ async def getOABatterySettings(hass, allData, devicesn, apiKey):
824831 return True
825832 else :
826833 response = json .loads (restOABatterySettings .data )
827- if response ["errno" ] == 0 and response ["msg" ] == "success" :
834+ if response ["errno" ] == 0 and ( response ["msg" ]== 'success' or response [ "msg" ] == 'Operation successful' ) :
828835 _LOGGER .debug (
829836 "OA Battery Settings Good Response: %s" , response ["result" ]
830837 )
@@ -895,7 +902,7 @@ async def getReport(hass, allData, apiKey, devicesn):
895902 else :
896903 # Openapi responded so process data
897904 response = json .loads (restOAReport .data )
898- if response ["errno" ] == 0 and response ["msg" ] == "success" :
905+ if response ["errno" ] == 0 and ( response ["msg" ]== 'success' or response [ "msg" ] == 'Operation successful' ) :
899906 _LOGGER .debug (
900907 "OA Report Data fetched OK: %s %s " , response , restOAReport .data [:350 ]
901908 )
@@ -914,7 +921,7 @@ async def getReport(hass, allData, apiKey, devicesn):
914921 if dataItem != None :
915922 cumulative_total = dataItem
916923 else :
917- _LOGGER .warning ("Report month fetch, None received" )
924+ _LOGGER .debug ("Report month fetch, None received" )
918925 break
919926 index += 1
920927 # cumulative_total += dataItem
@@ -962,7 +969,7 @@ async def getReportDailyGeneration(hass, allData, apiKey, devicesn):
962969 return True
963970 else :
964971 response = json .loads (restOAgen .data )
965- if response ["errno" ] == 0 and response ["msg" ] == "success" :
972+ if response ["errno" ] == 0 and ( response ["msg" ]== 'success' or response [ "msg" ] == 'Operation successful' ) :
966973 _LOGGER .debug (
967974 "OA Daily Generation Report Data fetched OK Response: %s" ,
968975 restOAgen .data [:500 ],
@@ -1018,38 +1025,40 @@ async def getRaw(hass, allData, apiKey, devicesn):
10181025
10191026 # "deviceSN" used for OpenAPI and it only fetches the real time data
10201027
1028+ # build the devicesn string
1029+ if V1_Api :
1030+ dsn = '{"sns":["' + devicesn + '"] }'
1031+ else :
1032+ dsn = '{"sn":"' + devicesn + '" }'
1033+
10211034 if RestrictGetVar :
10221035 _LOGGER .debug ("Getting Device Variable in restricted mode" )
1036+ # build the devicesn string
1037+ if V1_Api :
1038+ dsn = '{"sns":["' + devicesn + '"] '
1039+ else :
1040+ dsn = '{"sn":"' + devicesn + '"'
1041+
10231042 rawData = (
1024- '{"sn":"'
1025- + devicesn
1026- + '","variables":["ambientTemperation", \
1027- "batChargePower","batCurrent","batCurrent_1","batCurrent_2","batDischargePower", \
1028- "batTemperature","batTemperature_1","batTemperature_2","batVolt", "batVolt_1", "batVolt_2",\
1029- "boostTemperation", "chargeTemperature", "dspTemperature", \
1030- "epsCurrentR","epsCurrentS","epsCurrentT","epsPower","epsPowerR","epsPowerS","epsPowerT","epsVoltR","epsVoltS","epsVoltT", \
1031- "feedinPower", "generationPower","gridConsumptionPower", \
1032- "input","invBatCurrent","invBatPower","invBatVolt","invTemperation", \
1033- "loadsPower","loadsPowerR","loadsPowerS","loadsPowerT", \
1034- "meterPower","meterPower2","meterPowerR","meterPowerS","meterPowerT","PowerFactor", \
1035- "pv1Current","pv1Power","pv1Volt","pv2Current","pv2Power","pv2Volt", \
1036- "pv3Current","pv3Power","pv3Volt","pv4Current","pv4Power","pv4Volt","pvPower", \
1037- "RCurrent","ReactivePower","RFreq","RPower","RVolt", \
1038- "SCurrent","SFreq","SoC","SPower","SVolt", \
1039- "TCurrent","TFreq","TPower","TVolt", "SoC_1","Soc_2", \
1040- "ResidualEnergy","energyThroughput","runningState","currentFaultCount"] }'
1043+ dsn + ',"variables":["ambientTemperation", "batChargePower", "batCurrent", "batCurrent_1", "batCurrent_2", "batDischargePower", "batTemperature", "batTemperature_1", "batTemperature_2", "batVolt", "batVolt_1", "batVolt_2", "boostTemperation", "chargeTemperature", "dspTemperature", "epsCurrentR", "epsCurrentS", "epsCurrentT", "epsPower", "epsPowerR", "epsPowerS", "epsPowerT", "epsVoltR", "epsVoltS", "epsVoltT", "feedinPower", "generationPower", "gridConsumptionPower", "input", "invBatCurrent", "invBatPower", "invBatVolt", "invTemperation", "loadsPower", "loadsPowerR", "loadsPowerS", "loadsPowerT", "meterPower", "meterPower2", "meterPowerR", "meterPowerS", "meterPowerT", "PowerFactor", "pv1Current", "pv1Power", "pv1Volt", "pv2Current", "pv2Power", "pv2Volt", "pv3Current", "pv3Power", "pv3Volt", "pv4Current", "pv4Power", "pv4Volt", "pvPower", "RCurrent", "ReactivePower", "RFreq", "RPower", "RVolt", "SCurrent", "SFreq", "SoC", "SPower", "SVolt", "TCurrent", "TFreq", "TPower", "TVolt", "SoC_1", "Soc_2", "ResidualEnergy", "energyThroughput", "runningState", "currentFaultCount"] }'
10411044 )
10421045 else :
1043- rawData = '{"sn":"' + devicesn + '" }'
1046+ rawData = dsn # '{"sn":"' + dsn + '" }'
10441047
10451048 _LOGGER .debug ("getRaw OA request: %s" , rawData )
10461049
10471050 timestamp = round (time .time () * 1000 )
10481051
1049- path = _ENDPOINT_OA_DEVICE_VARIABLES
1052+ if V1_Api :
1053+ path = _ENDPOINT_OA_DEVICE_VARIABLES_V1
1054+ _LOGGER .debug ("Using V1 API" )
1055+ else :
1056+ path = _ENDPOINT_OA_DEVICE_VARIABLES
1057+
10501058 headerData = GetAuth ().get_signature (token = apiKey , path = path )
10511059
1052- path = _ENDPOINT_OA_DOMAIN + _ENDPOINT_OA_DEVICE_VARIABLES
1060+ path = _ENDPOINT_OA_DOMAIN + path
1061+ _LOGGER .debug ("Path: %s" , path )
10531062
10541063 restOADeviceVariables = RestData (
10551064 hass ,
@@ -1073,7 +1082,7 @@ async def getRaw(hass, allData, apiKey, devicesn):
10731082 else :
10741083 # Openapi responded correctly
10751084 response = json .loads (restOADeviceVariables .data )
1076- if response ["errno" ] == 0 and response ["msg" ] == "success" :
1085+ if response ["errno" ] == 0 and ( response ["msg" ]== 'success' or response [ "msg" ] == 'Operation successful' ) :
10771086 ResponseTime = round (time .time () * 1000 ) - timestamp
10781087 if ResponseTime > 0 :
10791088 allData ["raw" ]["ResponseTime" ] = ResponseTime
@@ -1158,15 +1167,24 @@ async def getRaw(hass, allData, apiKey, devicesn):
11581167 else :
11591168 variableValue = 0
11601169 _LOGGER .debug ("Variable %s no value, set to zero" , variableName )
1161- # fix for second battery items
1170+ # fix for various battery and scale items
11621171 if variableName == "SoC_1" :
11631172 variableName = "SoC_1" # do nothing for the moment, future release might align this correctly to use SoC
11641173 elif variableName == "batTemperature_1" :
1165- variableName = "batTemperature" # use same entity as for single battery systems
1174+ variableName = "batTemperature" # use entity for single battery systems
11661175 elif variableName == "invBatPower_1" :
1167- variableName = (
1168- "invBatPower" # use same entity as for single battery systems
1169- )
1176+ variableName = "invBatPower" # use entity for single battery systems
1177+ elif variableName == "ResidualEnergy" :
1178+ if item .get ("unit" ) is not None :
1179+ scale = item ["unit" ]
1180+ if scale in ['1.0kWh' , 'kWh' , None ]:
1181+ variableValue = round ((variableValue * 100 ),2 )
1182+ _LOGGER .debug ("OA Variables ResidualEnergy Scale: *100 %s" , scale )
1183+ elif scale == "0.1kWh" :
1184+ variableValue = round ((variableValue * 10 ),2 )
1185+ _LOGGER .debug ("OA Variables ResidualEnergy Scale: *10 %s" , scale )
1186+ else :
1187+ _LOGGER .debug ("OA Variables ResidualEnergy Scale: %s" , scale )
11701188
11711189 allData ["raw" ][variableName ] = variableValue
11721190 _LOGGER .debug (
@@ -2073,7 +2091,8 @@ def native_value(self) -> float | None:
20732091 else :
20742092 re = self .coordinator .data ["raw" ]["ResidualEnergy" ]
20752093 if re > 0 :
2076- re = re / 100
2094+ if re > 50 : # if openAPI scale is invalid (bug)
2095+ re = re / 100
20772096 else :
20782097 re = 0
20792098 return re
0 commit comments