Growatt integration into HA, to operate the battery to turn on and off

Hi,
I’ve integrated a growatt battery with HA. I was trying to automate the process of the battery charging, but I’m not finding that action under the automation. So, is it that Growatt does not let the user operate the battery via HA? or should I do it via some external API(custom written by me)?
If the API is an option, could you please guide me through the process or give me a pointer to the docs I can follow?

Hi, I’ve just had a Growatt PV system installed and have an exactly same question. Did you find the answer please?

1 Like

Hi, I’d be interested in this too, I’m looking to create automation to charge the battery to charge off peak but only if tomorrow is cloudy.

No news on this? I am also trying to get this automated via time-cost tarif etc

Hi, have a look at this thread: Growatt Inverter Mode Switch - Feature Requests - Home Assistant Community (home-assistant.io)
Unfortunately it looks bit too complex for me but it might help someone else.

Eventually I was able to crack this, read my post here: Growatt Inverter Mode Switch - #24 by martinm

@martinm I have read the same article but I’m struggling. If you could provide any help that would be great.

I have App daemon installed directly from home assist addin and added the files to the correct locations and updated Apps.yaml.

I have added this to scripts

set_charge_rate:
alias: Growatt Battery - Set Charge Rate
sequence:
- event: SET_CHARGE_RATE
event_data:
stop_battery_soc: “{{ stop_battery_soc }}”

But do I need to add more to call things?

Thanks

James

Hi @jamesa22,
If you configured everything correctly, can you call the script from your HA? See my screenshot:

If it works, you can automate the logic into a nightly script, as per my last code snippet.

Martin

Hi Martin,

Thanks for the quick response, I tried this:

However I check the Growatt website and the SOC are still set to 100 so I’m guessing this didn’t work.

James

Is your appdaemon logging anything?

Hi Martin,

Here is my log, Nothing gets logged:

2022-12-23 15:49:06.259447 INFO AppDaemon: AppDaemon Version 4.2.1 starting
2022-12-23 15:49:06.259798 INFO AppDaemon: Python version is 3.10.9
2022-12-23 15:49:06.260095 INFO AppDaemon: Configuration read from: /config/appdaemon/appdaemon.yaml
2022-12-23 15:49:06.260445 INFO AppDaemon: Added log: AppDaemon
2022-12-23 15:49:06.260809 INFO AppDaemon: Added log: Error
2022-12-23 15:49:06.261151 INFO AppDaemon: Added log: Access
2022-12-23 15:49:06.261446 INFO AppDaemon: Added log: Diag
2022-12-23 15:49:06.480559 INFO AppDaemon: Loading Plugin HASS using class HassPlugin from module hassplugin
2022-12-23 15:49:06.775834 INFO HASS: HASS Plugin Initializing
2022-12-23 15:49:06.776207 INFO HASS: HASS Plugin initialization complete
2022-12-23 15:49:06.777261 INFO AppDaemon: Initializing HTTP
2022-12-23 15:49:06.778007 INFO AppDaemon: Using ‘ws’ for event stream
2022-12-23 15:49:06.784880 INFO AppDaemon: Starting API
2022-12-23 15:49:06.789813 INFO AppDaemon: Starting Admin Interface
2022-12-23 15:49:06.790575 INFO AppDaemon: Starting Dashboards
2022-12-23 15:49:06.820139 INFO HASS: Connected to Home Assistant 2022.12.8
2022-12-23 15:49:06.861586 INFO AppDaemon: App ‘set_discharge_rate’ added
2022-12-23 15:49:06.864892 INFO AppDaemon: App ‘set_charge_rate’ added
2022-12-23 15:49:06.867832 INFO AppDaemon: App ‘get_discharge_rate’ added
2022-12-23 15:49:06.870097 INFO AppDaemon: Found 3 total apps
2022-12-23 15:49:06.871391 INFO AppDaemon: Starting Apps with 3 workers and 3 pins
2022-12-23 15:49:06.875670 INFO AppDaemon: Running on port 5050
2022-12-23 15:49:06.925555 INFO HASS: Evaluating startup conditions
2022-12-23 15:49:06.944121 INFO HASS: Startup condition met: hass state=RUNNING
2022-12-23 15:49:06.944902 INFO HASS: All startup conditions met
2022-12-23 15:49:06.987970 INFO AppDaemon: Got initial state from namespace default
2022-12-23 15:49:08.892595 INFO AppDaemon: Scheduler running in realtime
2022-12-23 15:49:08.899156 INFO AppDaemon: Adding /config/appdaemon/apps to module import path
2022-12-23 15:49:08.907550 INFO AppDaemon: Loading App Module: /config/appdaemon/apps/set_charge_rate.py
2022-12-23 15:49:08.971881 WARNING AppDaemon: No app description found for: /config/appdaemon/apps/growattServer.py - ignoring
2022-12-23 15:49:08.973731 WARNING AppDaemon: No app description found for: /config/appdaemon/apps/hello.py - ignoring
2022-12-23 15:49:08.975472 INFO AppDaemon: Loading App Module: /config/appdaemon/apps/get_inverter_settings.py
2022-12-23 15:49:08.979665 INFO AppDaemon: Loading App Module: /config/appdaemon/apps/set_discharge_rate.py
2022-12-23 15:49:08.983763 INFO AppDaemon: Initializing app set_discharge_rate using class HouseBatteryDischarge from module set_discharge_rate
2022-12-23 15:49:08.988712 INFO AppDaemon: Initializing app set_charge_rate using class HouseBatteryCharge from module set_charge_rate
2022-12-23 15:49:08.993418 INFO AppDaemon: Initializing app get_discharge_rate using class InverterSettings from module get_inverter_settings
2022-12-23 15:49:09.009696 INFO AppDaemon: App initialization complete

Can you confirm where you set the login details for the growatt website?

Thanks

Hi James,

I think you’ll have to add some logging into set_charge_rate.py to get a better understanding of what’s going on. Below is my complete script, look for self.log:

import growattServer
import appdaemon.plugins.hass.hassapi as hass

SET_CHARGE_RATE = 'SET_CHARGE_RATE'


class HouseBatteryCharge(hass.Hass):

  def initialize(self):
    self.listen_event(self.set_charge_rate, SET_CHARGE_RATE)


  def set_charge_rate(self, event, data, kwargs):

    stop_battery_soc = str(data['stop_battery_soc'])
    self.log("stop_battery_soc is: " + stop_battery_soc + "%")
    
    self.username = <username>
    self.user_pass = <password>
    self.api = growattServer.GrowattApi()
    self.login_response = self.api.login(self.username, self.user_pass)
    self.plant_list = self.api.plant_list(self.login_response['user']['id'])

    #Simple logic to just get the first inverter from the first plant
    #Expand this using a for-loop to perform for more systems (see mix_example for more detail)
    self.plant = self.plant_list['data'][0] #This is an array - we just take the first - would need a for-loop for more systems
    self.plant_id = self.plant['plantId']
    self.log(self.plant_id)
    self.plant_name = self.plant['plantName']
    self.log(self.plant_name)
    self.plant_info = self.api.plant_info(self.plant_id)

    self.device = self.plant_info['deviceList'][0] #This is an array - we just take the first - would need a for-loop for more systems
    self.device_sn = self.device['deviceSn']

    self.schedule_settings = ["100", #Charging power %
                              stop_battery_soc,  #Stop charging SoC %
                              "1",   #Allow AC charging (1 = Enabled)
                              "00", "30", #Schedule 1 - Start time
                              "04", "30", #Schedule 1 - End time
                              "1",        #Schedule 1 - Enabled/Disabled (1 = Enabled)
                              "00", "00", #Schedule 2 - Start time
                              "00", "00", #Schedule 2 - End time
                              "0",        #Schedule 2 - Enabled/Disabled (0 = Disabled)
                              "00", "00", #Schedule 3 - Start time
                              "00", "00", #Schedule 3 - End time
                              "0"]        #Schedule 3 - Enabled/Disabled (0 = Disabled)

    self.log(self.schedule_settings)
    self.response = self.api.update_mix_inverter_setting(self.device_sn, 'mix_ac_charge_time_period', self.schedule_settings)
    self.log(self.response)

Also, what do you have in your HA logfile? Mine says:

2022-12-23 20:00:00.270 INFO (MainThread) [homeassistant.components.script.set_charge_rate] Growatt Battery - Set Charge Rate: Running script sequence
2022-12-23 20:00:00.271 INFO (MainThread) [homeassistant.components.script.set_charge_rate] Growatt Battery - Set Charge Rate: Executing step SET_CHARGE_RATE

Basically, if you see in HA log that the HA script is executing, and then expected entries from the Pyhton script in AppDaemon log, then the HA-AppDaemon integration is working.
Another point of failure could be accessing the Growatt server itself. To make sure that this bit works, I first stepped through the standalone code in my Python IDE. I suggest you start with that. I used a modified settings_example.py from indykoning/PyPi_GrowattServer (github.com) for that.

Martin

HI Martin,

Thanks for this, The log is as follows:

2022-12-26 15:07:26.948035 INFO set_charge_rate: stop_battery_soc is: 90%
2022-12-26 15:07:27.153666 WARNING set_charge_rate: ------------------------------------------------------------
2022-12-26 15:07:27.155105 WARNING set_charge_rate: Unexpected error in worker for App set_charge_rate:
2022-12-26 15:07:27.156589 WARNING set_charge_rate: Worker Ags: {‘id’: ‘4c74d68928644a8da7686a550dde9274’, ‘name’: ‘set_charge_rate’, ‘objectid’: ‘4b49e90e666b4a3a94ecf2d3660fe2eb’, ‘type’: ‘event’, ‘event’: ‘SET_CHARGE_RATE’, ‘function’: <bound method HouseBatteryCharge.set_charge_rate of <set_charge_rate.HouseBatteryCharge object at 0x7f93b45ea0>>, ‘data’: {‘stop_battery_soc’: 90, ‘metadata’: {‘origin’: ‘LOCAL’, ‘time_fired’: ‘2022-12-26T15:07:26.814199+00:00’, ‘context’: {‘id’: ‘01GN7FJTP9EWRX6GTVACZE93J9’, ‘parent_id’: None, ‘user_id’: ‘f2d846aee1e04f8b8d01b40a7e3b6f62’}}}, ‘pin_app’: True, ‘pin_thread’: 1, ‘kwargs’: {’__thread_id’: ‘thread-1’}}
2022-12-26 15:07:27.157993 WARNING set_charge_rate: ------------------------------------------------------------
2022-12-26 15:07:27.164810 WARNING set_charge_rate: Traceback (most recent call last):
File “/usr/lib/python3.10/site-packages/appdaemon/threading.py”, line 950, in worker
funcref(args[“event”], data, self.AD.events.sanitize_event_kwargs(app, args[“kwargs”]))
File “/config/appdaemon/apps/set_charge_rate.py”, line 21, in set_charge_rate
self.login_response = self.api.login(self.username, self.user_pass)
File “/config/appdaemon/apps/growattServer.py”, line 119, in login
data = json.loads(response.content.decode(‘utf-8’))[‘back’]
File “/usr/lib/python3.10/json/init.py”, line 346, in loads
return _default_decoder.decode(s)
File “/usr/lib/python3.10/json/decoder.py”, line 337, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
File “/usr/lib/python3.10/json/decoder.py”, line 355, in raw_decode
raise JSONDecodeError(“Expecting value”, s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
2022-12-26 15:07:27.166177 WARNING set_charge_rate: ------------------------------------------------------------

I’m not seeing anything in the HA log for this script however, So I suspect that is where the issue lies. How does the integration work? - Happy Christmas by the way :slight_smile:
James

Hi James,
Merry Christmas :slightly_smiling_face: I suspect this is your problem:

File “/config/appdaemon/apps/set_charge_rate.py”, line 21, in set_charge_rate
self.login_response = self.api.login(self.username, self.user_pass)

I suspect your login to the Growatt server fails (assuming that’s the line 21 in your set_charge_rate.py script). That itself is not a great surprise, I found the reliability of those servers bit dodgy (that’s why I run the script twice with some delay between the runs). I would advise you to step through the code in a Python IDE to see if it works on its own before you drop it to AppDaemon. Also, what server name do you have in growattServer.py (line 26)? I currently use:

server_url = 'https://server.growatt.com/'

But in the past I was experimenting also with https://server-api.growatt.com/, because at some point recently Growatt seemed to be switching to a new URL (separate discussion under Growatt HA integration).

My entries in HA log are INFO, so I assume you’ll need to adjust your logging level to see anything in your HA log. But since the AppDaemon is triggering your set_charge_rate.py script, that part seems to be working OK.

My understanding of the AppDaemon - HA integration is:

  • AppDaemon runs and listens to HA events through HA URL
  • HA script triggers the SET_CHARGE_RATE event
  • AppDaemon recognises the SET_CHARGE_RATE event and runs the set_charge_rate.py script

It makes you wonder whether it has to be this complicated, but apparently there are reasons to run set_charge_rate.py within AppDaemon and not have it executed within HA directly (which I tried and it didn’t work, my understanding is that HA hasn’t got the required Python capabilities, multithreading etc).

Martin

HI Martin,

Thanks for coming back to me.

I have tried both server.growatt and server-api.growatt.

I do agree with you however and think it looks like a log in issue more than anything else.

Where do you enter the login credential’s for this? I have added these in set_charge_rate.py and get_inverter_settings.py. I will add at the moment the username and password are in plain text also.

I would also like to confirm how you are executing the set charge rate python? I have some buttons assigned so I’m trying to use these to via the helpers option.

e.g.

house_battery_set_charge_rate_button

growatt_force_charge_on

Thanks

James

I have just tested again using the script and it’s worked. It must of been the server address.

Thanks for your help.

Could you point me in the right direction for a scrip which turns on a timer for a set amount of time?

Thanks

James

Hi James,
Great, glad to hear that.
What should the timer do?

Martin

I just want to set a timer and turn it off and on.

So the options are there, So thinking it might be easier to create 2 versions of set_discharge_rate.

I have tried this but I’m getting: No app description found for: /config/appdaemon/apps/set_charge_rate_intel.py

James

Ah, I see. I could afford to leave my time window hardcoded, I don’t need to change it. I’m sure you could pass as script parameters the current time as the start time and the current time + duration as the end time. You’d pass these parameters in the same way as you pass the stop_battery_soc parameter. It needs some Python skills, but you can use the existing pieces of code as your template.
Good luck!

Martin