Skip to content

Commit

Permalink
Add command retries.
Browse files Browse the repository at this point in the history
  • Loading branch information
danielsmyers committed Jun 26, 2024
1 parent 3f3025a commit 9e38e57
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 9 deletions.
2 changes: 1 addition & 1 deletion client/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "evolutionhttp"
version = "0.0.13"
version = "0.0.14"
authors = [
{ name = "Daniel Myers", email = "danielsmyers@gmail.com" },
]
Expand Down
18 changes: 12 additions & 6 deletions client/src/evolutionhttp/local_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,13 +192,19 @@ async def _maybe_process_commands(self) -> None:
self._is_cmd_active = True

# Send the command and wait for the response
await self._device.write(cmd)
response = None
try:
async with asyncio.timeout(self._timeout_sec):
response = await self._device.read_next()
except TimeoutError:
_LOGGER.error("Timeout waiting for response to %s" % cmd)
cmd_verb = cmd.split('!')[0] if _is_write(cmd) else cmd.split('?')[0]
for attempt in [0, 1, 2]:
await self._device.write(cmd)
response = None
try:
async with asyncio.timeout(self._timeout_sec):
response = await self._device.read_next()
if response.startswith(cmd_verb) and not "NAK" in response:
break
_LOGGER.error("Bad response to command %s: %s", cmd, response)
except TimeoutError:
_LOGGER.error("Timeout waiting for response to %s" % cmd)
self._is_cmd_active = False
fut.set_result(response)

Expand Down
31 changes: 29 additions & 2 deletions client/tests/local_client_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,38 @@ async def test_file_io(self):
)

client: BryantEvolutionLocalClient = None
with mock.patch(
with patch(
"aiofiles.threadpool.sync_open", return_value=mock_file_stream
) as mock_open:
client = await BryantEvolutionLocalClient.get_client(
1, 1, "unused_filename"
1, 1, "test_file_io"
)

assert await client.set_heating_setpoint(97)
mock_file_stream.write.assert_called_with(b"S1Z1HTSP!97\n")
assert await client.read_heating_setpoint() == 97
mock_file_stream.write.assert_called_with(b"S1Z1HTSP?\n")

async def test_retries(self):
"""Test that the device retries NAK and corrupted errors."""
read_file_chunks = [
b"S1Z1HTSP:NAK\n",
b"bogus\n",
b"S1Z1HTSP:ACK\n",
b"S1Z1HTSP:97\xf8F\n",
]
file_chunks_iter = iter(read_file_chunks)

mock_file_stream = mock.MagicMock(
readline=lambda *args, **kwargs: next(file_chunks_iter),
)

client: BryantEvolutionLocalClient = None
with patch(
"aiofiles.threadpool.sync_open", return_value=mock_file_stream
) as mock_open:
client = await BryantEvolutionLocalClient.get_client(
1, 1, "test_retries"
)

assert await client.set_heating_setpoint(97)
Expand Down

0 comments on commit 9e38e57

Please sign in to comment.