88from pymodbus .client .sync import ModbusTcpClient
99from pymodbus .compat import iteritems
1010from pymodbus .constants import Endian
11+ from pymodbus .exceptions import ConnectionException
1112from pymodbus .payload import BinaryPayloadDecoder
13+ from pymodbus .pdu import ModbusExceptions
1214
1315from .const import DOMAIN , SUNSPEC_NOT_IMPL_UINT16
1416from .helpers import parse_modbus_string
@@ -77,6 +79,7 @@ def __init__(
7779 self ._host = host
7880 self ._port = port
7981 self ._lock = threading .Lock ()
82+ self ._client = None
8083 self ._name = name
8184 self ._id = name .lower ()
8285 self .number_of_inverters = number_of_inverters
@@ -88,9 +91,6 @@ def __init__(
8891 self .inverters = []
8992 self .meters = []
9093 self .batteries = []
91-
92- self ._client = ModbusTcpClient (host = self ._host , port = self ._port )
93-
9494 self .initalized = False
9595 self .online = False
9696
@@ -114,7 +114,11 @@ async def _async_init_solaredge(self) -> None:
114114 await self ._hass .async_add_executor_job (new_inverter .init_device )
115115 self .inverters .append (new_inverter )
116116
117- except Exception as e :
117+ except ModbusReadError as e :
118+ self .disconnect ()
119+ raise HubInitFailed (f"{ e } " )
120+
121+ except DeviceInvalid as e :
118122 """Inverters are required"""
119123 _LOGGER .error (f"Inverter device ID { inverter_unit_id } : { e } " )
120124 raise HubInitFailed (f"Inverter device ID { inverter_unit_id } not found." )
@@ -138,7 +142,12 @@ async def _async_init_solaredge(self) -> None:
138142
139143 self .meters .append (new_meter_1 )
140144 _LOGGER .debug (f"Found meter 1 on inverter ID { inverter_unit_id } " )
141- except Exception :
145+
146+ except ModbusReadError as e :
147+ self .disconnect ()
148+ raise HubInitFailed (f"{ e } " )
149+
150+ except DeviceInvalid :
142151 pass
143152
144153 try :
@@ -159,7 +168,12 @@ async def _async_init_solaredge(self) -> None:
159168
160169 self .meters .append (new_meter_2 )
161170 _LOGGER .debug (f"Found meter 2 on inverter ID { inverter_unit_id } " )
162- except Exception :
171+
172+ except ModbusReadError as e :
173+ self .disconnect ()
174+ raise HubInitFailed (f"{ e } " )
175+
176+ except DeviceInvalid :
163177 pass
164178
165179 try :
@@ -180,7 +194,12 @@ async def _async_init_solaredge(self) -> None:
180194
181195 self .meters .append (new_meter_3 )
182196 _LOGGER .debug (f"Found meter 3 on inverter ID { inverter_unit_id } " )
183- except Exception :
197+
198+ except ModbusReadError as e :
199+ self .disconnect ()
200+ raise HubInitFailed (f"{ e } " )
201+
202+ except DeviceInvalid :
184203 pass
185204
186205 if self ._detect_batteries :
@@ -202,7 +221,12 @@ async def _async_init_solaredge(self) -> None:
202221
203222 self .batteries .append (new_battery_1 )
204223 _LOGGER .debug (f"Found battery 1 inverter { inverter_unit_id } " )
205- except Exception :
224+
225+ except ModbusReadError as e :
226+ self .disconnect ()
227+ raise HubInitFailed (f"{ e } " )
228+
229+ except DeviceInvalid :
206230 pass
207231
208232 try :
@@ -223,7 +247,12 @@ async def _async_init_solaredge(self) -> None:
223247
224248 self .batteries .append (new_battery_2 )
225249 _LOGGER .debug (f"Found battery 2 inverter { inverter_unit_id } " )
226- except Exception :
250+
251+ except ModbusReadError as e :
252+ self .disconnect ()
253+ raise HubInitFailed (f"{ e } " )
254+
255+ except DeviceInvalid :
227256 pass
228257
229258 try :
@@ -246,7 +275,12 @@ async def async_refresh_modbus_data(self, _now: Optional[int] = None) -> bool:
246275 await self .connect ()
247276
248277 if not self .initalized :
249- await self ._async_init_solaredge ()
278+ try :
279+ await self ._async_init_solaredge ()
280+
281+ except ConnectionException as e :
282+ self .disconnect ()
283+ raise HubInitFailed (f"Setup failed: { e } " )
250284
251285 if not self .is_socket_open ():
252286 self .online = False
@@ -264,9 +298,21 @@ async def async_refresh_modbus_data(self, _now: Optional[int] = None) -> bool:
264298 for battery in self .batteries :
265299 await self ._hass .async_add_executor_job (battery .read_modbus_data )
266300
267- except Exception as e :
301+ except ModbusReadError as e :
302+ self .online = False
303+ self .disconnect ()
304+ raise DataUpdateFailed (f"Update failed: { e } " )
305+
306+ except DeviceInvalid as e :
307+ self .online = False
308+ if not self .keep_modbus_open :
309+ self .disconnect ()
310+ raise DataUpdateFailed (f"Invalid device: { e } " )
311+
312+ except ConnectionException as e :
268313 self .online = False
269- raise DataUpdateFailed (f"Failed to update devices: { e } " )
314+ self .disconnect ()
315+ raise DataUpdateFailed (f"Connection failed: { e } " )
270316
271317 if not self .keep_modbus_open :
272318 self .disconnect ()
@@ -282,22 +328,30 @@ def name(self):
282328 def hub_id (self ) -> str :
283329 return self ._id
284330
285- def disconnect (self ):
286- """Disconnect client."""
331+ def disconnect (self ) -> None :
332+ """Disconnect modbus client."""
287333 with self ._lock :
288- self ._client .close ()
334+ if self ._client is not None :
335+ self ._client .close ()
336+ self ._client = None
289337
290- async def connect (self ):
291- """Connect client."""
338+ async def connect (self ) -> None :
339+ """Connect modbus client."""
292340 with self ._lock :
341+ if self ._client is None :
342+ self ._client = ModbusTcpClient (host = self ._host , port = self ._port )
293343 await self ._hass .async_add_executor_job (self ._client .connect )
294344
295- def is_socket_open (self ):
296- """Check client."""
345+ def is_socket_open (self ) -> bool :
346+ """Check modbus client connection status ."""
297347 with self ._lock :
298- return self ._client .is_socket_open ()
348+ if self ._client is None :
349+ return False
350+ else :
351+ return self ._client .is_socket_open ()
299352
300353 async def shutdown (self ) -> None :
354+ """Shut down the hub."""
301355 self .online = False
302356 self .disconnect ()
303357 self ._client = None
@@ -548,7 +602,12 @@ def init_device(self) -> None:
548602 f"meter { self .meter_id } : { meter_info } "
549603 ),
550604 )
551- raise ModbusReadError (meter_info )
605+
606+ if meter_info .exception_code == ModbusExceptions .IllegalAddress :
607+ raise DeviceInvalid (meter_info )
608+
609+ else :
610+ raise ModbusReadError (meter_info )
552611
553612 decoder = BinaryPayloadDecoder .fromRegisters (
554613 meter_info .registers , byteorder = Endian .Big
@@ -815,7 +874,12 @@ def init_device(self) -> None:
815874 f"battery { self .battery_id } : { battery_info } "
816875 ),
817876 )
818- raise ModbusReadError (battery_info )
877+
878+ if battery_info .exception_code == ModbusExceptions .IllegalAddress :
879+ raise DeviceInvalid (battery_info )
880+
881+ else :
882+ raise ModbusReadError (battery_info )
819883
820884 decoder = BinaryPayloadDecoder .fromRegisters (
821885 battery_info .registers ,
0 commit comments