Skip to content

Commit

Permalink
fix: reconnect handling
Browse files Browse the repository at this point in the history
The disconnect request had a race condition to trigger multiple
connection tasks, if the device was currently trying to connect.

This is a dirty fix only: disconnect request will be ignored if a
reconnection task is currently running.
Proper disconnect handling requires a state-machine, or at least a
rewrite with a single and cancellable connection task.
  • Loading branch information
zehnm committed Nov 15, 2023
1 parent 928055f commit 053b23e
Show file tree
Hide file tree
Showing 2 changed files with 12 additions and 6 deletions.
17 changes: 11 additions & 6 deletions intg-denonavr/avr.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
DEFAULT_TIMEOUT = 5
VOLUME_STEP = 0.5

BACKOFF_MAX = 30
BACKOFF_MAX: float = 30
MIN_RECONNECT_DELAY: float = 0.5
BACKOFF_FACTOR: float = 1.5

Expand Down Expand Up @@ -387,19 +387,18 @@ async def connect(self):
_LOG.debug("Connection task already running for %s", self.id)
return

if self._use_telnet and self._receiver.telnet_connected:
if self._active:
_LOG.debug("[%s] Already connected", self.id)
return

self._connecting = True
try:
request_start = None
success = False
_LOG.debug("Starting connection task for %s", self.id)

while not success:
try:
if not self._connecting:
return
_LOG.info("Connecting AVR %s on %s", self.id, self._receiver.host)
self.events.emit(Events.CONNECTING, self.id)
request_start = time.time()
Expand Down Expand Up @@ -444,10 +443,11 @@ async def _handle_connection_failure(self, connect_duration: float, ex):
if backoff <= 0:
backoff = 0.1
_LOG.error(
"Cannot connect to '%s' on %s, trying again in %.1fs. %s",
"Cannot connect to '%s' on %s, trying again in %.1fs (connect: %.1fs). %s",
self.id if self.id else self._name,
self._receiver.host,
backoff,
connect_duration,
ex,
)

Expand Down Expand Up @@ -475,7 +475,12 @@ def _backoff(self) -> float:

async def disconnect(self):
"""Disconnect from AVR."""
self._connecting = False
_LOG.debug("Disconnect %s", self.id)
# Note: disconnecting during a connection task is currently not supported!
# Simply setting self._connecting = False doesn't work, and will start even more connection tasks after wakeup!
# This requires a state machine, or at least a separate connection task which can be cancelled.
if self._connecting:
return
self._active = False

try:
Expand Down
1 change: 1 addition & 0 deletions intg-denonavr/driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ async def receiver_status_poller(interval: float = 10.0) -> None:
async def on_r2_connect_cmd() -> None:
"""Connect all configured receivers when the Remote Two sends the connect command."""
# TODO check if we were in standby and ignore the call? We'll also get an EXIT_STANDBY
_LOG.debug("R2 connect command: connecting device(s)")
for receiver in _configured_avrs.values():
# start background task
_LOOP.create_task(receiver.connect())
Expand Down

0 comments on commit 053b23e

Please sign in to comment.