Skip to content
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

Add configurable number of connection attempts #46

Closed
wants to merge 8 commits into from
Closed

Add configurable number of connection attempts #46

wants to merge 8 commits into from

Conversation

gmoelter
Copy link

@gmoelter gmoelter commented Jan 9, 2022

Fixes #34

* default value=2 (->1 retry)
* add  variable conn_error
* implement each conn.make_request in a try-except structure and set conn_error true if an exception occurs
@gmoelter gmoelter changed the title Fix issue #34 and add configurable number of connection attemps Fix issue #34 and add configurable number of connection attempts Jan 9, 2022
Comment on lines +214 to +216
try:
self._conn.make_request(PROP_WRITE_HANDLE, value)
except Exception as ex:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't currently have a test device at hand to check this, but wouldn't it make more sense to perform the error&retry checking inside make_request or leave the whole re-try machinery for upstreams to handle?

Copy link
Author

@gmoelter gmoelter Jan 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inside the make_request function (connection.py) possible errors are fetched. But the calling instance (eg. update function) must recognize and handle the error. That's the reason for the stacktrace.

You don't need a real test device, try it with a random mac like 11:22:33:44:55:66.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I found a test device and did some quick testing.

Now, I think it's better to let the exceptions bubble up instead of having a device global error flag (e.g., if you query multiple different things, some of them may succeed and you can still display that information to the user), and handle the exception at user's end.

In the cli, instead of checking for the the error flag, it would be better to do something like this for the @cli.group to capture all exceptions: https://stackoverflow.com/questions/44344940/python-click-subcommand-unified-error-handling/44347763#44347763

@gmoelter
Copy link
Author

gmoelter commented Jan 15, 2022

After the last commits eq3cli.py also works perfect!

eq3cli.py --mac 00:00:00:00:00:00 --connectionattempts 3 state
INFO:eq3bt.connection:00:00:00:00:00:00: Connection attempt #1(3) failed
INFO:eq3bt.connection:00:00:00:00:00:00: Connection attempt #2(3) failed
INFO:eq3bt.connection:00:00:00:00:00:00: Connection attempt #3(3) failed
INFO:eq3bt.connection:Got exception from bluepy while making a request: Failed to connect to peripheral 00:00:00:00:00:00, addr type: public
WARNING:eq3bt.eq3btsmart:Failed to connect to peripheral 00:00:00:00:00:00, addr type: public
Connection error

Finally README.md has to be updated.

eq3bt/eq3btsmart.py Outdated Show resolved Hide resolved
eq3bt/connection.py Outdated Show resolved Hide resolved
eq3bt/connection.py Outdated Show resolved Hide resolved
Comment on lines +214 to +216
try:
self._conn.make_request(PROP_WRITE_HANDLE, value)
except Exception as ex:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I found a test device and did some quick testing.

Now, I think it's better to let the exceptions bubble up instead of having a device global error flag (e.g., if you query multiple different things, some of them may succeed and you can still display that information to the user), and handle the exception at user's end.

In the cli, instead of checking for the the error flag, it would be better to do something like this for the @cli.group to capture all exceptions: https://stackoverflow.com/questions/44344940/python-click-subcommand-unified-error-handling/44347763#44347763

Comment on lines +48 to +54
if not dev.connection_error:
click.echo("Current target temp: %s" % dev.target_temperature)
if target:
click.echo("Setting target temp: %s" % target)
dev.target_temperature = target
else:
click.echo("Connection error")
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if not dev.connection_error:
click.echo("Current target temp: %s" % dev.target_temperature)
if target:
click.echo("Setting target temp: %s" % target)
dev.target_temperature = target
else:
click.echo("Connection error")
if dev.connection_error:
click.echo("Connection error")
return
click.echo("Current target temp: %s" % dev.target_temperature)
if target:
click.echo("Setting target temp: %s" % target)
dev.target_temperature = target

It is better to return early for error cases, but see my commentary above regarding to how to handle errors in a nicer way with no need for adjusting each and every command separately.

eq3bt/eq3cli.py Outdated
@@ -22,16 +22,17 @@ def validate_mac(ctx, param, mac):
@click.group(invoke_without_command=True)
@click.option('--mac', envvar="EQ3_MAC", required=True, callback=validate_mac)
@click.option('--interface', default=None)
@click.option('--connectionattempts', default=2)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@click.option('--connectionattempts', default=2)
@click.option('--retry', default=2)

Or --retries would be shorter and still understandable name for this.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's okay for me. But 'connectionattempts' is not the same as 'retries'. The change must also be reflected in eq3btsmart.py and connection.py. See commit 603b391

@rytilahti rytilahti changed the title Fix issue #34 and add configurable number of connection attempts Add configurable number of connection attempts Feb 18, 2022
@rytilahti
Copy link
Owner

I realized one more thing, maybe it would make sense to lower the timeout for the tries in this same PR. At the moment it takes very long to retry, and most of the times if the device has not responded in a couple of seconds it likely makes more sense to retry instead of waiting long for an answer.

@gmoelter
Copy link
Author

Maybe it would make sense to lower the timeout for the tries in this same PR.

Then we should make 'timeout' configurable. OK?

@gmoelter
Copy link
Author

The last few commits should address your previous comments as far as they pertain the issue this PR is supposed to solve. Hence, from my side, I believe it should be ready to be merged.

As for your other suggestions, such as the error handling overall or the question about time delay in case when a connection could not be established, I think these are certainly worth investigating. However, at the same time, they do not directly pertain the issue of this PR, which is why they should rather be the subject of a separate PR.

For instance, in order to properly solve the issue with the time delay, one has to ultimately extend the bluepy interface, since this is precisely where I believe the delay is coming from.

@rytilahti
Copy link
Owner

However, at the same time, they do not directly pertain the issue of this PR, which is why they should rather be the subject of a separate PR.

I agree that the timeout handling should not be a part of this PR and is something to be considered later on, but the error handling should definitely be cleaned to keep the code base clean and maintainable.

The retry logic should only involve changes inside of Thermostat if possible, especially as there will be another backend to support (#48) and maybe in the future someone contributes a backend using bleak to add support for mac and windows systems.

So, after a brief investigation, I think the best option would be using a retry library like tenacity and simply decorate all I/O doing calls inside Thermostat. I'm not sure how to pass the cli parameter to it offhand so that'll need some investigation and testing to figure out.

@gmoelter gmoelter closed this Mar 10, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

error in code when connection fails
2 participants