Fixing reconnect issues after outage

This commit is contained in:
Constantin Pascal
2025-12-30 09:36:58 +02:00
parent 1d0cc328c0
commit a7b68fada7
+49 -11
View File
@@ -221,10 +221,17 @@ class ModbusRTUMonitorHub:
async def _reconnect(self) -> None:
"""Reconnect to Modbus gateway after connection loss."""
# Close existing connection
# Close existing connection and null out references
if self._writer:
self._writer.close()
await self._writer.wait_closed()
try:
self._writer.close()
await self._writer.wait_closed()
except Exception as ex:
_LOGGER.debug("Error closing writer during reconnect: %s", ex)
# Always null out reader/writer to ensure clean state
self._reader = None
self._writer = None
# Wait before reconnecting (30 seconds to prevent DDOS during server outages)
await asyncio.sleep(30)
@@ -232,10 +239,15 @@ class ModbusRTUMonitorHub:
# Attempt reconnection
try:
await self._connect()
_LOGGER.info("Reconnected to Modbus gateway")
_LOGGER.info("Reconnected to Modbus gateway at %s:%s", self.host, self.port)
except Exception as ex:
_LOGGER.error("Reconnection failed: %s", ex)
# Will retry on next loop iteration
_LOGGER.error(
"Reconnection failed for %s:%s - %s (will retry in 30 seconds)",
self.host,
self.port,
ex,
)
# Reader/writer are already None, loop will detect this and retry
async def _monitor_loop(self) -> None:
"""Main monitoring loop for passive frame reception."""
@@ -243,13 +255,24 @@ class ModbusRTUMonitorHub:
while True:
try:
# Check if reader is valid before attempting to read
if self._reader is None:
# No active connection, attempt reconnect
_LOGGER.info("No active connection to %s:%s, attempting reconnect", self.host, self.port)
await self._reconnect()
# Clear buffer for fresh start after reconnection
buffer.clear()
continue
# Read with timeout to allow graceful shutdown
data = await asyncio.wait_for(self._reader.read(1024), timeout=1.0)
if not data:
# Connection closed
_LOGGER.warning("Connection closed, attempting reconnect")
_LOGGER.warning("Connection closed to %s:%s, attempting reconnect", self.host, self.port)
await self._reconnect()
# Clear buffer for fresh start after reconnection
buffer.clear()
continue
buffer.extend(data)
@@ -290,8 +313,23 @@ class ModbusRTUMonitorHub:
continue
except asyncio.CancelledError:
break
except (AttributeError, OSError, ConnectionError) as ex:
# Connection-related errors - trigger reconnection
_LOGGER.warning(
"Connection error in monitor loop for %s:%s - %s (will reconnect)",
self.host,
self.port,
ex,
)
# Null out connection and trigger reconnect
self._reader = None
self._writer = None
await self._reconnect()
buffer.clear()
continue
except Exception as ex:
_LOGGER.error("Error in monitor loop: %s", ex)
# Other unexpected errors
_LOGGER.error("Unexpected error in monitor loop for %s:%s - %s", self.host, self.port, ex)
await asyncio.sleep(5)
async def _handle_frame(self, frame: ModbusRTUFrame) -> None:
@@ -632,10 +670,10 @@ class ModbusRTUMonitorHub:
) -> None:
"""Write temperature setpoint to slave using RTU protocol over existing connection."""
async with self._write_lock:
# Ensure writer is available
if not self._writer:
# Ensure writer is available and not closed
if not self._writer or self._writer.is_closing():
raise HomeAssistantError(
"No active connection to Modbus gateway - cannot write setpoint"
f"No active connection to Modbus gateway at {self.host}:{self.port} - cannot write setpoint"
)
# Scale temperature value (multiply by 10)