# Path: config/hass/custom_components/aarlo/pyaarlo/constant.py
# DEFAULT_RESOURCES = {'modes', 'siren', 'doorbells', 'lights', 'cameras'}
# 
# LOGIN_PATH = '/hmsweb/login/v2'
# 
# LOGOUT_PATH = '/hmsweb/logout'
# 
# NOTIFY_PATH = '/hmsweb/users/devices/notify/'
# 
# SUBSCRIBE_PATH = '/hmsweb/client/subscribe?token='
# 
# TRANSID_PREFIX = 'web'
# 
# DEVICES_PATH = '/hmsweb/users/devices'
# 
# Path: config/hass/custom_components/aarlo/pyaarlo/sseclient.py
# class SSEClient(object):
#     def __init__(self, log, url, last_id=None, retry=3000, session=None, chunk_size=1024, reconnect_cb=None, **kwargs):
#         self.log = log
#         self.url = url
#         self.last_id = last_id
#         self.retry = retry
#         self.chunk_size = chunk_size
#         self.running = True
#         self.reconnect_cb = reconnect_cb
# 
#         # Optional support for passing in a requests.Session()
#         self.session = session
# 
#         # Any extra kwargs will be fed into the requests.get call later.
#         self.requests_kwargs = kwargs
# 
#         # The SSE spec requires making requests with Cache-Control: nocache
#         if 'headers' not in self.requests_kwargs:
#             self.requests_kwargs['headers'] = {}
#         self.requests_kwargs['headers']['Cache-Control'] = 'no-cache'
# 
#         # The 'Accept' header is not required, but explicit > implicit
#         self.requests_kwargs['headers']['Accept'] = 'text/event-stream'
# 
#         # Keep data here as it streams in
#         self.buf = u''
# 
#         self._connect()
# 
#     def stop(self):
#         self.running = False
# 
#     def _connect(self):
#         if self.last_id:
#             self.requests_kwargs['headers']['Last-Event-ID'] = self.last_id
# 
#         # Use session if set.  Otherwise fall back to requests module.
#         requester = self.session or requests
#         self.resp = requester.get(self.url, stream=True, **self.requests_kwargs)
#         self.resp_iterator = self.resp.iter_content(chunk_size=self.chunk_size)
# 
#         # TODO: Ensure we're handling redirects.  Might also stick the 'origin'
#         # attribute on Events like the Javascript spec requires.
#         self.resp.raise_for_status()
# 
#     def _event_complete(self):
#         return re.search(end_of_field, self.buf) is not None
# 
#     def __iter__(self):
#         return self
# 
#     def __next__(self):
#         decoder = codecs.getincrementaldecoder(
#             self.resp.encoding)(errors='replace')
#         while not self._event_complete():
#             try:
#                 next_chunk = next(self.resp_iterator)
#                 if not next_chunk:
#                     raise EOFError()
#                 self.buf += decoder.decode(next_chunk)
# 
#             except (StopIteration, requests.RequestException, EOFError, six.moves.http_client.IncompleteRead) as e:
#                 if not self.running:
#                     self.log.debug('stopping')
#                     return None
# 
#                 self.log.debug('sseclient-error={}'.format(type(e).__name__))
#                 time.sleep(self.retry / 1000.0)
#                 self._connect()
# 
#                 # signal up!
#                 if self.reconnect_cb:
#                     self.reconnect_cb()
# 
#                 # The SSE spec only supports resuming from a whole message, so
#                 # if we have half a message we should throw it out.
#                 head, sep, tail = self.buf.rpartition('\n')
#                 self.buf = head + sep
#                 continue
# 
#         # Split the complete event (up to the end_of_field) into event_string,
#         # and retain anything after the current complete event in self.buf
#         # for next time.
#         (event_string, self.buf) = re.split(end_of_field, self.buf, maxsplit=1)
#         msg = Event.parse(event_string)
# 
#         # If the server requests a specific retry delay, we need to honor it.
#         if msg.retry:
#             self.retry = msg.retry
# 
#         # last_id should only be set if included in the message.  It's not
#         # forgotten if a message omits it.
#         if msg.id:
#             self.last_id = msg.id
# 
#         return msg
# 
#     if six.PY2:
#         next = __next__
# 
# Path: config/hass/custom_components/aarlo/pyaarlo/util.py
# def time_to_arlotime(timestamp=None):
#     if timestamp is None:
#         timestamp = time.time()
#     return int(timestamp * 1000)


import json
import pprint
import re
import threading
import time
import uuid
import requests
import requests.adapters
from .constant import (DEFAULT_RESOURCES, LOGIN_PATH, LOGOUT_PATH,
                       NOTIFY_PATH, SUBSCRIBE_PATH, TRANSID_PREFIX, DEVICES_PATH)
from .sseclient import SSEClient
from .util import time_to_arlotime
                continue

            # is this from a notify? then signal to waiting entity, also
            # pass into dispatcher
            tid = response.get('transId')
            with self._lock:
                if tid and tid in self._requests:
                    self._requests[tid] = response
                    self._lock.notify_all()
                    continue

            self._ev_dispatcher(response)

    def _ev_thread(self):

        self._arlo.debug('starting event loop')
        while True:

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

            # get stream, restart after requested seconds of inactivity or forced close
            try:
                if self._arlo.cfg.stream_timeout == 0:
                    self._arlo.debug('starting stream with no timeout')
                    # self._ev_stream = SSEClient( self.get( SUBSCRIBE_PATH + self._token,stream=True,raw=True ) )

# Path: config/hass/custom_components/aarlo/pyaarlo/constant.py
# DEFAULT_RESOURCES = {'modes', 'siren', 'doorbells', 'lights', 'cameras'}
# 
# LOGIN_PATH = '/hmsweb/login/v2'
# 
# LOGOUT_PATH = '/hmsweb/logout'
# 
# NOTIFY_PATH = '/hmsweb/users/devices/notify/'
# 
# SUBSCRIBE_PATH = '/hmsweb/client/subscribe?token='
# 
# TRANSID_PREFIX = 'web'
# 
# DEVICES_PATH = '/hmsweb/users/devices'
# 
# Path: config/hass/custom_components/aarlo/pyaarlo/sseclient.py
# class SSEClient(object):
#     def __init__(self, log, url, last_id=None, retry=3000, session=None, chunk_size=1024, reconnect_cb=None, **kwargs):
#         self.log = log
#         self.url = url
#         self.last_id = last_id
#         self.retry = retry
#         self.chunk_size = chunk_size
#         self.running = True
#         self.reconnect_cb = reconnect_cb
# 
#         # Optional support for passing in a requests.Session()
#         self.session = session
# 
#         # Any extra kwargs will be fed into the requests.get call later.
#         self.requests_kwargs = kwargs
# 
#         # The SSE spec requires making requests with Cache-Control: nocache
#         if 'headers' not in self.requests_kwargs:
#             self.requests_kwargs['headers'] = {}
#         self.requests_kwargs['headers']['Cache-Control'] = 'no-cache'
# 
#         # The 'Accept' header is not required, but explicit > implicit
#         self.requests_kwargs['headers']['Accept'] = 'text/event-stream'
# 
#         # Keep data here as it streams in
#         self.buf = u''
# 
#         self._connect()
# 
#     def stop(self):
#         self.running = False
# 
#     def _connect(self):
#         if self.last_id:
#             self.requests_kwargs['headers']['Last-Event-ID'] = self.last_id
# 
#         # Use session if set.  Otherwise fall back to requests module.
#         requester = self.session or requests
#         self.resp = requester.get(self.url, stream=True, **self.requests_kwargs)
#         self.resp_iterator = self.resp.iter_content(chunk_size=self.chunk_size)
# 
#         # TODO: Ensure we're handling redirects.  Might also stick the 'origin'
#         # attribute on Events like the Javascript spec requires.
#         self.resp.raise_for_status()
# 
#     def _event_complete(self):
#         return re.search(end_of_field, self.buf) is not None
# 
#     def __iter__(self):
#         return self
# 
#     def __next__(self):
#         decoder = codecs.getincrementaldecoder(
#             self.resp.encoding)(errors='replace')
#         while not self._event_complete():
#             try:
#                 next_chunk = next(self.resp_iterator)
#                 if not next_chunk:
#                     raise EOFError()
#                 self.buf += decoder.decode(next_chunk)
# 
#             except (StopIteration, requests.RequestException, EOFError, six.moves.http_client.IncompleteRead) as e:
#                 if not self.running:
#                     self.log.debug('stopping')
#                     return None
# 
#                 self.log.debug('sseclient-error={}'.format(type(e).__name__))
#                 time.sleep(self.retry / 1000.0)
#                 self._connect()
# 
#                 # signal up!
#                 if self.reconnect_cb:
#                     self.reconnect_cb()
# 
#                 # The SSE spec only supports resuming from a whole message, so
#                 # if we have half a message we should throw it out.
#                 head, sep, tail = self.buf.rpartition('\n')
#                 self.buf = head + sep
#                 continue
# 
#         # Split the complete event (up to the end_of_field) into event_string,
#         # and retain anything after the current complete event in self.buf
#         # for next time.
#         (event_string, self.buf) = re.split(end_of_field, self.buf, maxsplit=1)
#         msg = Event.parse(event_string)
# 
#         # If the server requests a specific retry delay, we need to honor it.
#         if msg.retry:
#             self.retry = msg.retry
# 
#         # last_id should only be set if included in the message.  It's not
#         # forgotten if a message omits it.
#         if msg.id:
#             self.last_id = msg.id
# 
#         return msg
# 
#     if six.PY2:
#         next = __next__
# 
# Path: config/hass/custom_components/aarlo/pyaarlo/util.py
# def time_to_arlotime(timestamp=None):
#     if timestamp is None:
#         timestamp = time.time()
#     return int(timestamp * 1000)


# Path: config/hass/custom_components/aarlo/pyaarlo/backend.py
import json
import pprint
import re
import threading
import time
import uuid
import requests
import requests.adapters
from .constant import (DEFAULT_RESOURCES, LOGIN_PATH, LOGOUT_PATH,
                       NOTIFY_PATH, SUBSCRIBE_PATH, TRANSID_PREFIX, DEVICES_PATH)
from .sseclient import SSEClient
from .util import time_to_arlotime

                continue

            # is this from a notify? then signal to waiting entity, also
            # pass into dispatcher
            tid = response.get('transId')
            with self._lock:
                if tid and tid in self._requests:
                    self._requests[tid] = response
                    self._lock.notify_all()
                    continue

            self._ev_dispatcher(response)

    def _ev_thread(self):

        self._arlo.debug('starting event loop')
        while True:

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

            # get stream, restart after requested seconds of inactivity or forced close
            try:
                if self._arlo.cfg.stream_timeout == 0:
                    self._arlo.debug('starting stream with no timeout')
                    # self._ev_stream = SSEClient( self.get( SUBSCRIBE_PATH + self._token,stream=True,raw=True ) )

                    self._ev_stream = SSEClient(self._arlo, self._arlo.cfg.host + SUBSCRIBE_PATH + self._token,