Sonnenbatterie with APIv2 / Webhook

No, my battery is a 5 year old eco 9.43. 10kWh 9 usable and a Fronius Primo 6.0-1 inverter.

Operating mode is managed via REST API.

EM_OperatingMode=1 puts the battery into manual mode that allows POST commands to charge and discharge.
EM_OperatingMode=2 puts the battery into automatic (self consumption) mode

I was wondering if the battery was changing mode by itself when you see the SOC drift but I understand it’s not the case.

No don’t think so. Automatic mode would either change the output of the battery to match the house consumption or if PV output is available would charge the battery ant available PV output.

Hi, Any chance I can see the code behind the buttons? I don’t use node_red.
I take it you don’t have a configuration yaml file when you use node_red?
I’m garbage at writing code from scratch but I can usually follow basic code to fudge my way through getting my things working… :laughing:

This is what i think worked before I used node red. The below code is an switch.yaml file. I stopped using them as I wanted a dynamic message so I don’t know if they still work but its a start for you @whistlebare.
You need your Token from the API and IP address and unique_id if you want.

- platform: command_line
  switches:
    sonnen_automatic_mode:
      unique_id: xxxxxxx
      friendly_name: 'Set Sonnen to Auto'
      command_on: "curl -X PUT -d EM_OperatingMode=2 --header 'Auth-Token: xxxxxxxx' http://192.168.xxx.xxx:80/api/v2/configurations"
      value_template: >
          {{value_json.config.on}}
      icon_template: >
        {% if value_json.config.on == true %} mdi:toggle-switch
        {% else %} mdi:toggle-switch-off
        {% endif %}
    sonnen_tou_mode:
      unique_id: xxxxxxxx
      friendly_name: 'Set Sonnen to TOU'
      command_on: "curl -X PUT -d EM_OperatingMode=10 --header 'Auth-Token: xxxxxxxx' http://192.168.xxx.xxx:80/api/v2/configurations"
      value_template: >
          {{value_json.config.on}}
      icon_template: >
        {% if value_json.config.on == true %} mdi:toggle-switch
        {% else %} mdi:toggle-switch-off
        {% endif %}
    sonnen_manual_mode:
      unique_id: xxxxxxxxx
      friendly_name: 'Set Sonnen to Manual'
      command_on: "curl -X PUT -d EM_OperatingMode=1 --header 'Auth-Token: xxxxxxxxx' http://192.168.xxx.xxx:80/api/v2/configurations"
      value_template: >
          {{value_json.config.on}}
      icon_template: >
        {% if value_json.config.on == true %} mdi:toggle-switch
        {% else %} mdi:toggle-switch-off
        {% endif %}
1 Like

Sure, however Node-Red takes a lot of the coding out. If you are less interested in coding then you’re probably a candidate for Node-Red.

I use switch nodes for these buttons. You Need Custom Integration installed in Home Assistant for this node to function. This integration is kind of a feed back into HA from node-red. In this case it allows me to place a switch on my dashboard that has an affect in Node-Red.

These are the switch nodes:


The way I use them is when you activate the dashboard switchs the action triggers a “flow” in NR.

For example the automatic mode switch feeds into 6 different lines of nodes.

\

The first is a REST API PUT command that sets the backup buffer of the battery to 0%. The reason for this is because I use 100% backup buffer to put the battery into standby mode and switching back to 0% just makes sure that standby is off.

This PUT command is carried out using first a function node:

This is the guts of the 0% Backup Buffer function node. Its very simple. All its doing is create a message array with the variables you can see (i’ve blanked out the Auth-Token as that’s private).

This message array ‘msg’ is then passed to the next node which is a http request node. It has no setting except the PUT method as all the details have been passed using the msg array from the previous function node.

After that I pass the returned responce from the subject server (the battery) to some error hadleing steps.


This simply tests that the return responce was an error and if so write it and some other info like timestamp and the contents of the origin PUT command etc.

So here is the entire flow for EMHASS


The details of all this is in that document I mentioned above and in fact the entire flow is there as well so you can import it into your node-red instant and with a little bit of adjustment use the same setup. Thats the EMHASS.JSON file. It’s all the code in this RN flow.

I can’t explane it all here as that’s what the document does and its 37 pages long.

There is no code other than these nodes and what’s explained in the doco. Node-red does away with most coding and automations. I don’t use automations any more as I’ve translated them all into node-red.

2 Likes

Thanks for that…I might have a play with the code and see how I get on. I have automations working for various triggers to do with my Intelligent Octopus set up, it would be nice to have some visual indications and buttons to go along with it

Damn looks like i’ve got to install Node Red on my docker and learn yet another program :laughing: Thanks for the in depth explanation… :+1:

I’ll bet you’ll eventually migrate all your automations to node-red if you do install it.

Hi!
This might me a little bit off topic, but maybe a good place to ask:
I read the values from api/v2/status and discovered some time ago, that “SystemStatus” changes sometimes to “CriticalError” for 5 or 15 minutes. Sometimes means between once per week and 24 times a day (which means about 2h no operation of the battery!).
I’m curious, if I’m the only one with this behaviour or if this happens also to others. Usually this value is not watched regularly, but maybe some of you could take a look on your recorded values and give a response, if this happens also to you.

Hello
Thank you for the great work here.
My English is not that perfect, so everything with a translator! Sorry!
I’m not that familiar with Node-red yet, I’m still at the beginning.
I also have a solar battery and would like to minimize its high consumption.
When there is no more production and the battery supplies the house, I still have a power supply of 50 - 100 watts.
I can already send charging commands or change the work mode.
Maybe not correct, I’m definitely using the wrong nodes.
What I can’t do is send a variable value (consumption is constantly changing).
As you can see in the pictures, I send 50 watts to unload with the URL.
But I would like to send a value that is consumption + grid consumption. It also has a sensor for that.
For example, the house consumes 400 watts and the power supply is 50 watts, so I would like to send 450 watts to discharge.
Maybe someone can give me some food for thought.
This is what it looks like at the moment.
Can’t attach images!
URL - http://192.168.1.70/api/v2/setpoint/discharge/50

Thank you in advance
greeting

sonnen battery is manageable via RESTful API. So you can use the Home Assistant RESTful Command to send commands to the battery to charge or discharge at whatever rate you want.

Alternatively you can do the same thing with Node-Red by using the http request node.

In either case to change the battery charge/discharge rate requires a PUT command after the battery has been put in manual mode (which can also be done with a RESTful command).

Example:


These two flows put the battery into manual mode and then pass a number in Watts to charge the battery.
Here’s the JSON code for these two flows:

[{"id":"f8c8624c7c9dca94","type":"http request","z":"65840aa926d9c567","name":"POST","method":"POST","ret":"obj","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"bearer","senderr":false,"headers":[],"x":1250,"y":340,"wires":[["0e3f0373d7e81e09","7e7a97b8e68d3389"]]},{"id":"858d53f8ca9abe53","type":"function","z":"65840aa926d9c567","name":"Charge Setup","func":"var pospayload = 0.0\npospayload = Math.abs(msg.payload)\n// Increase pospayload by 100 if it is between 200 and 2500\nif (pospayload >= 200 && pospayload <= 2500) \n{\n    pospayload += 100;\n}\nmsg.headers = {}\nmsg.headers['Auth-Token'] = 'YOUR-SONNEN-AUTH-TOKEN'\nmsg.headers [\"Content-Type\"] = \"application/x-www-form-urlencoded\"\nmsg.url = \"http://192.168.1.70:80/api/v2/setpoint/charge/\" + pospayload.toString()\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":1080,"y":340,"wires":[["f8c8624c7c9dca94"]]},{"id":"0e3f0373d7e81e09","type":"debug","z":"65840aa926d9c567","name":"debug Charge","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1440,"y":340,"wires":[]},{"id":"402679171d35e0d2","type":"http request","z":"65840aa926d9c567","name":"PUT","method":"PUT","ret":"obj","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"bearer","senderr":false,"headers":[],"x":1250,"y":280,"wires":[["5bd08bcd972153bc","81532e458a6da3d1"]]},{"id":"9efde64dbc00c864","type":"function","z":"65840aa926d9c567","name":"Manual Mode","func":"msg.payload = \"EM_OperatingMode=1\"\nmsg.headers = {}\nmsg.headers['Auth-Token'] = 'YOUR-SONNEN-AUTH-TOKEN'\nmsg.headers [\"Content-Type\"] = \"application/x-www-form-urlencoded\"\nmsg.url = \"http://192.168.1.70:80/api/v2/configurations\" \nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1080,"y":280,"wires":[["402679171d35e0d2"]]},{"id":"5bd08bcd972153bc","type":"debug","z":"65840aa926d9c567","name":"debug Manual Mode","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1460,"y":280,"wires":[]},{"id":"4b6455180cf0d210","type":"delay","z":"65840aa926d9c567","name":"4 sec","pauseType":"delay","timeout":"4","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":930,"y":340,"wires":[["858d53f8ca9abe53"]]},{"id":"93beed478696134e","type":"link in","z":"65840aa926d9c567","name":"link in Charge Setup","links":["8f6da85cea51a6c2","63ba9e6850f7361b","95976ebe7f6655d3","cc0a6d9606abe439","cba7fee3cee79393","b42b8c412b6200e7"],"x":835,"y":340,"wires":[["4b6455180cf0d210"]]},{"id":"2608790487e879c8","type":"link in","z":"65840aa926d9c567","name":"link in Manual Mode Setup","links":["8f6da85cea51a6c2","c4a70f29ecac9603","767baa7b0d82b4ca","d9ba6153cfb88bbb","63ba9e6850f7361b","95976ebe7f6655d3","cc0a6d9606abe439","87b07b541bba63f6","cba7fee3cee79393","b42b8c412b6200e7"],"x":835,"y":280,"wires":[["e8a2ee908ef64a78"]]},{"id":"e8a2ee908ef64a78","type":"delay","z":"65840aa926d9c567","name":"2 sec","pauseType":"delay","timeout":"2","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":930,"y":280,"wires":[["9efde64dbc00c864"]]}]

You should be able to import this JSON into you Node-Red instance and work with it. Remember to replace the AUTH-TOKEN with your battery AUTH-TOKEN which can be found here on your battery (assume it’s IP address is 192.168.1.70.

Now to decide what rate to charge or discharge I use EMHASS. My config for EMHASS is located here.

1 Like

Hi and thanks for all the effort here!

I’m trying to setup the rest integration for my battery. I want to do as much use as possible of the restful integration, instead of using the sensor integration, because this allows to reduce the number of requests.

In the case of the powermeter endpoint, I want to get both kwh_imported sensors from this query:

[
  {
    "a_l1": 0,
    "a_l2": 0,
    "a_l3": 0,
    "a_total": 0,
    "channel": 1,
    "deviceid": 0,
    "direction": "production",
    "error": 0,
    "frequency": 0,
    "kwh_exported": 0,
    "kwh_imported": 17.5,
    "v_l1_l2": 0,
    "v_l1_n": 237.8000030517578,
    "v_l2_l3": 0,
    "v_l2_n": 0,
    "v_l3_l1": 0,
    "v_l3_n": 0,
    "va_total": 0,
    "var_total": 0,
    "w_l1": 0,
    "w_l2": 0,
    "w_l3": 0,
    "w_total": 0
  },
  {
    "a_l1": 1.6030000448226929,
    "a_l2": 0,
    "a_l3": 0,
    "a_total": 0,
    "channel": 2,
    "deviceid": 1,
    "direction": "consumption",
    "error": 0,
    "frequency": 0,
    "kwh_exported": 0,
    "kwh_imported": 12.300000190734863,
    "v_l1_l2": 0,
    "v_l1_n": 237.8000030517578,
    "v_l2_l3": 0,
    "v_l2_n": 0,
    "v_l3_l1": 0,
    "v_l3_n": 0,
    "va_total": 379.7999877929687,
    "var_total": -379.7999877929687,
    "w_l1": -17.100000381469727,
    "w_l2": 0,
    "w_l3": 0,
    "w_total": 7.5
  }
]

However, this configuration fails.

rest:
  - resource: http://BATTERY_IP:80/api/v2/powermeter
    headers:
      Auth-Token: !secret sonnen_api_token
    scan_interval: 15
    sensor:
      - name: Sonnen Solar production Wh
        unique_id: sonnen_powermeter_solar_production
        json_attributes_path: "$.[0]"
        json_attributes:
          - "kwh_imported"
        unit_of_measurement: Wh
        device_class: energy
        state_class: measurement

In the logs I get these errors:

 Template variable warning: 'list object' has no attribute 'kwh_imported' when rendering '{{ value_json.kwh_imported }}' 

and

Failed to set state for sensor.sonnen_solar_production_kwh, fall back to unknown

Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 1209, in _async_write_ha_state
    hass.states.async_set_internal(
  File "/usr/src/homeassistant/homeassistant/core.py", line 2332, in async_set_internal
    state = State(
            ^^^^^^
  File "/usr/src/homeassistant/homeassistant/core.py", line 1776, in __init__
    validate_state(state)
  File "/usr/src/homeassistant/homeassistant/core.py", line 240, in validate_state
    raise InvalidStateError(
homeassistant.exceptions.InvalidStateError: Invalid state with length 856. State max length is 255 characters.

Thanks for any help!

Try this, let me know how it goes:

rest:
  - resource: http://BATTERY_IP:80/api/v2/powermeter
    headers:
      Auth-Token: !secret sonnen_api_token
    scan_interval: 15
    sensor:
      - name: "Sonnen Solar Production Wh"
        unique_id: "sonnen_powermeter_solar_production"
        value_template: "{{ value_json[0].kwh_imported }}"
        unit_of_measurement: "Wh"
        device_class: energy
        state_class: measurement

      - name: "Sonnen Consumption Wh"
        unique_id: "sonnen_powermeter_consumption"
        value_template: "{{ value_json[1].kwh_imported }}"
        unit_of_measurement: "Wh"
        device_class: energy
        state_class: measurement

Wooow! It works!

I don’t understand why it doesn’t work with the path (which seems like the intended way of doing it), but I can live with that!

Thank you very much

PS: I corrected the paths removing the points, which made the list conversion errors disapear, but still no numeric result.

The “path” leads to complex JSON data when all you want is one element. So you have to use jinja template to extract the data of interets.

Hi!

now I’m trying to set the Em_OperatingMode to manual. I can do it with this curl command:

curl -X PUT -d EM_OperatingMode=1 --header 'Auth-Token: TOKEN' http://sonnen:80/api/v2/configurations

And I get the appropriate response:

{"EM_OperatingMode":"1"}

I would like to have it as a switch though:

switch:
  - name: Sonnen battery manual mode
    platform: rest
    resource: http://sonnen:80/api/v2/configurations
    method: put
    headers:
      Auth-Token: !secret sonnen_api_token
    body_on: "EM_OperatingMode=1"
    body_off: "EM_OperatingMode=2"
    is_on_template: '{{ value_json["EM_OperatingMode"] == "1" }}'

But it doesn’t work. In the logs I only get:

ERROR (MainThread) [homeassistant.components.rest.switch] Can't turn on http://sonnen:80/api/v2/configurations. Is resource/endpoint offline?

Any clues?

I think the problem lies in how you’re specifying the body_on and body_off parameters.

In your curl command, you’re using -d EM_OperatingMode=1, which sends a JSON payload to the API. However, in your Home Assistant configuration, you’ve specified body_on: "EM_OperatingMode=1" and body_off: "EM_OperatingMode=2", without any indication that these should be treated as JSON payloads.

When you send a request with these values, it’s likely being interpreted as a query string (e.g. http://sonnen:80/api/v2/configurations?EM_OperatingMode=1) instead of a JSON payload. This is why the API is not receiving the expected data and returning an error.

To fix this, you should use the following configuration:

switch:
  - name: Sonnen battery manual mode
    platform: rest
    resource: http://sonnen:80/api/v2/configurations
    method: put
    headers:
      Auth-Token: !secret sonnen_api_token
    body_on: '{"EM_OperatingMode": "1"}'
    body_off: '{"EM_OperatingMode": "2"}'
    is_on_template: '{{ value_json["EM_OperatingMode"] == "1" }}'

By surrounding the values with double quotes and curly braces, you’re telling Home Assistant to treat them as JSON payloads.

Hi!

thanks for the answer, but unfortunately it didn’t work. I get the same log output and nothing happens in the battery. I also got this working in HTTPie (GUI) and I had to specify that the payload is Form data:

PUT /api/v2/configurations HTTP/1.1
Auth-Token: TOKEN
Content-Length: 18
Content-Type: application/x-www-form-urlencoded
Host: sonnen
User-Agent: HTTPie

EM_OperatingMode=2

So I guess we need to tell HA somehow to send the payload as form data, which, turns out you can do with a header:

switch:
  - name: Sonnen battery manual mode
    platform: rest
    resource: http://sonnen:80/api/v2/configurations
    method: put
    headers:
      Auth-Token: !secret sonnen_api_token
      Content-Type: application/x-www-form-urlencoded
    body_on: "EM_OperatingMode=1"
    body_off: "EM_OperatingMode=2"
    is_on_template: '{{ value_json["EM_OperatingMode"] == "1" }}'

This worked for me.

Thanks for the hint!!

1 Like