Force Charge / Discharge and Grid Interaction

Post Reply
jlass6688
Posts: 2
Joined: Tue Aug 13, 2024 5:43 pm

Hello! Im looking into control over my battery through the FoxESS API.

I can see we have the option to enable work modes, including a Force Discharge and Force Charge. The API docs themselves dont explain much about these modes so I had a few questions.

If I enable Force Charge for a time period, will the battery be charged from both Solar and Grid? Is there a way to switch between only charging from Solar and charging from both Solar and Grid?

Similarly, if I enable Force Discharge, will it discharge to cover both the local load and grid? Is there a way to only enable the discharging to cover the local load?

Thanks in advance for your help!
Dave Foster
Posts: 1558
Joined: Thu Oct 13, 2022 7:21 pm

I assume you are referring to the OpenAPI and the scheduler, the Fox apps also modify that when you want to set schedules (Force Charge/Discharge) so the functionality is the same here as you get in the apps.

If I take your last point first, if the inverter is in Self Use, the batteries are above the minSoC and the PV isn't enough to cover the house loads the battery will always be used anyway - you don't have to set any modes for that.

If you set Force Discharge, you have to set a discharge power level - so if you set it to 5000 (watts) the inverter will output 5000 watts AC which will be used to cover the house load and the rest will be exported to grid. If there is PV generation of 2000 watts, the battery will supply the remaining 3000 watts (plus a bit for conversion losses). so if the house load is 300 watts then the export to grid would be 4700 watts.

If you set Force Charge there is no charge power setting to consider, it will charge at the maximum rate the batteries will allow (based on temp and SoC) - if we assume they can charge at 4000 watts, and there is PV generation of 2000 watts the grid will import the rest to make up the difference.

Using the scheduler you can't set whether the power will be taken from solar or the grid, for both 'Force' modes if PV is available it will be always be used first.
jlass6688
Posts: 2
Joined: Tue Aug 13, 2024 5:43 pm

Thanks Dave! Very helpful. For the time being, we're restricted to using the API and not through the Fox ESS apps.

Would you happen to know what the use case for the minSocOnGrid field on the Set Time Segment endpoint is used for? The description isnt super clear to me
Screenshot 2024-08-14 at 13.50.05.png


EDIT: Found an answer (From Dave actually)
minSoC is the minimum allowable safe state of charge for the battery

minSoC on grid is the minimum you want the battery to fall to whilst running on grid power.

The reason for the difference is that some people use the EPS (backup) power option and to do this effectively you need to leave a surplus to run off just in case the grid power is to fail.
Dave Foster
Posts: 1558
Joined: Thu Oct 13, 2022 7:21 pm

Yep that’s correct :)

Be careful when setting it, it’s normally set at 10% (unless you have EPS fitted where you might want to keep a bit more reserve) - when setting it in a schedule it will stay set as you set it after the schedule ends, unless you set it back later. So ot’s best to leave it at the default.

For completeness ‘FDSoC’ is used when force discharging to set the minimum you want the batteries to discharge to so it doesn’t burn through all your battery energy and leaves some for the house.
Tweety
Posts: 2
Joined: Mon Jan 06, 2025 4:08 pm

I'm joining the thread. I'm also trying to force the operating states of the installation using the scheduler. I'm using the API and have implemented test code in Python based on the available documentation.

Code: Select all

# Set the time segment information

import json
import time
import hashlib
import requests
import urllib3

# Disabling HTTPS warnings
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

# Basic settings
api_key = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'  # api_key
device_sn = "xxxxxxxxxxxxxxxxx"  # device_sn
domain = 'https://www.foxesscloud.com'
path = '/op/v0/device/scheduler/enable'  # Endpoint for setting time segments.
url = f"{domain}{path}"

# A function generating the signature and headers.
def generate_signature(api_key, endpoint):
    timestamp = round(time.time() * 1000)  # Timestamp in milliseconds
    signature_data = f"{endpoint}\\r\\n{api_key}\\r\\n{timestamp}"
    signature = hashlib.md5(signature_data.encode('utf-8')).hexdigest()
    
    headers = {
        'Content-Type': 'application/json',
        'token': api_key,
        'timestamp': str(timestamp),
        'signature': signature,
        'lang': 'en'
    }
    return headers

# Function to set time segments
def set_time_segment(api_key, device_sn, groups):
    headers = generate_signature(api_key, path)

    # Creating the request body
    payload = {
        "deviceSN": device_sn,
        "groups": groups
    }

    print (payload);

    response = requests.post(url, headers=headers, json=payload, verify=False)
    
    if response.status_code == 200:
        try:
            data = response.json()
            print("Response Data:", json.dumps(data, indent=4))
            
            if data.get("errno") == 0:
                print("Time segment information set successfully.")
            else:
                print("Error in the response:", data.get("errno"), data.get("msg"))
        except json.JSONDecodeError:
            print("Unable to read the response as JSON:", response.text)
    else:
        print("Błąd:", response.status_code, response.text)

# Sample time segment data
groups = [
    {
        "enable": 1,
        "startHour": 0,
        "startMinute": 0,
        "endHour": 5,
        "endMinute": 0,
        "workMode": "SelfUse",
        "minSocOnGrid": 5,
        "fdSoc": 90,
        "fdPwr": 3000
    },
    {
        "enable": 1,
        "startHour": 6,
        "startMinute": 0,
        "endHour": 17,
        "endMinute": 0,
        "workMode": "Feedin",
        "minSocOnGrid": 5,
        "fdSoc": 90,
        "fdPwr": 3000
    }
]

# Function call
set_time_segment(api_key, device_sn, groups)
Unfortunately, I am receiving information as shown below.

Code: Select all

Response Data: {
    "errno": 40257,
    "msg": "Parameter does not meet expectations",
    "result": null
}
I don't quite understand which parameters are being referred to. The payload looks correct.

What could be causing this issue?

My inverter is H3-8.0-E.
Dave Foster
Posts: 1558
Joined: Thu Oct 13, 2022 7:21 pm

Firstly just to check that your firmware does support schedules, i.e. you should be able to set them in the Fox V2 app (or website).

The 40257 is saying that one of the parameters is invalid, the parameters are all of the items in the payload so something in either 'DeviceSN' or 'Groups' is not what it is expecting or outside of limits.

You cannot set a minSoC of 5% that is below the boundary limits (10-100) - that alone could trigger the problem.

If you haven't already it would be easier to test your signature code by using a simple read only function like Get Device time ( /op/v0/device/time/get) as that only has 1 parameter - i'd be more inclined to use this function for setting your signature in headers

Code: Select all

fr'{endpoint}\r\n{api_key}\r\n{timestamp}'
but yours should work ok.
Tweety
Posts: 2
Joined: Mon Jan 06, 2025 4:08 pm

That's correct. In the FOX V2 app, I can set schedules, and the inverter operates according to the declared schedules. The inverter's firmware has been updated to the latest version available on our local distributor's website.

I know that error 40257 is related to parameter issues. There are five parameters that, in my opinion, are correctly declared. The parameters that need to be provided are the same as in other queries that work. Just in case, I’m attaching the details of my parameters:

Code: Select all

{'Content-Type': 'application/json', 'token': '46e0daf2-14b2-4378-861d-xxxxxxxxxxxx', 'timestamp': '1738662838028', 'signature': '0c2e373a42b7f57cacb3ddb67d7cee47', 'lang': 'en'}
I have set various values for minSoC, including those within the 10-100 range. However, it does not change the final result I am getting.

Additionally, I would like to mention that when I set the schedules manually from the app and then use the method "Get the time segment information," I receive the following response:

Code: Select all

Response Data: {
    "errno": 0,
    "msg": "success",
    "result": null
}
The above confirms that the parameters are correct. However, the problem may be broader.
Post Reply