Allow pymodbus to reconnect in running system (not startup) (#53020)
Allow pymodbus to reconnect (not during startup).pull/53191/head
parent
12b29e2895
commit
78ef02f4d9
|
@ -249,17 +249,22 @@ class ModbusHub:
|
||||||
for entry in self._pb_call.values():
|
for entry in self._pb_call.values():
|
||||||
entry[ENTRY_FUNC] = getattr(self._client, entry[ENTRY_NAME])
|
entry[ENTRY_FUNC] = getattr(self._client, entry[ENTRY_NAME])
|
||||||
|
|
||||||
|
await self.async_connect_task()
|
||||||
|
return True
|
||||||
|
|
||||||
|
async def async_connect_task(self):
|
||||||
|
"""Try to connect, and retry if needed."""
|
||||||
async with self._lock:
|
async with self._lock:
|
||||||
if not await self.hass.async_add_executor_job(self._pymodbus_connect):
|
if not await self.hass.async_add_executor_job(self._pymodbus_connect):
|
||||||
self._log_error("initial connect failed, no retry", error_state=False)
|
err = f"{self._config_name} connect failed, retry in pymodbus"
|
||||||
return False
|
self._log_error(err, error_state=False)
|
||||||
|
return
|
||||||
|
|
||||||
# Start counting down to allow modbus requests.
|
# Start counting down to allow modbus requests.
|
||||||
if self._config_delay:
|
if self._config_delay:
|
||||||
self._async_cancel_listener = async_call_later(
|
self._async_cancel_listener = async_call_later(
|
||||||
self.hass, self._config_delay, self.async_end_delay
|
self.hass, self._config_delay, self.async_end_delay
|
||||||
)
|
)
|
||||||
return True
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_end_delay(self, args):
|
def async_end_delay(self, args):
|
||||||
|
@ -313,8 +318,6 @@ class ModbusHub:
|
||||||
return None
|
return None
|
||||||
if not self._client:
|
if not self._client:
|
||||||
return None
|
return None
|
||||||
if not self._client.is_socket_open():
|
|
||||||
return None
|
|
||||||
async with self._lock:
|
async with self._lock:
|
||||||
result = await self.hass.async_add_executor_job(
|
result = await self.hass.async_add_executor_job(
|
||||||
self._pymodbus_call, unit, address, value, use_call
|
self._pymodbus_call, unit, address, value, use_call
|
||||||
|
|
|
@ -515,35 +515,6 @@ async def test_pymodbus_constructor_fail(hass, caplog):
|
||||||
assert mock_pb.called
|
assert mock_pb.called
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"do_connect,do_exception,do_text",
|
|
||||||
[
|
|
||||||
[False, None, "initial connect failed, no retry"],
|
|
||||||
[True, ModbusException("no connect"), "Modbus Error: no connect"],
|
|
||||||
],
|
|
||||||
)
|
|
||||||
async def test_pymodbus_connect_fail(
|
|
||||||
hass, do_connect, do_exception, do_text, caplog, mock_pymodbus
|
|
||||||
):
|
|
||||||
"""Run test for failing pymodbus connect."""
|
|
||||||
config = {
|
|
||||||
DOMAIN: [
|
|
||||||
{
|
|
||||||
CONF_TYPE: "tcp",
|
|
||||||
CONF_HOST: TEST_HOST,
|
|
||||||
CONF_PORT: 5501,
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
caplog.set_level(logging.ERROR)
|
|
||||||
mock_pymodbus.connect.return_value = do_connect
|
|
||||||
mock_pymodbus.connect.side_effect = do_exception
|
|
||||||
assert await async_setup_component(hass, DOMAIN, config) is False
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
assert caplog.messages[0].startswith(f"Pymodbus: {do_text}")
|
|
||||||
assert caplog.records[0].levelname == "ERROR"
|
|
||||||
|
|
||||||
|
|
||||||
async def test_pymodbus_close_fail(hass, caplog, mock_pymodbus):
|
async def test_pymodbus_close_fail(hass, caplog, mock_pymodbus):
|
||||||
"""Run test for failing pymodbus close."""
|
"""Run test for failing pymodbus close."""
|
||||||
config = {
|
config = {
|
||||||
|
@ -563,44 +534,6 @@ async def test_pymodbus_close_fail(hass, caplog, mock_pymodbus):
|
||||||
# Close() is called as part of teardown
|
# Close() is called as part of teardown
|
||||||
|
|
||||||
|
|
||||||
async def test_disconnect(hass, mock_pymodbus):
|
|
||||||
"""Run test for startup delay."""
|
|
||||||
|
|
||||||
# the purpose of this test is to test a device disconnect
|
|
||||||
# We "hijiack" a binary_sensor to make a proper blackbox test.
|
|
||||||
entity_id = f"{BINARY_SENSOR_DOMAIN}.{TEST_SENSOR_NAME}"
|
|
||||||
config = {
|
|
||||||
DOMAIN: [
|
|
||||||
{
|
|
||||||
CONF_TYPE: "tcp",
|
|
||||||
CONF_HOST: TEST_HOST,
|
|
||||||
CONF_PORT: 5501,
|
|
||||||
CONF_NAME: TEST_MODBUS_NAME,
|
|
||||||
CONF_BINARY_SENSORS: [
|
|
||||||
{
|
|
||||||
CONF_INPUT_TYPE: CALL_TYPE_COIL,
|
|
||||||
CONF_NAME: f"{TEST_SENSOR_NAME}",
|
|
||||||
CONF_ADDRESS: 52,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
mock_pymodbus.read_coils.return_value = ReadResult([0x01])
|
|
||||||
mock_pymodbus.is_socket_open.return_value = False
|
|
||||||
now = dt_util.utcnow()
|
|
||||||
with mock.patch("homeassistant.helpers.event.dt_util.utcnow", return_value=now):
|
|
||||||
assert await async_setup_component(hass, DOMAIN, config) is True
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
# pass first scan_interval
|
|
||||||
now = now + timedelta(seconds=20)
|
|
||||||
with mock.patch("homeassistant.helpers.event.dt_util.utcnow", return_value=now):
|
|
||||||
async_fire_time_changed(hass, now)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
assert hass.states.get(entity_id).state == STATE_UNAVAILABLE
|
|
||||||
|
|
||||||
|
|
||||||
async def test_delay(hass, mock_pymodbus):
|
async def test_delay(hass, mock_pymodbus):
|
||||||
"""Run test for startup delay."""
|
"""Run test for startup delay."""
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue