New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Port to asyncio #18
Port to asyncio #18
Conversation
Wow, great work! I do not exactly know where to start understanding asyncio, but removing polling is always a good thing :) |
The API has changed somewhat: Need to call Several methods are now
Also note that It may be possible to rename all these to In addition, I've introduced My original attempt was to only introduce callbacks whenever the status is updated, but it was quite straight forward to remove the threading and low level socket usage with I've only used async/await in Javascript/TypeScript prior to this. I watched this as an introduction to What worries me most though, is that |
I have not played with asyncio before, but it looks just right for this application. I like the updates, and cleanup :) We need the working reconnect, and I think wrappers for those not using (or used to) asyncio is a good idea. Users must be allowed to register callbacks after calling start(). The main risk is that all the initial updates (G00) are done, and you do not get called until after the next update. I think this would be solved by always calling a callback when it is registered (I would think of this as an update to the current state). |
Fixed bug in discovery when complete serial number is provided. Moved creating tasks to start. Moved callbacks to connect_hub and socket_received.
@capelevy thank you very much for the review and comments! I have addressed all but the reconnect - still working on reproducing disconnects. I did not make sync wrappers for Executing the callback in To avoid the race condition, it is sufficient to read state between Example use in
|
Simplified get_response to read one response message at a time Implemented reconnect Fixed sync wrappers to run as soon as possible
Timeout on initial connect Respect discover=False in reconnect
This should fix echoromeo#14.
try: | ||
message = await self._reader.readuntil(b'\r') | ||
message = message[:-1] | ||
except ConnectionError as e: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am also able to provoke TimeoutError at this point:
ERROR:pynobo:Unhandled exception [Errno 60] Operation timed out
<...>
TimeoutError: [Errno 60] Operation timed out
could we check for that as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, we can reconnect on timeout. But I'm curious to how this happened. Do you have steps to reproduce?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I get this when disabling WIFI. After three to four DEBUG:pynobo:sending: ['HANDSHAKE']
I get the exception, triggered from self.get_response()
, line 513. This happens on macOS, not sure about other platforms...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reproduced (also on macOS) by disabling ethernet after connection is made. Can also be reproduces by disconnecting ethernet cable, so this is a quite likely error to happen.
The timeout probably happens because asyncio sets a 60 seconds timeout on the reader, which is quite acceptable, since we expect a handshake every 14 seconds (recommended) to 30 seconds (max).
It was quite tricky to reconnect though, as the hub was rediscovered before local IP address was reassigned on DHCP, so I needed some error handling in reconnect_hub
as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The warning below is when the first call to asyncio.open_connection
below failed with ENETUNREACH
.
The HELLO
message afterwards is after a successful call to asyncio.open_connection
.
2021-04-28 21:25:04,738 - pynobo - DEBUG - sending: ['HANDSHAKE']
2021-04-28 21:25:18,742 - pynobo - DEBUG - sending: ['HANDSHAKE']
2021-04-28 21:25:32,743 - pynobo - DEBUG - sending: ['HANDSHAKE']
2021-04-28 21:25:46,745 - pynobo - DEBUG - sending: ['HANDSHAKE']
2021-04-28 21:25:58,830 - pynobo - INFO - Reconnecting due to [Errno 60] Operation timed out
2021-04-28 21:25:58,830 - pynobo - INFO - reconnecting to hub
2021-04-28 21:26:06,425 - pynobo - INFO - broadcast received: __NOBOHUB__102000013 from <ip redacted>
2021-04-28 21:26:07,843 - pynobo - WARNING - Failed to connect to ip set(): [Errno 51] Network is unreachable
2021-04-28 21:26:08,845 - pynobo - DEBUG - sending: ['HELLO', '1.1', <serial redacted>, '20210428212608']
Good work, Øyvind! The reconnect seems to be working well. I also like the async wrappers. This is turning out to be a nice upgrade :) |
Set thread to be daemon Check for self._writer in start to support calling async_connect_hub before start Expanded readme
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good work @oyvindwe! Quick responses and well thought out changes.
I suspect this fixes Issues #9 and #14, @echoromeo do you have any further comments?
Yes. Note that the error code depends on OS, but the constants in |
For the record - from
In CPython, these constants are generated compile time based on the OS. See https://github.com/python/cpython/blob/63298930fb531ba2bb4f23bc3b915dbf1e17e9e1/Modules/errnomodule.c On Linux:
On macOS:
|
@echoromeo Is it OK to merge this PR? I am ready to submit a PR to make an official integration for Home Assistant, but that requires an updated version on Pypi. See https://github.com/oyvindwe/home-assistant/tree/nobo_hub |
Hi, sorry for the delay. I trust the approval of @capelevy so I will merge. |
No worries - I'll be busy as well. :) The HA PR may take a little more time than i thought, to get to 100% test coverage of the configuration flow. |
Initial port to use asyncio instead of threads. Also support callbacks to avoid polling for data.
I'm also preparing a PR for https://github.com/echoromeo/hanobo that works with this version. This makes changes immediately show up in Home Assistant.
Note that this version is not backwards compatible, but I'd like to get your thoughts on it.
Also, reconnect is not implemented, so I'm marking the PR as a draft.