Skip to content

Commit 215a9f8

Browse files
first commit
0 parents  commit 215a9f8

File tree

13 files changed

+625
-0
lines changed

13 files changed

+625
-0
lines changed

README.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Home Assistant Sungrow Inverter Integration
2+
3+
This integration allows you to monitor and control your Sungrow solar inverter through Home Assistant using Modbus TCP/RTU protocol.
4+
5+
## Features
6+
7+
- Real-time monitoring of solar production
8+
- Battery status monitoring (if applicable)
9+
- Grid connection status
10+
- Power generation statistics
11+
- System status and alarms
12+
13+
## Supported Devices
14+
15+
- Sungrow Inverters with Modbus TCP/RTU support
16+
17+
## Installation
18+
19+
1. Copy the `sungrow_inverter` folder to your Home Assistant's `custom_components` directory
20+
2. Restart Home Assistant
21+
3. Add the integration through the Home Assistant UI
22+
23+
## Configuration
24+
25+
The integration can be configured through the Home Assistant UI:
26+
27+
1. Go to Configuration > Integrations
28+
2. Click on "+ Add Integration"
29+
3. Search for "Sungrow Inverter"
30+
4. Enter the required connection details:
31+
- Host/IP address (for TCP) or Serial port (for RTU)
32+
- Port (default: 502 for TCP)
33+
- Slave ID (default: 1)
34+
- Connection type (TCP/RTU)
35+
36+
## Modbus Registers
37+
38+
The integration uses the following Modbus registers (based on the official documentation):
39+
40+
- Total power yield (register 5004)
41+
- Total running time (register 5006)
42+
- Internal temperature (register 5008)
43+
- DC voltage and current (registers 5011-5016)
44+
- Grid voltage and current (registers 5019-5024)
45+
- Active power (register 5031)
46+
- Grid frequency (register 5036)
47+
- Work state (register 5038)
48+
49+
## Troubleshooting
50+
51+
If you encounter any issues:
52+
53+
1. Check your network connection to the inverter
54+
2. Verify the Modbus settings in your inverter
55+
3. Check the Home Assistant logs for any error messages
56+
4. Ensure the correct slave ID is configured
57+
58+
## License
59+
60+
This project is licensed under the MIT License - see the LICENSE file for details.
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
"""The Sungrow Inverter integration."""
2+
from __future__ import annotations
3+
4+
import logging
5+
from typing import Any
6+
7+
from homeassistant.config_entries import ConfigEntry
8+
from homeassistant.const import CONF_HOST, CONF_PORT, CONF_TYPE
9+
from homeassistant.core import HomeAssistant
10+
from pymodbus.client import ModbusTcpClient, ModbusSerialClient
11+
from pymodbus.exceptions import ModbusException
12+
13+
from .const import (
14+
CONF_SLAVE_ID,
15+
DEFAULT_PORT,
16+
DEFAULT_SLAVE_ID,
17+
DOMAIN,
18+
PLATFORMS,
19+
)
20+
21+
_LOGGER = logging.getLogger(__name__)
22+
23+
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
24+
"""Set up Sungrow Inverter from a config entry."""
25+
hass.data.setdefault(DOMAIN, {})
26+
27+
# Create Modbus client based on connection type
28+
if entry.data[CONF_TYPE] == "tcp":
29+
client = ModbusTcpClient(
30+
host=entry.data[CONF_HOST],
31+
port=entry.data.get(CONF_PORT, DEFAULT_PORT),
32+
)
33+
else:
34+
client = ModbusSerialClient(
35+
port=entry.data[CONF_HOST],
36+
baudrate=9600,
37+
bytesize=8,
38+
parity="N",
39+
stopbits=1,
40+
)
41+
42+
# Test connection
43+
try:
44+
if not client.connect():
45+
_LOGGER.error("Failed to connect to Sungrow inverter")
46+
return False
47+
except ModbusException as ex:
48+
_LOGGER.error("Modbus error: %s", ex)
49+
return False
50+
51+
hass.data[DOMAIN][entry.entry_id] = {
52+
"client": client,
53+
"slave_id": entry.data.get(CONF_SLAVE_ID, DEFAULT_SLAVE_ID),
54+
}
55+
56+
# Set up platforms
57+
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
58+
59+
return True
60+
61+
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
62+
"""Unload a config entry."""
63+
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
64+
client = hass.data[DOMAIN][entry.entry_id]["client"]
65+
client.close()
66+
hass.data[DOMAIN].pop(entry.entry_id)
67+
68+
return unload_ok
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
"""Config flow for Sungrow Inverter integration."""
2+
from __future__ import annotations
3+
4+
import logging
5+
from typing import Any
6+
7+
import voluptuous as vol
8+
from homeassistant import config_entries
9+
from homeassistant.const import CONF_HOST, CONF_PORT, CONF_TYPE
10+
from homeassistant.data_entry_flow import FlowResult
11+
from pymodbus.client import ModbusTcpClient, ModbusSerialClient
12+
from pymodbus.exceptions import ModbusException
13+
14+
from .const import (
15+
CONF_SLAVE_ID,
16+
DEFAULT_PORT,
17+
DEFAULT_SLAVE_ID,
18+
DOMAIN,
19+
)
20+
21+
_LOGGER = logging.getLogger(__name__)
22+
23+
CONNECTION_TYPES = {
24+
"tcp": "TCP",
25+
"rtu": "RTU (Serial)",
26+
}
27+
28+
class SungrowConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
29+
"""Handle a config flow for Sungrow Inverter."""
30+
31+
VERSION = 1
32+
33+
async def async_step_user(
34+
self, user_input: dict[str, Any] | None = None
35+
) -> FlowResult:
36+
"""Handle the initial step."""
37+
errors: dict[str, str] = {}
38+
39+
if user_input is not None:
40+
# Validate connection
41+
try:
42+
if user_input[CONF_TYPE] == "tcp":
43+
client = ModbusTcpClient(
44+
host=user_input[CONF_HOST],
45+
port=user_input.get(CONF_PORT, DEFAULT_PORT),
46+
)
47+
else:
48+
client = ModbusSerialClient(
49+
port=user_input[CONF_HOST],
50+
baudrate=9600,
51+
bytesize=8,
52+
parity="N",
53+
stopbits=1,
54+
)
55+
56+
if not client.connect():
57+
errors["base"] = "cannot_connect"
58+
else:
59+
client.close()
60+
return self.async_create_entry(
61+
title=f"Sungrow Inverter ({user_input[CONF_HOST]})",
62+
data=user_input,
63+
)
64+
except ModbusException:
65+
errors["base"] = "cannot_connect"
66+
67+
data_schema = vol.Schema(
68+
{
69+
vol.Required(CONF_TYPE): vol.In(CONNECTION_TYPES),
70+
vol.Required(CONF_HOST): str,
71+
vol.Optional(CONF_PORT, default=DEFAULT_PORT): int,
72+
vol.Optional(CONF_SLAVE_ID, default=DEFAULT_SLAVE_ID): int,
73+
}
74+
)
75+
76+
return self.async_show_form(
77+
step_id="user",
78+
data_schema=data_schema,
79+
errors=errors,
80+
)
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
"""Constants for the Sungrow Inverter integration."""
2+
from typing import Final
3+
4+
DOMAIN: Final = "sungrow_inverter"
5+
6+
# Configuration
7+
CONF_SLAVE_ID: Final = "slave_id"
8+
DEFAULT_SLAVE_ID: Final = 1
9+
DEFAULT_PORT: Final = 502
10+
11+
# Platforms
12+
PLATFORMS: Final = ["sensor"]
13+
14+
# Modbus Registers
15+
REGISTER_TOTAL_POWER_YIELD: Final = 5004
16+
REGISTER_TOTAL_RUNNING_TIME: Final = 5006
17+
REGISTER_INTERNAL_TEMPERATURE: Final = 5008
18+
REGISTER_DC_VOLTAGE_1: Final = 5011
19+
REGISTER_DC_CURRENT_1: Final = 5012
20+
REGISTER_DC_VOLTAGE_2: Final = 5013
21+
REGISTER_DC_CURRENT_2: Final = 5014
22+
REGISTER_DC_VOLTAGE_3: Final = 5015
23+
REGISTER_DC_CURRENT_3: Final = 5016
24+
REGISTER_GRID_VOLTAGE_AB: Final = 5019
25+
REGISTER_GRID_VOLTAGE_BC: Final = 5020
26+
REGISTER_GRID_VOLTAGE_CA: Final = 5021
27+
REGISTER_GRID_CURRENT_A: Final = 5022
28+
REGISTER_GRID_CURRENT_B: Final = 5023
29+
REGISTER_GRID_CURRENT_C: Final = 5024
30+
REGISTER_ACTIVE_POWER: Final = 5031
31+
REGISTER_GRID_FREQUENCY: Final = 5036
32+
REGISTER_WORK_STATE: Final = 5038
33+
34+
# Sensor types
35+
SENSOR_TYPES: Final = {
36+
"total_power_yield": {
37+
"name": "Total Power Yield",
38+
"unit": "kWh",
39+
"icon": "mdi:solar-power",
40+
"register": REGISTER_TOTAL_POWER_YIELD,
41+
"scale": 0.1,
42+
},
43+
"total_running_time": {
44+
"name": "Total Running Time",
45+
"unit": "h",
46+
"icon": "mdi:clock-outline",
47+
"register": REGISTER_TOTAL_RUNNING_TIME,
48+
"scale": 0.1,
49+
},
50+
"internal_temperature": {
51+
"name": "Internal Temperature",
52+
"unit": "°C",
53+
"icon": "mdi:thermometer",
54+
"register": REGISTER_INTERNAL_TEMPERATURE,
55+
"scale": 0.1,
56+
},
57+
"dc_voltage_1": {
58+
"name": "DC Voltage 1",
59+
"unit": "V",
60+
"icon": "mdi:lightning-bolt",
61+
"register": REGISTER_DC_VOLTAGE_1,
62+
"scale": 0.1,
63+
},
64+
"dc_current_1": {
65+
"name": "DC Current 1",
66+
"unit": "A",
67+
"icon": "mdi:current-dc",
68+
"register": REGISTER_DC_CURRENT_1,
69+
"scale": 0.1,
70+
},
71+
"grid_voltage_ab": {
72+
"name": "Grid Voltage AB",
73+
"unit": "V",
74+
"icon": "mdi:lightning-bolt",
75+
"register": REGISTER_GRID_VOLTAGE_AB,
76+
"scale": 0.1,
77+
},
78+
"grid_current_a": {
79+
"name": "Grid Current A",
80+
"unit": "A",
81+
"icon": "mdi:current-ac",
82+
"register": REGISTER_GRID_CURRENT_A,
83+
"scale": 0.1,
84+
},
85+
"active_power": {
86+
"name": "Active Power",
87+
"unit": "W",
88+
"icon": "mdi:power-plug",
89+
"register": REGISTER_ACTIVE_POWER,
90+
"scale": 1,
91+
},
92+
"grid_frequency": {
93+
"name": "Grid Frequency",
94+
"unit": "Hz",
95+
"icon": "mdi:sine-wave",
96+
"register": REGISTER_GRID_FREQUENCY,
97+
"scale": 0.01,
98+
},
99+
"work_state": {
100+
"name": "Work State",
101+
"unit": None,
102+
"icon": "mdi:state-machine",
103+
"register": REGISTER_WORK_STATE,
104+
"scale": 1,
105+
},
106+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"domain": "sungrow_inverter",
3+
"name": "Sungrow Inverter",
4+
"documentation": "https://github.com/gabrielkirsten/sungrow-inverter",
5+
"requirements": ["pymodbus>=3.0.0"],
6+
"dependencies": [],
7+
"codeowners": ["@gabrielkirsten"],
8+
"version": "0.1.0",
9+
"config_flow": true,
10+
"iot_class": "local_polling"
11+
}

0 commit comments

Comments
 (0)