Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion pyaarlo/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -487,12 +487,23 @@ def _initial_refresh_done(self):
self._lock.notify_all()

def stop(self, logout=False):
"""Stop connection to Arlo and, optionally, logout."""
"""Stop connection to Arlo and, optionally, logout.

Always stops the background worker, media library, and event stream
thread. If logout=True, also sends a logout request to the Arlo API
to invalidate the session.

Must be called before creating a new PyArlo instance in the same
process, otherwise ghost threads from the old instance will interfere
with the new connection. See https://github.com/twrecked/pyaarlo/issues/71
"""
self._st.save()
self._bg.stop()
self._ml.stop()
if logout:
self._be.logout()
else:
self._be.stop()

@property
def entity_id(self):
Expand Down
35 changes: 28 additions & 7 deletions pyaarlo/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,8 @@ def _event_handle_response(self, response):

def _event_stop_loop(self):
self._stop_thread = True
with self._lock:
self._lock.notify_all()

def _event_main(self):
self.debug("re-logging in")
Expand All @@ -495,9 +497,11 @@ def _event_main(self):
dump.write("{}: {}\n".format(time_stamp, "event_thread start"))

# login again if not first iteration, this will also create a new session
while not self._logged_in:
while not self._logged_in and not self._stop_thread:
with self._lock:
self._lock.wait(5)
if self._stop_thread:
break
self.debug("re-logging in")
self._logged_in = self._login()

Expand Down Expand Up @@ -1159,14 +1163,31 @@ def _wait_for_transaction(self, tid, timeout):
def is_connected(self):
return self._logged_in

def logout(self):
self.debug("trying to logout")
def stop(self):
"""Stop the event stream thread and wait for it to exit.

Without this, dropping a PyArlo reference leaves ghost threads
that hold stale connections and attempt their own re-logins,
interfering with any new PyArlo instance in the same process.
See https://github.com/twrecked/pyaarlo/issues/71
"""
self.debug("stopping backend")
self._event_stop_loop()
if self._event_client is not None:
if self._use_mqtt:
self._event_client.disconnect()
else:
self._event_client.stop()
try:
if self._use_mqtt:
self._event_client.disconnect()
else:
self._event_client.stop()
except Exception:
pass
if self._event_thread is not None and self._event_thread.is_alive():
self._event_thread.join(timeout=10)

def logout(self):
"""Stop the event stream and log out of the Arlo API."""
self.debug("trying to logout")
self.stop()
self.put(LOGOUT_PATH)

def notify(self, base, body, timeout=None, wait_for=None):
Expand Down