Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple inverter support rollup #65

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
651e960
Update nl.json
WillCodeForCats Aug 23, 2021
1a88771
Import PR#12 changes
WillCodeForCats Aug 23, 2021
13c6120
Import PR#12 changes
WillCodeForCats Aug 23, 2021
858561c
Import PR#12 changes
WillCodeForCats Aug 23, 2021
506bcc4
Remove _stub methods
WillCodeForCats Aug 23, 2021
d1150d0
Import changes from PR#12
WillCodeForCats Aug 23, 2021
cbcdb64
Import changes from PR#12
WillCodeForCats Aug 23, 2021
9a8da93
Update README.md
WillCodeForCats Aug 23, 2021
473b330
Import changes from PR#12
WillCodeForCats Aug 23, 2021
4f58448
Import changes from PR#12
WillCodeForCats Aug 23, 2021
69c8b5e
Import changes from PR#27
WillCodeForCats Aug 23, 2021
cd9cbc5
Merge branch 'binsentsu:master' into master
WillCodeForCats Aug 26, 2021
71e72f5
Merge branch 'binsentsu:master' into master
WillCodeForCats Sep 7, 2021
580a100
fix indents
WillCodeForCats Sep 7, 2021
0f6f23b
fix indents
WillCodeForCats Sep 7, 2021
f0b1ed7
Update README.md
WillCodeForCats Sep 9, 2021
c7420c1
Add files via upload
WillCodeForCats Sep 13, 2021
a56d57b
Add files via upload
WillCodeForCats Sep 13, 2021
110fbae
Added info about the setup this is tested on
WillCodeForCats Sep 13, 2021
2fda2db
Update README.md
WillCodeForCats Sep 13, 2021
ddd10c1
Update README.md
WillCodeForCats Sep 15, 2021
1a2c828
Fix spelling
WillCodeForCats Sep 17, 2021
233417c
Fix spelling
WillCodeForCats Sep 17, 2021
7dda85d
Update README.md
WillCodeForCats Sep 22, 2021
2f803f3
Update README.md
WillCodeForCats Sep 22, 2021
a5e2e94
Update README.md
WillCodeForCats Sep 22, 2021
7c7ca1d
German translation #54
WillCodeForCats Oct 11, 2021
c4f9769
Make all non-energy sensors state class measurement #53
WillCodeForCats Oct 11, 2021
05ea23e
Change default scan interval to 60 seconds
WillCodeForCats Oct 18, 2021
16fdfbc
Update const.py
WillCodeForCats Oct 20, 2021
add9038
Add status_text sensor
WillCodeForCats Oct 20, 2021
618a442
Add files via upload
WillCodeForCats Oct 20, 2021
9882d5c
Add files via upload
WillCodeForCats Oct 20, 2021
6e66b71
Add files via upload
WillCodeForCats Oct 20, 2021
647e165
Add vendor status text sensor
WillCodeForCats Oct 20, 2021
37e2dd6
Add files via upload
WillCodeForCats Oct 20, 2021
7901fb1
Fix conflicts
WillCodeForCats Oct 21, 2021
85d97a9
Update README.md
WillCodeForCats Oct 21, 2021
091399b
Update README.md
WillCodeForCats Oct 21, 2021
2942fbb
Update de.json
WillCodeForCats Oct 21, 2021
2bc2b37
Update en.json
WillCodeForCats Oct 21, 2021
ad9f65f
Create .gitignore
WillCodeForCats Oct 21, 2021
b640ebf
Update README.md
WillCodeForCats Oct 21, 2021
99fbc60
Update en.json
WillCodeForCats Oct 21, 2021
cc73d08
Delete de.json
WillCodeForCats Oct 21, 2021
03ff9ce
Update strings.json
WillCodeForCats Oct 21, 2021
e15f86f
Update strings.json
WillCodeForCats Oct 21, 2021
bd626a0
Update const.py
WillCodeForCats Oct 21, 2021
41060d0
Update const.py
WillCodeForCats Oct 21, 2021
13058b9
Update const.py
WillCodeForCats Oct 21, 2021
33cbfd6
Update sensor.py
WillCodeForCats Oct 21, 2021
b872854
Update __init__.py
WillCodeForCats Oct 21, 2021
7d81d5b
Merge pull request #1 from binsentsu/master
WillCodeForCats Oct 21, 2021
b4e39e1
Files from multiple inverter installation
WillCodeForCats Oct 21, 2021
949e747
Update const.py
WillCodeForCats Oct 21, 2021
9213044
Merge translation files
WillCodeForCats Oct 21, 2021
a171237
Delete .gitignore
WillCodeForCats Oct 21, 2021
b3c065c
Update strings.json
WillCodeForCats Oct 21, 2021
a470d14
Merge branch 'feature/multiple-inverter' of https://github.com/WillCo…
WillCodeForCats Oct 21, 2021
a5282ab
Create .DS_Store
WillCodeForCats Oct 21, 2021
a356c22
Delete .DS_Store
WillCodeForCats Oct 21, 2021
875e5d3
Multiple inverter support for battery
WillCodeForCats Oct 21, 2021
c58f786
Update sensor.py
WillCodeForCats Oct 21, 2021
1e3178d
Update config_flow.py
WillCodeForCats Oct 21, 2021
0b125ea
Update __init__.py
WillCodeForCats Oct 21, 2021
d051bba
Merge battery stuff
WillCodeForCats Oct 21, 2021
80dcbc5
Cleanup
WillCodeForCats Oct 21, 2021
2290db9
Cleanup
WillCodeForCats Oct 21, 2021
9e62741
Fix spelling
WillCodeForCats Oct 21, 2021
1a20577
Update const.py
WillCodeForCats Oct 21, 2021
cd2a892
Match live environment
WillCodeForCats Oct 21, 2021
814c75b
Fix spelling
WillCodeForCats Oct 21, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 60 additions & 35 deletions custom_components/solaredge_modbus/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,24 @@
DOMAIN,
DEFAULT_NAME,
DEFAULT_SCAN_INTERVAL,
CONF_NUMBER_INVERTERS,
CONF_READ_METER1,
CONF_READ_METER2,
CONF_READ_METER3,
CONF_READ_BATTERY1,
CONF_READ_BATTERY2,
DEFAULT_NUMBER_INVERTERS,
DEFAULT_READ_METER1,
DEFAULT_READ_METER2,
DEFAULT_READ_METER3,
DEFAULT_READ_BATTERY1,
DEFAULT_READ_BATTERY2,
BATTERY_STATUSSES,
BATTERY_STATUSES,
STOREDGE_CONTROL_MODE,
STOREDGE_AC_CHARGE_POLICY,
STOREDGE_CHARGE_DISCHARGE_MODE
STOREDGE_CHARGE_DISCHARGE_MODE,
DEVICE_STATUSES,
VENDOR_STATUSES,
)

_LOGGER = logging.getLogger(__name__)
Expand All @@ -52,6 +56,9 @@
vol.Optional(
CONF_SCAN_INTERVAL, default=DEFAULT_SCAN_INTERVAL
): cv.positive_int,
vol.Optional(
CONF_NUMBER_INVERTERS, default=DEFAULT_NUMBER_INVERTERS
): cv.positive_int,
}
)

Expand Down Expand Up @@ -79,11 +86,15 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
read_meter3 = entry.data.get(CONF_READ_METER3, False)
read_battery1 = entry.data.get(CONF_READ_BATTERY1, False)
read_battery2 = entry.data.get(CONF_READ_BATTERY2, False)
number_of_inverters = entry.data.get(CONF_NUMBER_INVERTERS, 1)
# TODO is there anyway to ensure we don't receive 0 during config flow?
if number_of_inverters < 1:
number_of_inverters = 1

_LOGGER.debug("Setup %s.%s", DOMAIN, name)

hub = SolaredgeModbusHub(
hass, name, host, port, scan_interval, read_meter1, read_meter2, read_meter3, read_battery1, read_battery2
hass, name, host, port, scan_interval, read_meter1, read_meter2, read_meter3, read_battery1, read_battery2, number_of_inverters
)
"""Register the hub."""
hass.data[DOMAIN][name] = {"hub": hub}
Expand Down Expand Up @@ -138,7 +149,8 @@ def __init__(
read_meter2=False,
read_meter3=False,
read_battery1=False,
read_battery2=False
read_battery2=False,
number_of_inverters=1,
):
"""Initialize the Modbus hub."""
self._hass = hass
Expand All @@ -150,6 +162,7 @@ def __init__(
self.read_meter3 = read_meter3
self.read_battery1 = read_battery1
self.read_battery2 = read_battery2
self.number_of_inverters = number_of_inverters
self._scan_interval = timedelta(seconds=scan_interval)
self._unsub_interval_method = None
self._sensors = []
Expand Down Expand Up @@ -225,7 +238,7 @@ def calculate_value(self, value, sf):

def read_modbus_data(self):
return (
self.read_modbus_data_inverter()
self.read_modbus_data_inverters()
and self.read_modbus_data_meter1()
and self.read_modbus_data_meter2()
and self.read_modbus_data_meter3()
Expand Down Expand Up @@ -548,9 +561,12 @@ def read_modbus_data_meter(self, meter_prefix, start_address):
else:
return False

def read_modbus_data_inverter(self):
inverter_data = self.read_holding_registers(unit=1, address=40071, count=38)
if not inverter_data.isError():
def read_modbus_data_inverters(self):
for inverter_index in range(self.number_of_inverters):
inverter_prefix = "i" + str(inverter_index + 1) + "_"
inverter_data = self.read_holding_registers(unit=inverter_index + 1, address=40071, count=38)
if inverter_data.isError():
return False
decoder = BinaryPayloadDecoder.fromRegisters(
inverter_data.registers, byteorder=Endian.Big
)
Expand All @@ -565,10 +581,10 @@ def read_modbus_data_inverter(self):
accurrentb = self.calculate_value(accurrentb, accurrentsf)
accurrentc = self.calculate_value(accurrentc, accurrentsf)

self.data["accurrent"] = round(accurrent, abs(accurrentsf))
self.data["accurrenta"] = round(accurrenta, abs(accurrentsf))
self.data["accurrentb"] = round(accurrentb, abs(accurrentsf))
self.data["accurrentc"] = round(accurrentc, abs(accurrentsf))
self.data[inverter_prefix + "accurrent"] = round(accurrent, abs(accurrentsf))
self.data[inverter_prefix + "accurrenta"] = round(accurrenta, abs(accurrentsf))
self.data[inverter_prefix + "accurrentb"] = round(accurrentb, abs(accurrentsf))
self.data[inverter_prefix + "accurrentc"] = round(accurrentc, abs(accurrentsf))

acvoltageab = decoder.decode_16bit_uint()
acvoltagebc = decoder.decode_16bit_uint()
Expand All @@ -585,66 +601,66 @@ def read_modbus_data_inverter(self):
acvoltagebn = self.calculate_value(acvoltagebn, acvoltagesf)
acvoltagecn = self.calculate_value(acvoltagecn, acvoltagesf)

self.data["acvoltageab"] = round(acvoltageab, abs(acvoltagesf))
self.data["acvoltagebc"] = round(acvoltagebc, abs(acvoltagesf))
self.data["acvoltageca"] = round(acvoltageca, abs(acvoltagesf))
self.data["acvoltagean"] = round(acvoltagean, abs(acvoltagesf))
self.data["acvoltagebn"] = round(acvoltagebn, abs(acvoltagesf))
self.data["acvoltagecn"] = round(acvoltagecn, abs(acvoltagesf))
self.data[inverter_prefix + "acvoltageab"] = round(acvoltageab, abs(acvoltagesf))
self.data[inverter_prefix + "acvoltagebc"] = round(acvoltagebc, abs(acvoltagesf))
self.data[inverter_prefix + "acvoltageca"] = round(acvoltageca, abs(acvoltagesf))
self.data[inverter_prefix + "acvoltagean"] = round(acvoltagean, abs(acvoltagesf))
self.data[inverter_prefix + "acvoltagebn"] = round(acvoltagebn, abs(acvoltagesf))
self.data[inverter_prefix + "acvoltagecn"] = round(acvoltagecn, abs(acvoltagesf))

acpower = decoder.decode_16bit_int()
acpowersf = decoder.decode_16bit_int()
acpower = self.calculate_value(acpower, acpowersf)

self.data["acpower"] = round(acpower, abs(acpowersf))
self.data[inverter_prefix + "acpower"] = round(acpower, abs(acpowersf))

acfreq = decoder.decode_16bit_uint()
acfreqsf = decoder.decode_16bit_int()
acfreq = self.calculate_value(acfreq, acfreqsf)

self.data["acfreq"] = round(acfreq, abs(acfreqsf))
self.data[inverter_prefix + "acfreq"] = round(acfreq, abs(acfreqsf))

acva = decoder.decode_16bit_int()
acvasf = decoder.decode_16bit_int()
acva = self.calculate_value(acva, acvasf)

self.data["acva"] = round(acva, abs(acvasf))
self.data[inverter_prefix + "acva"] = round(acva, abs(acvasf))

acvar = decoder.decode_16bit_int()
acvarsf = decoder.decode_16bit_int()
acvar = self.calculate_value(acvar, acvarsf)

self.data["acvar"] = round(acvar, abs(acvarsf))
self.data[inverter_prefix + "acvar"] = round(acvar, abs(acvarsf))

acpf = decoder.decode_16bit_int()
acpfsf = decoder.decode_16bit_int()
acpf = self.calculate_value(acpf, acpfsf)

self.data["acpf"] = round(acpf, abs(acpfsf))
self.data[inverter_prefix + "acpf"] = round(acpf, abs(acpfsf))

acenergy = decoder.decode_32bit_uint()
acenergysf = decoder.decode_16bit_uint()
acenergy = validate(self.calculate_value(acenergy, acenergysf), ">", 0)

self.data["acenergy"] = round(acenergy * 0.001, 3)
self.data[inverter_prefix + "acenergy"] = round(acenergy * 0.001, 3)

dccurrent = decoder.decode_16bit_uint()
dccurrentsf = decoder.decode_16bit_int()
dccurrent = self.calculate_value(dccurrent, dccurrentsf)

self.data["dccurrent"] = round(dccurrent, abs(dccurrentsf))
self.data[inverter_prefix + "dccurrent"] = round(dccurrent, abs(dccurrentsf))

dcvoltage = decoder.decode_16bit_uint()
dcvoltagesf = decoder.decode_16bit_int()
dcvoltage = self.calculate_value(dcvoltage, dcvoltagesf)

self.data["dcvoltage"] = round(dcvoltage, abs(dcvoltagesf))
self.data[inverter_prefix + "dcvoltage"] = round(dcvoltage, abs(dcvoltagesf))

dcpower = decoder.decode_16bit_int()
dcpowersf = decoder.decode_16bit_int()
dcpower = self.calculate_value(dcpower, dcpowersf)

self.data["dcpower"] = round(dcpower, abs(dcpowersf))
self.data[inverter_prefix + "dcpower"] = round(dcpower, abs(dcpowersf))

# skip register
decoder.skip_bytes(2)
Expand All @@ -657,16 +673,25 @@ def read_modbus_data_inverter(self):
tempsf = decoder.decode_16bit_int()
tempsink = self.calculate_value(tempsink, tempsf)

self.data["tempsink"] = round(tempsink, abs(tempsf))
self.data[inverter_prefix + "tempsink"] = round(tempsink, abs(tempsf))

status = decoder.decode_16bit_int()
self.data["status"] = status
self.data[inverter_prefix + "status"] = status

if status in DEVICE_STATUSES:
self.data[inverter_prefix + "status_text"] = DEVICE_STATUSES[status]
else:
self.data[inverter_prefix + "status_text"] = "Unknown"

statusvendor = decoder.decode_16bit_int()
self.data["statusvendor"] = statusvendor
self.data[inverter_prefix + "statusvendor"] = statusvendor

if statusvendor in VENDOR_STATUSES:
self.data[inverter_prefix + "statusvendor_text"] = VENDOR_STATUSES[statusvendor]
else:
self.data[inverter_prefix + "statusvendor_text"] = "Unknown"

return True
else:
return False
return True

def read_modbus_data_storage(self):
if not self.read_battery1 and not self.read_battery2:
Expand Down Expand Up @@ -834,8 +859,8 @@ def decode_string(decoder):
self.data[battery_prefix + 'current'] = 0
self.data[battery_prefix + 'power'] = 0

if battery_status in BATTERY_STATUSSES:
self.data[battery_prefix + 'status'] = BATTERY_STATUSSES[battery_status]
if battery_status in BATTERY_STATUSES:
self.data[battery_prefix + 'status'] = BATTERY_STATUSES[battery_status]
else:
self.data[battery_prefix + 'status'] = battery_status

Expand Down
7 changes: 5 additions & 2 deletions custom_components/solaredge_modbus/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,18 @@
DEFAULT_NAME,
DEFAULT_SCAN_INTERVAL,
DEFAULT_PORT,
CONF_NUMBER_INVERTERS,
CONF_READ_METER1,
CONF_READ_METER2,
CONF_READ_METER3,
CONF_READ_BATTERY1,
CONF_READ_BATTERY2,
DEFAULT_NUMBER_INVERTERS,
DEFAULT_READ_METER1,
DEFAULT_READ_METER2,
DEFAULT_READ_METER3,
DEFAULT_READ_BATTERY1,
DEFAULT_READ_BATTERY2
DEFAULT_READ_BATTERY2,
)
from homeassistant.core import HomeAssistant, callback

Expand All @@ -28,6 +30,8 @@
vol.Optional(CONF_NAME, default=DEFAULT_NAME): str,
vol.Required(CONF_HOST): str,
vol.Required(CONF_PORT, default=DEFAULT_PORT): int,
# Would like to restrict this to only allow numbers between 1-9 but not sure how
vol.Required(CONF_NUMBER_INVERTERS, default=DEFAULT_NUMBER_INVERTERS): int,
vol.Optional(CONF_READ_METER1, default=DEFAULT_READ_METER1): bool,
vol.Optional(CONF_READ_METER2, default=DEFAULT_READ_METER2): bool,
vol.Optional(CONF_READ_METER3, default=DEFAULT_READ_METER3): bool,
Expand Down Expand Up @@ -89,4 +93,3 @@ async def async_step_user(self, user_input=None):
return self.async_show_form(
step_id="user", data_schema=DATA_SCHEMA, errors=errors
)

76 changes: 65 additions & 11 deletions custom_components/solaredge_modbus/const.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
DOMAIN = "solaredge_modbus"
DEFAULT_NAME = "solaredge"
DEFAULT_SCAN_INTERVAL = 30
DEFAULT_SCAN_INTERVAL = 60
DEFAULT_PORT = 1502
DEFAULT_NUMBER_INVERTERS = 1
DEFAULT_READ_METER1 = False
DEFAULT_READ_METER2 = False
DEFAULT_READ_METER3 = False
DEFAULT_READ_BATTERY1 = False
DEFAULT_READ_BATTERY2 = False
CONF_SOLAREDGE_HUB = "solaredge_hub"
ATTR_STATUS_DESCRIPTION = "status_description"
ATTR_MANUFACTURER = "Solaredge"
CONF_NUMBER_INVERTERS = "number_of_inverters"
CONF_READ_METER1 = "read_meter_1"
CONF_READ_METER2 = "read_meter_2"
CONF_READ_METER3 = "read_meter_3"
Expand Down Expand Up @@ -38,10 +39,11 @@
"DC_Power": ["DC Power", "dcpower", "W", "mdi:solar-power"],
"Temp_Sink": ["Temp Sink", "tempsink", "°C", None],
"Status": ["Status", "status", None, None],
"Status_Text": ["Status Text", "status_text", None, None],
"Status_Vendor": ["Status Vendor", "statusvendor", None, None],
"Status_Vendor_Text": ["Status Vendor Text", "statusvendor_text", None, None],
}


METER1_SENSOR_TYPES = {
"M1_AC_Current": ["M1 AC Current", "m1_accurrent", "A", "mdi:current-ac"],
"M1_AC_Current_A": ["M1 AC Current_A", "m1_accurrenta", "A", "mdi:current-ac"],
Expand Down Expand Up @@ -264,18 +266,31 @@
"BATTERY2_Status": ["Battery2 Status", "battery2_status", None, None],
}

DEVICE_STATUSSES = {
# parameter names per sunspec
#DEVICE_STATUSES = {
# 1: "I_STATUS_OFF",
# 2: "I_STATUS_SLEEPING",
# 3: "I_STATUS_STARTING",
# 4: "I_STATUS_MPPT",
# 5: "I_STATUS_THROTTLED",
# 6: "I_STATUS_SHUTTING_DOWN",
# 7: "I_STATUS_FAULT",
# 8: "I_STATUS_STANDBY",
#}

# English descriptions of parameter names
DEVICE_STATUSES = {
1: "Off",
2: "Sleeping (auto-shutdown) – Night mode",
3: "Grid Monitoring/wake-up",
4: "Inverter is ON and producing power",
5: "Production (curtailed)",
6: "Shutting down",
2: "Sleeping (Auto-Shutdown)",
3: "Grid Monitoring",
4: "Production",
5: "Production (Curtailed)",
6: "Shutting Down",
7: "Fault",
8: "Maintenance/setup",
8: "Maintenance",
}

BATTERY_STATUSSES = {
BATTERY_STATUSES = {
1: "Off",
3: "Charging",
4: "Discharging",
Expand Down Expand Up @@ -323,3 +338,42 @@
["Storage Remote Charge Limit", "storage_remote_charge_limit", 0xE00E, "f", {"min": 0, "max": 20000, "unit": "W"}],
["Storage Remote Discharge Limit", "storage_remote_discharge_limit", 0xE010, "f", {"min": 0, "max": 20000, "unit": "W"}],
]

VENDOR_STATUSES = {
0: "No Error",
17: "Temperature too high",
25: "Isolation faults",
27: "Hardware error",
31: "AC voltage too high",
33: "AC voltage too high",
32: "AC voltage too low",
34: "AC freq. too high",
35: "AC freq. too low",
41: "AC voltage too low",
44: "No country selected",
64: "AC voltage too high",
65: "AC voltage too high",
66: "AC voltage too high",
61: "AC voltage too low",
62: "AC voltage too low",
63: "AC voltage too low",
67: "AC voltage too low",
68: "AC voltage too low",
69: "AC voltage too low",
79: "AC freq. too high",
80: "AC freq. too high",
81: "AC freq. too high",
82: "AC freq. too low",
83: "AC freq. too low",
84: "AC freq. too low",
95: "Hardware error",
104: "Temperature too high",
106: "Hardware error",
120: "Hardware error",
121: "Isolation faults",
125: "Hardware error",
126: "Hardware error",
150: "Arc fault detected",
151: "Arc fault detected",
153: "Hardware error",
}
Loading