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 inverters #12

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
127 changes: 71 additions & 56 deletions custom_components/solaredge_modbus/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
DOMAIN,
DEFAULT_NAME,
DEFAULT_SCAN_INTERVAL,
CONF_NUMBER_INVERTERS,
DEFAULT_NUMBER_INVERTERS,
CONF_READ_METER1,
CONF_READ_METER2,
CONF_READ_METER3,
Expand All @@ -41,6 +43,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 All @@ -66,11 +71,15 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
read_meter1 = entry.data.get(CONF_READ_METER1, False)
read_meter2 = entry.data.get(CONF_READ_METER2, False)
read_meter3 = entry.data.get(CONF_READ_METER3, 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
hass, name, host, port, scan_interval, read_meter1, read_meter2, read_meter3, number_of_inverters
)
"""Register the hub."""
hass.data[DOMAIN][name] = {"hub": hub}
Expand Down Expand Up @@ -112,6 +121,7 @@ def __init__(
read_meter1=False,
read_meter2=False,
read_meter3=False,
number_of_inverters=1,
):
"""Initialize the Modbus hub."""
self._hass = hass
Expand All @@ -121,6 +131,7 @@ def __init__(
self.read_meter1 = read_meter1
self.read_meter2 = read_meter2
self.read_meter3 = read_meter3
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 @@ -186,43 +197,47 @@ def calculate_value(self, value, sf):

def read_modbus_data_stub(self):
return (
self.read_modbus_data_inverter_stub()
self.read_modbus_data_inverters_stub()
and self.read_modbus_data_meter1_stub()
and self.read_modbus_data_meter2_stub()
and self.read_modbus_data_meter3_stub()
)

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()
)

def read_modbus_data_inverter_stub(self):
self.data["accurrent"] = 1
self.data["accurrenta"] = 1
self.data["accurrentb"] = 1
self.data["accurrentc"] = 1
self.data["acvoltageab"] = 1
self.data["acvoltagebc"] = 1
self.data["acvoltageca"] = 1
self.data["acvoltagean"] = 1
self.data["acvoltagebn"] = 1
self.data["acvoltagecn"] = 1
self.data["acpower"] = 1
self.data["acfreq"] = 1
self.data["acva"] = 1
self.data["acvar"] = 1
self.data["acpf"] = 1
self.data["acenergy"] = 1
self.data["dccurrent"] = 1
self.data["dcvoltage"] = 1
self.data["dcpower"] = 1
self.data["tempsink"] = 1
self.data["status"] = 1
self.data["statusvendor"] = 1
# Don't really understand the purpose of this stub method
# and when it would be called and whether the number of inverters
# would be available when called (default to 1)
def read_modbus_data_inverters_stub(self):
for inverter_index in range(self.number_of_inverters):
inverter_prefix = "i" + str(inverter_index + 1) + "_"
self.data[inverter_prefix + "accurrent"] = 1
self.data[inverter_prefix + "accurrenta"] = 1
self.data[inverter_prefix + "accurrentb"] = 1
self.data[inverter_prefix + "accurrentc"] = 1
self.data[inverter_prefix + "acvoltageab"] = 1
self.data[inverter_prefix + "acvoltagebc"] = 1
self.data[inverter_prefix + "acvoltageca"] = 1
self.data[inverter_prefix + "acvoltagean"] = 1
self.data[inverter_prefix + "acvoltagebn"] = 1
self.data[inverter_prefix + "acvoltagecn"] = 1
self.data[inverter_prefix + "acpower"] = 1
self.data[inverter_prefix + "acfreq"] = 1
self.data[inverter_prefix + "acva"] = 1
self.data[inverter_prefix + "acvar"] = 1
self.data[inverter_prefix + "acpf"] = 1
self.data[inverter_prefix + "acenergy"] = 1
self.data[inverter_prefix + "dccurrent"] = 1
self.data[inverter_prefix + "dcvoltage"] = 1
self.data[inverter_prefix + "dcpower"] = 1
self.data[inverter_prefix + "tempsink"] = 1
self.data[inverter_prefix + "status"] = 1
self.data[inverter_prefix + "statusvendor"] = 1

return True

Expand Down Expand Up @@ -628,9 +643,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 @@ -645,10 +663,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 @@ -665,66 +683,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 = self.calculate_value(acenergy, acenergysf)

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 @@ -737,13 +755,10 @@ 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
statusvendor = decoder.decode_16bit_int()
self.data["statusvendor"] = statusvendor

return True
else:
return False
self.data[inverter_prefix + "statusvendor"] = statusvendor
return True
5 changes: 4 additions & 1 deletion custom_components/solaredge_modbus/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
DEFAULT_NAME,
DEFAULT_SCAN_INTERVAL,
DEFAULT_PORT,
CONF_NUMBER_INVERTERS,
DEFAULT_NUMBER_INVERTERS,
CONF_READ_METER1,
CONF_READ_METER2,
CONF_READ_METER3,
Expand All @@ -24,6 +26,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 @@ -83,4 +87,3 @@ async def async_step_user(self, user_input=None):
return self.async_show_form(
step_id="user", data_schema=DATA_SCHEMA, errors=errors
)

2 changes: 2 additions & 0 deletions custom_components/solaredge_modbus/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
DEFAULT_NAME = "solaredge"
DEFAULT_SCAN_INTERVAL = 30
DEFAULT_PORT = 1502
DEFAULT_NUMBER_INVERTERS = 1
DEFAULT_READ_METER1 = False
DEFAULT_READ_METER2 = False
DEFAULT_READ_METER3 = 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
25 changes: 14 additions & 11 deletions custom_components/solaredge_modbus/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,20 @@ async def async_setup_entry(hass, entry, async_add_entities):
}

entities = []
for sensor_info in SENSOR_TYPES.values():
sensor = SolarEdgeSensor(
hub_name,
hub,
device_info,
sensor_info[0],
sensor_info[1],
sensor_info[2],
sensor_info[3],
)
entities.append(sensor)
for inverter_index in range(hub.number_of_inverters):
inverter_variable_prefix = "i" + str(inverter_index + 1) + "_"
inverter_title_prefix = "I" + str(inverter_index + 1) + " "
for sensor_info in SENSOR_TYPES.values():
sensor = SolarEdgeSensor(
hub_name,
hub,
device_info,
inverter_title_prefix + sensor_info[0],
inverter_variable_prefix + sensor_info[1],
sensor_info[2],
sensor_info[3],
)
entities.append(sensor)

if hub.read_meter1 == True:
for meter_sensor_info in METER1_SENSOR_TYPES.values():
Expand Down
1 change: 1 addition & 0 deletions custom_components/solaredge_modbus/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"host": "The ip-address of your Solaredge device",
"name": "The prefix to be used for your SolarEdge sensors",
"port": "The TCP port on which to connect to the SolarEdge",
"number_of_inverters": "The number of inverters connected",
"read_meter_1": "Read meter 1 data (only for meter models)",
"read_meter_2": "Read meter 2 data (only for meter models)",
"read_meter_3": "Read meter 3 data (only for meter models)",
Expand Down
1 change: 1 addition & 0 deletions custom_components/solaredge_modbus/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"host": "The ip-address of your Solaredge inverter",
"name": "The prefix to be used for your SolarEdge sensors",
"port": "The TCP port on which to connect to the SolarEdge inverter",
"number_of_inverters": "The number of inverters connected",
"read_meter_1": "Read meter 1 data (only for meter models)",
"read_meter_2": "Read meter 2 data (only for meter models)",
"read_meter_3": "Read meter 3 data (only for meter models)",
Expand Down
1 change: 1 addition & 0 deletions custom_components/solaredge_modbus/translations/nb.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"host": "IP-adressen til din Solaredge-omformer",
"name": "Prefikset som skal brukes til SolarEdge-sensorene dine",
"port": "TCP-porten som skal kobles til SolarEdge-omformeren",
"number_of_inverters": "Antall omformere koblet sammen",
"read_meter_1": "Les måler 1-data (bare for målermodeller)",
"read_meter_2": "Les måler 2-data (bare for målermodeller)",
"read_meter_3": "Les måler 3-data (bare for målermodeller)",
Expand Down
1 change: 1 addition & 0 deletions custom_components/solaredge_modbus/translations/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"host": "Het ip-adres van uw Solaredge-omvormer",
"name": "Het voorvoegsel dat moet worden gebruikt voor uw SolarEdge-sensoren",
"port": "De TCP-poort waarop verbinding moet worden gemaakt met de SolarEdge-omvormer",
"number_of_inverters": "Aantal aangesloten omvormers",
"read_meter_1": "Lees meter 1 data (enkel voor voor meter modellen)",
"read_meter_2": "Lees meter 2 data (enkel voor voor meter modellen)",
"read_meter_3": "Lees meter 3 data (enkel voor voor meter modellen)",
Expand Down