Skip to content

Commit 059580f

Browse files
Merge pull request #81 from WillCodeForCats/modbus-errors
Modbus error exceptions
2 parents e6daed4 + e87e83e commit 059580f

File tree

2 files changed

+87
-23
lines changed

2 files changed

+87
-23
lines changed

custom_components/solaredge_modbus_multi/hub.py

Lines changed: 86 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
from pymodbus.client.sync import ModbusTcpClient
99
from pymodbus.compat import iteritems
1010
from pymodbus.constants import Endian
11+
from pymodbus.exceptions import ConnectionException
1112
from pymodbus.payload import BinaryPayloadDecoder
13+
from pymodbus.pdu import ModbusExceptions
1214

1315
from .const import DOMAIN, SUNSPEC_NOT_IMPL_UINT16
1416
from .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,

custom_components/solaredge_modbus_multi/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@
88
"codeowners": ["@WillCodeForCats"],
99
"config_flow": true,
1010
"iot_class": "local_polling",
11-
"version": "v2.1.2"
11+
"version": "v2.1.3"
1212
}

0 commit comments

Comments
 (0)