Turn Your Dumb Solar Inverter Into a Smart One With This Home Assistant Hack
It’s been 2 years since I fell into the Renewable Energy Rabbit Hole and something this has been able to do, is to also spark my interest in IoT devices and hardware. Something I have realized over the years is that it is not only important for you to have a Solar Inverter setup, you also need data from your inverter setup. Now some inverters come with a manageable energy monitoring solution, the best I have seen so far has to be from Victron with their
I used to use an SRNE inverter and thanks to the good folks at
If you are someone like me who owns the same inverter and you are looking for a way to make it work locally on your Home Assistant network, this tutorial is for you. A basic overview of what we will be doing is taking the data from the Inverter’s
Prerequisites
Connect Your Waveshare RS485 to ETH Adapter to Your Home network.
-
The Waveshare device comes with a power adapter too. You plug it into power, and plug it into the side that has the DC 5V port.
-
Then you take an Ethernet cable, plug one side to your home router or network switch’s LAN port, and plug the other side to the Waveshare device.
-
After that, you should observe to see if the Ethernet port has the usual still and blinking light to confirm the cable connection was successful.
Visit the Waveshare Adapter Page
-
When you check at the back of your Waveshare device, you would see the Default IP, Username and Password. Mine has
192.168.0.7
as the Default IP,admin
as the username andadmin
as password. -
When you visit the default IP, you should see the Waveshare homepage which looks like this.
- However, if you can’t reach it, which I was unable to at first, it’s most likely because your router IP range is out of reach of the Waveshare device’s default IP. So let’s fix that.
Temporarily Change Your Computer’s IP to Match the Waveshare
For me, my router’s range is 192.168.8.x
, but the Waveshare was 192.168.0.7
, so my computer wasn’t able to communicate with it unless I temporarily changed my PC’s IP to match. \
-
Click on edit IP Assignment and choose Manual
-
Toggle IPv4 and you would see a drop-down of settings
-
Set IP Address to
192.168.0.10
(or any other192.168.0.x
, except.7
) -
Subnet Mask (from router’s LAN settings) to
255.255.255.0
-
Gateway to
192.168.0.1
(if required, or leave blank) -
Then save.
Access the Waveshare Device
- Open a browser and go to the Waveshare default IP,
192.168.0.7
- Login to the web interface using the default login details.
- Go to the Local IP Config tab, set the IP to static and pick an IP available on your router’s IP range. For mine, I picked
192.168.8.15
- Apply the changes and reboot the Waveshare device.
- Restore your computer’s IP back to Automatic DHCP.
- Now your Waveshare device should be accessible on your router’s network through the static IP you chose. Ideally, you should reserve that IP you chose for the Waveshare device on your router, through the router’s DHCP settings.
Connect your Inverter to the Waveshare Device
Now we can see the Waveshare device on our network, we are halfway there. Next thing we need to do is to connect the inverter itself to the Waveshare device.
- Get another Ethernet cable and connect one end to the Growatt Inverter RS485 port, which is under the inverter.
- Cut the other end of the Ethernet cable and expose pin 1 (Orange-white) and pin 2 (Orange).
-
Now we are done with all physical connections, let’s go to the Waveshare IP. Click on the Serial Port tab. Change the Work Mode to TCP Server. Baud Rate to 9600, Data Size to 8, Parity to None, Stop Bits to 1, Local Port Number to 502. Then save and restart.
Configure Home Assistant for Modbus RTU Over TCP
The Modbus data is currently being sent from the inverter to your home network. What’s left is to have Home Assistant decode that information and have them as entities. Luckily Home Assistant has a
- We start by modifying the
configuration.yaml
file on your Home Assistant network. - If you are like me, who likes to keep the
configuration.yaml
as lean as possible by having separate yaml files for things and then including them into theconfiguration.yaml
file, go ahead and create a new file calledmodbus.yaml
and a new file calledtemplates.yaml
. - Include the
modbus.yaml
into theconfiguration.yaml
by adding the code below.
modbus: !include modbus.yaml
template: !include templates.yaml
- Then go to the modbus.yaml and paste this code inside it.
#Modbus
- name: growatt
type: rtuovertcp
host: 192.168.8.15 # Waveshare RS485 adapter IP
port: 502 # Change if necessary
delay: 5
timeout: 5
message_wait_milliseconds: 500
sensors:
# System Status
- name: "Growatt System Status"
slave: 1
address: 0
input_type: input
data_type: uint16
# Solar PV Sensors
- name: "Growatt PV1 Voltage"
slave: 1
address: 1
input_type: input
data_type: uint16
scale: 0.1
unit_of_measurement: "V"
- name: "Growatt PV2 Voltage"
slave: 1
address: 2
input_type: input
data_type: uint16
scale: 0.1
unit_of_measurement: "V"
- name: "Growatt PV1 Current"
slave: 1
address: 7
input_type: input
data_type: uint16
scale: 0.1
unit_of_measurement: "A"
- name: "Growatt PV2 Current"
slave: 1
address: 8
input_type: input
data_type: uint16
scale: 0.1
unit_of_measurement: "A"
# PV Power (Read High & Low Registers)
- name: "Growatt PV1 Charge Power High"
slave: 1
address: 3
input_type: input
data_type: uint16
- name: "Growatt PV1 Charge Power Low"
slave: 1
address: 4
input_type: input
data_type: uint16
- name: "Growatt PV2 Charge Power High"
slave: 1
address: 5
input_type: input
data_type: uint16
- name: "Growatt PV2 Charge Power Low"
slave: 1
address: 6
input_type: input
data_type: uint16
# Inverter & System Temperature Sensors
- name: "Growatt Inverter Temperature"
slave: 1
address: 25
input_type: input
data_type: uint16
scale: 0.1
unit_of_measurement: "°C"
- name: "Growatt DC-DC Temperature"
slave: 1
address: 26
input_type: input
data_type: uint16
scale: 0.1
unit_of_measurement: "°C"
# Battery Sensors
- name: "Growatt Battery Voltage"
slave: 1
address: 17
input_type: input
data_type: uint16
scale: 0.01
unit_of_measurement: "V"
- name: "Growatt Battery SOC"
slave: 1
address: 18
input_type: input
data_type: uint16
unit_of_measurement: "%"
- name: "Growatt Battery Power High"
slave: 1
address: 77
input_type: input
data_type: int16
- name: "Growatt Battery Power Low"
slave: 1
address: 78
input_type: input
data_type: int16
# AC Output Sensors
- name: "Growatt AC Output Voltage"
slave: 1
address: 22
input_type: input
data_type: uint16
scale: 0.1
unit_of_measurement: "V"
- name: "Growatt AC Output Frequency"
slave: 1
address: 23
input_type: input
data_type: uint16
scale: 0.01
unit_of_measurement: "Hz"
# AC Charge Power (High & Low)
- name: "Growatt AC Charge Power High"
slave: 1
address: 13
input_type: input
data_type: uint16
- name: "Growatt AC Charge Power Low"
slave: 1
address: 14
input_type: input
data_type: uint16
# Grid Sensors
- name: "Growatt Grid Voltage"
slave: 1
address: 20
input_type: input
data_type: uint16
scale: 0.1
unit_of_measurement: "V"
- name: "Growatt Grid Frequency"
slave: 1
address: 21
input_type: input
data_type: uint16
scale: 0.01
unit_of_measurement: "Hz"
# Load Power
- name: "Growatt Load Power High"
slave: 1
address: 9
input_type: input
data_type: int16
- name: "Growatt Load Power Low"
slave: 1
address: 10
input_type: input
data_type: int16
# Load Percentage
- name: "Growatt Load Percentage"
slave: 1
address: 27
input_type: input
data_type: uint16
scale: 0.1
unit_of_measurement: "%"
# Fan Speeds
- name: "Growatt MPPT Fan Speed"
slave: 1
address: 81
input_type: input
data_type: uint16
unit_of_measurement: "%"
- name: "Growatt Inverter Fan Speed"
slave: 1
address: 82
input_type: input
data_type: uint16
unit_of_measurement: "%"
# Fault Codes
- name: "Growatt Fault Bit"
slave: 1
address: 40
input_type: input
data_type: uint16
- name: "Growatt Warning Bit"
slave: 1
address: 41
input_type: input
data_type: uint16
- name: "Growatt Fault Value"
slave: 1
address: 42
input_type: input
data_type: uint16
- name: "Growatt Warning Value"
slave: 1
address: 43
input_type: input
data_type: uint16
# Work Time Total (High & Low)
- name: "Growatt Work Time Total High"
slave: 1
address: 30
input_type: input
data_type: uint16
- name: "Growatt Work Time Total Low"
slave: 1
address: 31
input_type: input
data_type: uint16
# AC Input Power (High & Low)
- name: "Growatt AC Input Power High"
slave: 1
address: 36
input_type: input
data_type: uint16
- name: "Growatt AC Input Power Low"
slave: 1
address: 37
input_type: input
data_type: uint16
# PV Energy Today & Total
- name: "Growatt Solar Energy Today High"
slave: 1
address: 48
input_type: input
data_type: uint16
- name: "Growatt Solar Energy Today Low"
slave: 1
address: 49
input_type: input
data_type: uint16
- name: "Growatt Solar Energy Total High"
slave: 1
address: 50
input_type: input
data_type: uint16
- name: "Growatt Solar Energy Total Low"
slave: 1
address: 51
input_type: input
data_type: uint16
# AC Charge Energy
- name: "Growatt AC Charge Energy Today High"
slave: 1
address: 56
input_type: input
data_type: uint16
- name: "Growatt AC Charge Energy Today Low"
slave: 1
address: 57
input_type: input
data_type: uint16
- name: "Growatt AC Charge Energy Total High"
slave: 1
address: 58
input_type: input
data_type: uint16
- name: "Growatt AC Charge Energy Total Low"
slave: 1
address: 59
input_type: input
data_type: uint16
# Battery Discharge Energy
- name: "Growatt Battery Discharge Energy Today High"
slave: 1
address: 60
input_type: input
data_type: uint16
- name: "Growatt Battery Discharge Energy Today Low"
slave: 1
address: 61
input_type: input
data_type: uint16
- name: "Growatt Battery Discharge Energy Total High"
slave: 1
address: 62
input_type: input
data_type: uint16
- name: "Growatt Battery Discharge Energy Total Low"
slave: 1
address: 63
input_type: input
data_type: uint16
# AC Discharge Energy
- name: "Growatt AC Discharge Energy Today High"
slave: 1
address: 64
input_type: input
data_type: uint16
- name: "Growatt AC Discharge Energy Today Low"
slave: 1
address: 65
input_type: input
data_type: uint16
- name: "Growatt AC Discharge Energy Total High"
slave: 1
address: 66
input_type: input
data_type: uint16
- name: "Growatt AC Discharge Energy Total Low"
slave: 1
address: 67
input_type: input
data_type: uint16
# AC Charge Current & AC Discharge Power
- name: "Growatt AC Charge Battery Current"
slave: 1
address: 68
input_type: input
data_type: uint16
scale: 0.1
unit_of_measurement: "A"
- name: "Growatt AC Discharge Power High"
slave: 1
address: 69
input_type: input
data_type: uint16
- name: "Growatt AC Discharge Power Low"
slave: 1
address: 70
input_type: input
data_type: uint16
- Then go to the
templates.yaml
and paste this code inside it.
- sensor:
# Growatt Output Power
- name: "Growatt Output Power"
unit_of_measurement: "W"
state_class: measurement
device_class: power
state: >
{% set high = states('sensor.growatt_output_power_high') | int(0) %}
{% set low = states('sensor.growatt_output_power_low') | int(0) %}
{{ ((high * 65536) + low) * 0.1 }}
# Growatt Battery Power
- name: "Growatt Battery Power"
unit_of_measurement: "W"
state_class: measurement
device_class: power
state: >
{% set high = states('sensor.growatt_battery_power_high') | int(0) %}
{% set low = states('sensor.growatt_battery_power_low') | int(0) %}
{{ ((high * 65536) + low) * 0.1 }}
# Growatt PV1 Charge Power
- name: "Growatt PV1 Charge Power"
unit_of_measurement: "W"
state_class: measurement
device_class: power
state: >
{% set high = states('sensor.growatt_pv1_charge_power_high') | int(0) %}
{% set low = states('sensor.growatt_pv1_charge_power_low') | int(0) %}
{{ ((high * 65536) + low) * 0.1 }}
# Growatt PV2 Charge Power
- name: "Growatt PV2 Charge Power"
unit_of_measurement: "W"
state_class: measurement
device_class: power
state: >
{% set high = states('sensor.growatt_pv2_charge_power_high') | int(0) %}
{% set low = states('sensor.growatt_pv2_charge_power_low') | int(0) %}
{{ ((high * 65536) + low) * 0.1 }}
# Growatt Load Power
- name: "Growatt Load Power"
unit_of_measurement: "W"
state_class: measurement
device_class: power
state: >
{% set high = states('sensor.growatt_load_power_high') | int(0) %}
{% set low = states('sensor.growatt_load_power_low') | int(0) %}
{{ ((high * 65536) + low) * 0.1 }}
# Growatt Load Power Alternative (Handles Negative Power)
- name: "Growatt Load Power Signed"
unit_of_measurement: "W"
state_class: measurement
device_class: power
state: >
{% set high = states('sensor.growatt_load_power_high') | int(0) %}
{% set low = states('sensor.growatt_load_power_low') | int(0) %}
{% if high > 32767 %} # Handle negative values (Two's complement)
{% set high = high - 65536 %}
{% endif %}
{{ ((high * 65536) + low) * 0.1 }}
# Growatt AC Charge Power
- name: "Growatt AC Charge Power"
unit_of_measurement: "W"
state_class: measurement
device_class: power
state: >
{% set high = states('sensor.growatt_ac_charge_power_high') | int(0) %}
{% set low = states('sensor.growatt_ac_charge_power_low') | int(0) %}
{{ ((high * 65536) + low) * 0.1 }}
# Growatt Work Time Total
- name: "Growatt Work Time Total"
unit_of_measurement: "Hours"
state_class: measurement
state: >
{% set high = states('sensor.growatt_work_time_total_high') | int(0) %}
{% set low = states('sensor.growatt_work_time_total_low') | int(0) %}
{% set total_seconds = ((high * 65536) + low) * 0.5 %}
{{ (total_seconds / 3600) | round(2) }} # Convert seconds to hours
# Growatt AC Input Power
- name: "Growatt AC Input Power"
unit_of_measurement: "W"
state_class: measurement
device_class: power
state: >
{% set high = states('sensor.growatt_ac_input_power_high') | int(0) %}
{% set low = states('sensor.growatt_ac_input_power_low') | int(0) %}
{{ ((high * 65536) + low) * 0.1 }}
# Growatt Solar Energy Today
- name: "Growatt Solar Energy Today"
unit_of_measurement: "kWh"
state_class: total_increasing
state: >
{% set high = states('sensor.growatt_solar_energy_today_high') | int(0) %}
{% set low = states('sensor.growatt_solar_energy_today_low') | int(0) %}
{{ ((high * 65536) + low) * 0.1 }}
# Growatt Solar Energy Total
- name: "Growatt Solar Energy Total"
unit_of_measurement: "kWh"
state_class: total_increasing
state: >
{% set high = states('sensor.growatt_solar_energy_total_high') | int(0) %}
{% set low = states('sensor.growatt_solar_energy_total_low') | int(0) %}
{{ ((high * 65536) + low) * 0.1 }}
# Growatt AC Charge Energy Today
- name: "Growatt AC Charge Energy Today"
unit_of_measurement: "kWh"
state_class: total_increasing
state: >
{% set high = states('sensor.growatt_ac_charge_energy_today_high') | int(0) %}
{% set low = states('sensor.growatt_ac_charge_energy_today_low') | int(0) %}
{{ ((high * 65536) + low) * 0.1 }}
# Growatt AC Charge Energy Total
- name: "Growatt AC Charge Energy Total"
unit_of_measurement: "kWh"
state_class: total_increasing
state: >
{% set high = states('sensor.growatt_ac_charge_energy_total_high') | int(0) %}
{% set low = states('sensor.growatt_ac_charge_energy_total_low') | int(0) %}
{{ ((high * 65536) + low) * 0.1 }}
# Growatt AC Discharge Power
- name: "Growatt AC Discharge Power"
unit_of_measurement: "W"
state_class: measurement
device_class: power
state: >
{% set high = states('sensor.growatt_ac_discharge_power_high') | int(0) %}
{% set low = states('sensor.growatt_ac_discharge_power_low') | int(0) %}
{{ ((high * 65536) + low) * 0.1 }}
# Growatt AC Discharge Energy Today
- name: "Growatt AC Discharge Energy Today"
unit_of_measurement: "kWh"
state_class: total_increasing
state: >
{% set high = states('sensor.growatt_ac_discharge_energy_today_high') | int(0) %}
{% set low = states('sensor.growatt_ac_discharge_energy_today_low') | int(0) %}
{{ ((high * 65536) + low) * 0.1 }}
# Growatt AC Discharge Energy Total
- name: "Growatt AC Discharge Energy Total"
unit_of_measurement: "kWh"
state_class: total_increasing
state: >
{% set high = states('sensor.growatt_ac_discharge_energy_total_high') | int(0) %}
{% set low = states('sensor.growatt_ac_discharge_energy_total_low') | int(0) %}
{{ ((high * 65536) + low) * 0.1 }}
# Growatt Battery Discharge Energy Today
- name: "Growatt Battery Discharge Energy Today"
unit_of_measurement: "kWh"
state_class: total_increasing
state: >
{% set high = states('sensor.growatt_battery_discharge_energy_today_high') | int(0) %}
{% set low = states('sensor.growatt_battery_discharge_energy_today_low') | int(0) %}
{{ ((high * 65536) + low) * 0.1 }}
# Growatt Battery Discharge Energy Total
- name: "Growatt Battery Discharge Energy Total"
unit_of_measurement: "kWh"
state_class: total_increasing
state: >
{% set high = states('sensor.growatt_battery_discharge_energy_total_high') | int(0) %}
{% set low = states('sensor.growatt_battery_discharge_energy_total_low') | int(0) %}
{{ ((high * 65536) + low) * 0.1 }}
# Growatt System Status (Human-Readable)
- name: "Growatt System Status Description"
state: >
{% set status = states('sensor.growatt_system_status') | int(0) %}
{% if status == 0 %} Standby
{% elif status == 1 %} No Use
{% elif status == 2 %} Discharge
{% elif status == 3 %} Fault
{% elif status == 4 %} Flash
{% elif status == 5 %} PV charge
{% elif status == 6 %} AC charge
{% elif status == 7 %} Combine charge
{% elif status == 8 %} Combine charge and Bypass
{% elif status == 9 %} PV charge and Bypass
{% elif status == 10 %} AC charge and Bypass
{% elif status == 11 %} Bypass
{% elif status == 12 %} PV charge and Discharge
{% else %} Unknown Status ({{ status }})
{% endif %}
- Finally restart your Home Assistant network.
Conclusion
Now the Modbus sensor entities we created should be showing up in Home Assistant. You can confirm by going to the Entities tab in Home Assistant and filtering by Modbus integration or you search “Growatt” there.
Also for some context, you would observe in the modbus.yaml
file, there are High and Low of some values. Those values are stored as two registers (high and low bytes), so they need to be combined correctly. Since Home Assistant doesn’t support direct bit-shifting in Modbus, we use template sensors in the templates.yaml
file to combine the values.
Thanks for reading and feel free to share your experience in the comment section.