Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
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
108 changes: 86 additions & 22 deletions custom_components/solaredge_modbus_multi/hub.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
from pymodbus.client.sync import ModbusTcpClient
from pymodbus.compat import iteritems
from pymodbus.constants import Endian
from pymodbus.exceptions import ConnectionException
from pymodbus.payload import BinaryPayloadDecoder
from pymodbus.pdu import ModbusExceptions

from .const import DOMAIN, SUNSPEC_NOT_IMPL_UINT16
from .helpers import parse_modbus_string
Expand Down Expand Up @@ -77,6 +79,7 @@ def __init__(
self._host = host
self._port = port
self._lock = threading.Lock()
self._client = None
self._name = name
self._id = name.lower()
self.number_of_inverters = number_of_inverters
Expand All @@ -88,9 +91,6 @@ def __init__(
self.inverters = []
self.meters = []
self.batteries = []

self._client = ModbusTcpClient(host=self._host, port=self._port)

self.initalized = False
self.online = False

Expand All @@ -114,7 +114,11 @@ async def _async_init_solaredge(self) -> None:
await self._hass.async_add_executor_job(new_inverter.init_device)
self.inverters.append(new_inverter)

except Exception as e:
except ModbusReadError as e:
self.disconnect()
raise HubInitFailed(f"{e}")

except DeviceInvalid as e:
"""Inverters are required"""
_LOGGER.error(f"Inverter device ID {inverter_unit_id}: {e}")
raise HubInitFailed(f"Inverter device ID {inverter_unit_id} not found.")
Expand All @@ -138,7 +142,12 @@ async def _async_init_solaredge(self) -> None:

self.meters.append(new_meter_1)
_LOGGER.debug(f"Found meter 1 on inverter ID {inverter_unit_id}")
except Exception:

except ModbusReadError as e:
self.disconnect()
raise HubInitFailed(f"{e}")

except DeviceInvalid:
pass

try:
Expand All @@ -159,7 +168,12 @@ async def _async_init_solaredge(self) -> None:

self.meters.append(new_meter_2)
_LOGGER.debug(f"Found meter 2 on inverter ID {inverter_unit_id}")
except Exception:

except ModbusReadError as e:
self.disconnect()
raise HubInitFailed(f"{e}")

except DeviceInvalid:
pass

try:
Expand All @@ -180,7 +194,12 @@ async def _async_init_solaredge(self) -> None:

self.meters.append(new_meter_3)
_LOGGER.debug(f"Found meter 3 on inverter ID {inverter_unit_id}")
except Exception:

except ModbusReadError as e:
self.disconnect()
raise HubInitFailed(f"{e}")

except DeviceInvalid:
pass

if self._detect_batteries:
Expand All @@ -202,7 +221,12 @@ async def _async_init_solaredge(self) -> None:

self.batteries.append(new_battery_1)
_LOGGER.debug(f"Found battery 1 inverter {inverter_unit_id}")
except Exception:

except ModbusReadError as e:
self.disconnect()
raise HubInitFailed(f"{e}")

except DeviceInvalid:
pass

try:
Expand All @@ -223,7 +247,12 @@ async def _async_init_solaredge(self) -> None:

self.batteries.append(new_battery_2)
_LOGGER.debug(f"Found battery 2 inverter {inverter_unit_id}")
except Exception:

except ModbusReadError as e:
self.disconnect()
raise HubInitFailed(f"{e}")

except DeviceInvalid:
pass

try:
Expand All @@ -246,7 +275,12 @@ async def async_refresh_modbus_data(self, _now: Optional[int] = None) -> bool:
await self.connect()

if not self.initalized:
await self._async_init_solaredge()
try:
await self._async_init_solaredge()

except ConnectionException as e:
self.disconnect()
raise HubInitFailed(f"Setup failed: {e}")

if not self.is_socket_open():
self.online = False
Expand All @@ -264,9 +298,21 @@ async def async_refresh_modbus_data(self, _now: Optional[int] = None) -> bool:
for battery in self.batteries:
await self._hass.async_add_executor_job(battery.read_modbus_data)

except Exception as e:
except ModbusReadError as e:
self.online = False
self.disconnect()
raise DataUpdateFailed(f"Update failed: {e}")

except DeviceInvalid as e:
self.online = False
if not self.keep_modbus_open:
self.disconnect()
raise DataUpdateFailed(f"Invalid device: {e}")

except ConnectionException as e:
self.online = False
raise DataUpdateFailed(f"Failed to update devices: {e}")
self.disconnect()
raise DataUpdateFailed(f"Connection failed: {e}")

if not self.keep_modbus_open:
self.disconnect()
Expand All @@ -282,22 +328,30 @@ def name(self):
def hub_id(self) -> str:
return self._id

def disconnect(self):
"""Disconnect client."""
def disconnect(self) -> None:
"""Disconnect modbus client."""
with self._lock:
self._client.close()
if self._client is not None:
self._client.close()
self._client = None

async def connect(self):
"""Connect client."""
async def connect(self) -> None:
"""Connect modbus client."""
with self._lock:
if self._client is None:
self._client = ModbusTcpClient(host=self._host, port=self._port)
await self._hass.async_add_executor_job(self._client.connect)

def is_socket_open(self):
"""Check client."""
def is_socket_open(self) -> bool:
"""Check modbus client connection status."""
with self._lock:
return self._client.is_socket_open()
if self._client is None:
return False
else:
return self._client.is_socket_open()

async def shutdown(self) -> None:
"""Shut down the hub."""
self.online = False
self.disconnect()
self._client = None
Expand Down Expand Up @@ -548,7 +602,12 @@ def init_device(self) -> None:
f"meter {self.meter_id}: {meter_info}"
),
)
raise ModbusReadError(meter_info)

if meter_info.exception_code == ModbusExceptions.IllegalAddress:
raise DeviceInvalid(meter_info)

else:
raise ModbusReadError(meter_info)

decoder = BinaryPayloadDecoder.fromRegisters(
meter_info.registers, byteorder=Endian.Big
Expand Down Expand Up @@ -815,7 +874,12 @@ def init_device(self) -> None:
f"battery {self.battery_id}: {battery_info}"
),
)
raise ModbusReadError(battery_info)

if battery_info.exception_code == ModbusExceptions.IllegalAddress:
raise DeviceInvalid(battery_info)

else:
raise ModbusReadError(battery_info)

decoder = BinaryPayloadDecoder.fromRegisters(
battery_info.registers,
Expand Down
2 changes: 1 addition & 1 deletion custom_components/solaredge_modbus_multi/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
"codeowners": ["@WillCodeForCats"],
"config_flow": true,
"iot_class": "local_polling",
"version": "v2.1.2"
"version": "v2.1.3"
}