Esempio n. 1
0
    async def _update_activity_callback(self, message: dict = None) -> None:
        """Update current activity when changed."""
        _LOGGER.debug("%s: New activity was started", self.name)

        new_activity = None
        message_data = message.get('data')
        if message_data is not None:
            new_activity = int(message_data.get('activityId'))

        if new_activity is None:
            await self._get_current_activity()
            return

        self._current_activity_id = new_activity
        _LOGGER.debug("%s: New activity: %s(%s)", self.name,
                      self.get_activity_name(self._current_activity_id),
                      self._current_activity_id)

        # If we were provided a callback handler then call it now.
        if self._callbacks.new_activity:
            call_callback(callback_handler=self._callbacks.new_activity,
                          result=(self._current_activity_id,
                                  self.get_activity_name(
                                      self._current_activity_id)),
                          callback_uuid=self._ip_address,
                          callback_name='new_activity_callback')
Esempio n. 2
0
    async def refresh_info_from_hub(self) -> None:
        _LOGGER.debug("%s: Retrieving HUB information", self.name)

        async with self._sync_lck:
            try:
                # Retrieve configuration and HUB version config.
                with timeout(DEFAULT_TIMEOUT):
                    await self._get_config()
            except asyncio.TimeoutError:
                _LOGGER.error("%s: Timeout trying to retrieve configuraton.",
                              self.name)
                raise aioexc.TimeOut
            try:
                # Retrieve current activity, done only once config received.
                with timeout(DEFAULT_TIMEOUT):
                    await self._get_current_activity()
            except asyncio.TimeoutError:
                _LOGGER.error(
                    "%s: Timeout trying to retrieve current "
                    "activity.", self.name)
                return

        # If we were provided a callback handler then call it now.
        if self._callbacks.config_updated:
            call_callback(callback_handler=self._callbacks.config_updated,
                          result=self._hub_config.config,
                          callback_uuid=self._ip_address,
                          callback_name='config_updated_callback')
Esempio n. 3
0
    async def connect(self) -> bool:
        """

        :return: True if connection was successful, False if it was not.
        :rtype: bool
        :raises: :class:`~aioharmony.exceptions.TimeOut`
        """

        if self._hub_connection is None:
            if not await self._websocket_or_xmpp():
                return False

        try:
            with timeout(DEFAULT_TIMEOUT):
                if not await self._hub_connection.hub_connect():
                    return False
        except asyncio.TimeoutError:
            raise aioexc.TimeOut

        # Initiate a sync. That will then result in our notification handler
        # to receive the response and set our current config version
        # accordingly.

        results = await asyncio.gather(
            self.send_to_hub(command='get_current_state'),
            self.refresh_info_from_hub(),
            return_exceptions=True)
        for idx, result in enumerate(results):
            if isinstance(result, Exception):
                if not isinstance(result, aioexc.TimeOut):
                    raise result

                if idx == 0:
                    _LOGGER.error("%s: Timeout trying to sync hub.", self.name)

                continue

            if idx == 0:
                resp_data = result.get('data')
                if resp_data is not None:
                    self._hub_config = self._hub_config._replace(
                        config_version=resp_data.get('configVersion'))
                    self._hub_config = self._hub_config._replace(
                        info=resp_data)
                    _LOGGER.debug("%s: HUB configuration version is: %s",
                                  self.name, self._hub_config.config_version)

        if self._hub_connection.callbacks.connect is None and \
                self._callbacks.connect is not None:
            # First time call, add the callback handler now and run it.
            _LOGGER.debug("%s, calling connect callback for first time",
                          self.name)
            call_callback(callback_handler=self._callbacks.connect,
                          result=self._ip_address,
                          callback_uuid=self._ip_address,
                          callback_name='connected')
            self._hub_connection.callbacks = ConnectorCallbackType(
                self._callbacks.connect, self._callbacks.disconnect)
        return True
Esempio n. 4
0
 def _connected_handler(self, _) -> None:
     """Call handler for connection."""
     self._connected = True
     call_callback(callback_handler=self._callbacks.connect,
                   result=self._ip_address,
                   callback_uuid=self._ip_address,
                   callback_name='connected'
                   )
Esempio n. 5
0
    async def _get_current_activity(self) -> bool:
        """Update current activity when changed."""
        _LOGGER.debug("%s: Retrieving current activity", self.name)

        # Send the command to the HUB

        try:
            with timeout(DEFAULT_TIMEOUT / 2):
                response = await self.send_to_hub(
                    command='get_current_activity',
                    send_timeout=DEFAULT_TIMEOUT / 4)
        except (asyncio.TimeoutError, aioexc.TimeOut):
            _LOGGER.debug(
                "%s: Timeout trying to retrieve current activity, retrying.",
                self.name)
            try:
                with timeout(DEFAULT_TIMEOUT / 2):
                    response = await self.send_to_hub(
                        command='get_current_activity',
                        send_timeout=DEFAULT_TIMEOUT / 4)
            except (asyncio.TimeoutError, aioexc.TimeOut):
                _LOGGER.error(
                    "%s: Timeout trying to retrieve current activity.",
                    self.name)
                response = None

        if not response:
            # There was an issue
            return False

        if response.get('code') != 200:
            _LOGGER.error(
                "%s: Incorrect status code %s received trying to get"
                "current activity for %s", self.name, response.get('code'),
                self._ip_address)
            return False

        self._current_activity_id = int(response['data']['result'])
        _LOGGER.debug("%s: Current activity: %s(%s)", self.name,
                      self.get_activity_name(self._current_activity_id),
                      self._current_activity_id)

        # If we were provided a callback handler then call it now.
        if self._callbacks.new_activity:
            _LOGGER.debug("%s: Calling callback handler for new_activity",
                          self.name)
            call_callback(callback_handler=self._callbacks.new_activity,
                          result=(self._current_activity_id,
                                  self.get_activity_name(
                                      activity_id=self._current_activity_id)),
                          callback_uuid=self._ip_address,
                          callback_name='new_activity_callback')
        return True
    async def _reconnect(self) -> None:
        """Perform reconnect to HUB if connection failed"""
        call_callback(callback_handler=self._callbacks.disconnect,
                      result=self._ip_address,
                      callback_uuid=self._ip_address,
                      callback_name='disconnected')
        if not self._connected:
            _LOGGER.debug(
                "%s: Connection was closed through "
                "disconnect, not reconnecting", self._ip_address)
            return

        if not self._auto_reconnect:
            _LOGGER.debug("%s: Connection closed, auto-reconnect disabled",
                          self._ip_address)
            return

        _LOGGER.debug("%s: Connection closed, reconnecting", self._ip_address)

        async with self._connect_disconnect_lock:
            # It is possible that the web socket hasn't been closed yet,
            # if this is the case then close it now.
            if self._websocket is not None and not self._websocket.closed:
                _LOGGER.debug("%s: Web Socket half-closed, closing first",
                              self._ip_address)
                with suppress(asyncio.TimeoutError), timeout(DEFAULT_TIMEOUT):
                    await self._websocket.close()

            if self._aiohttp_session is not None and not \
                    self._aiohttp_session.closed:
                _LOGGER.debug("%s: Closing sessions", self._ip_address)
                with suppress(asyncio.TimeoutError), timeout(DEFAULT_TIMEOUT):
                    await self._aiohttp_session.close()

        # Set web socket to none allowing for reconnect.
        self._websocket = None
        self._aiohttp_session = None

        is_reconnect = False
        self._connected = False
        sleep_time = 1
        await asyncio.sleep(sleep_time)
        while not await self.hub_connect(is_reconnect=is_reconnect):
            await asyncio.sleep(sleep_time)
            sleep_time = sleep_time * 2
            sleep_time = min(sleep_time, 30)
            is_reconnect = True
Esempio n. 7
0
    async def refresh_info_from_hub(self) -> None:
        _LOGGER.debug("%s: Retrieving HUB information", self.name)

        async with self._sync_lck:
            try:
                # Retrieve configuration and HUB version config.
                with timeout(DEFAULT_TIMEOUT * 4):
                    results = await asyncio.gather(self._get_config(),
                                                   self._retrieve_hub_info(),
                                                   return_exceptions=True)
            except asyncio.TimeoutError:
                _LOGGER.error("%s: Timeout trying to retrieve configuraton.",
                              self.name)
                raise aioexc.TimeOut

            for idx, result in enumerate(results):
                if isinstance(result, aioexc.TimeOut):
                    # Timeout exception, just put out error then.
                    if idx == 0:
                        result_name = 'config'
                    else:
                        result_name = 'hub info'

                    _LOGGER.error("%s: Timeout trying to retrieve %s.",
                                  self.name, result_name)
                    return
                elif isinstance(result, Exception):
                    # Other exception, raise it.
                    raise result
            try:
                # Retrieve current activity, done only once config received.
                with timeout(DEFAULT_TIMEOUT):
                    await self._get_current_activity()
            except asyncio.TimeoutError:
                _LOGGER.error(
                    "%s: Timeout trying to retrieve current "
                    "activity.", self.name)
                return

        # If we were provided a callback handler then call it now.
        if self._callbacks.config_updated:
            _LOGGER.debug("%s: Calling callback handler for config_updated",
                          self.name)
            call_callback(callback_handler=self._callbacks.config_updated,
                          result=self._hub_config.config,
                          callback_uuid=self._ip_address,
                          callback_name='config_updated_callback')
Esempio n. 8
0
    async def _disconnected_handler(self, _) -> None:
        """Perform reconnect to HUB if connection failed"""
        call_callback(callback_handler=self._callbacks.disconnect,
                      result=self._ip_address,
                      callback_uuid=self._ip_address,
                      callback_name='disconnected'
                      )
        if not self._connected:
            _LOGGER.debug("%s: Connection was closed through "
                          "disconnect, not reconnecting",
                          self._ip_address)
            return

        if not self._auto_reconnect:
            _LOGGER.debug("%s: Connection closed, auto-reconnect disabled",
                          self._ip_address)
            return

        _LOGGER.debug("%s: Connection closed, reconnecting",
                      self._ip_address)
        self._connected = False
        is_reconnect = False

        self._deregister_handlers()
        self._init_super()

        sleep_time = 1
        await asyncio.sleep(sleep_time)
        while True:
            try:
                if await self.hub_connect(is_reconnect=is_reconnect):
                    # Exit loop if connected.
                    break
            except IqTimeout:
                pass
            finally:
                # Wait and try again.
                await asyncio.sleep(sleep_time)
                sleep_time = sleep_time * 2
                sleep_time = min(sleep_time, 30)
            is_reconnect = True
Esempio n. 9
0
    async def _update_start_activity_callback(self,
                                              message: dict = None) -> None:
        """Update current activity when changed."""
        _LOGGER.debug("%s: New activity starting notification", self.name)

        message_data = message.get('data')
        if message_data is not None and message_data.get(
                'activityStatus') == 0:
            # The HUB sends a power off notification again that it is starting when it is done
            # thus intercepting this so we do not redo the callback.
            if int(message_data.get(
                    'activityId')) == -1 and self._current_activity_id == -1:
                return

            self._current_activity_id = -1
            _LOGGER.debug("%s: Powering off from activity: %s(%s)", self.name,
                          self.get_activity_name(self._current_activity_id),
                          self._current_activity_id)
            self._current_activity_id = -1
        else:
            if message_data is not None:
                self._current_activity_id = int(message_data.get('activityId'))
            else:
                self._current_activity_id = None

            _LOGGER.debug("%s: New activity starting: %s(%s)", self.name,
                          self.get_activity_name(self._current_activity_id),
                          self._current_activity_id)

        # If we were provided a callback handler then call it now.
        if self._callbacks.new_activity_starting:
            _LOGGER.debug(
                "%s: Calling callback handler for new_activity_starting",
                self.name)
            call_callback(
                callback_handler=self._callbacks.new_activity_starting,
                result=(self._current_activity_id,
                        self.get_activity_name(self._current_activity_id)),
                callback_uuid=self._ip_address,
                callback_name='new_activity_starting_callback')
Esempio n. 10
0
    async def hub_connect(self, is_reconnect: bool = False) -> bool:
        """Connect to Hub Web Socket"""
        # Acquire the lock.
        if self._connect_disconnect_lock.locked():
            _LOGGER.debug("%s: Waiting for other connect", self._ip_address)

        async with self._connect_disconnect_lock:
            # Return connected if we are already connected.
            if self._websocket is not None and not self._websocket.closed:
                return True

            _LOGGER.debug("%s: Starting connect.", self._ip_address)

            if is_reconnect:
                log_level = 10
            else:
                log_level = 40

            if await self._get_remote_id() is None:
                # No remote ID means no connect.
                _LOGGER.log(log_level, "%s: Unable to retrieve HUB id",
                            self._ip_address)
                return False

            _LOGGER.debug("%s: Connecting for hub %s", self._ip_address,
                          self._remote_id)
            try:
                self._websocket = await self._session.ws_connect(
                    'ws://{}:{}/?domain={}&hubId={}'.format(
                        self._ip_address, DEFAULT_HUB_PORT, self._domain,
                        self._remote_id),
                    heartbeat=10)
            except (aiohttp.ServerTimeoutError, aiohttp.ClientError,
                    aiohttp.WSServerHandshakeError) as exc:
                if isinstance(exc, aiohttp.ServerTimeoutError):
                    _LOGGER.log(log_level,
                                "%s: Connection timed out for hub %s",
                                self._ip_address, self._remote_id)
                elif isinstance(exc, aiohttp.ClientError):
                    _LOGGER.log(
                        log_level, "%s: Exception trying to establish web "
                        "socket connection for hub %s: %s", self._ip_address,
                        self._remote_id, exc)
                else:
                    _LOGGER.log(
                        log_level, "%s: Invalid status code %s received "
                        "trying to connect for hub %s: %s", self._ip_address,
                        exc.status, self._remote_id, exc)
                self._websocket = None
                return False

            _LOGGER.debug("%s: Connected to hub %s", self._ip_address,
                          self._remote_id)
            # Now put the listener on the loop.
            if not self._listener_task:
                self._listener_task = asyncio.ensure_future(
                    self._listener(self._websocket))

            # Set connected to True, disconnect sets this to False to
            # prevent automatic reconnect when disconnect is explicitly called
            self._connected = True
            call_callback(callback_handler=self._callbacks.connect,
                          result=self._ip_address,
                          callback_uuid=self._ip_address,
                          callback_name='connected')
            _LOGGER.debug("%s: Connected to hub", self._ip_address)
            return True
Esempio n. 11
0
    async def _callback_handler(self) -> None:
        """
        Listens on the queue for JSON messages and then processes them by
        calling any handler(s)
        """
        _LOGGER.debug("%s: Callback handler started", self._name)

        while True:
            # Put everything here in a try block, we do not want this
            # to stop running out due to an exception.
            try:
                # Wait for something to appear on the queue.
                message = await self._message_queue.get()
                _LOGGER.debug("%s: Message received: %s",
                              self._name,
                              message)

                # Go through list and call
                for handler in self._get_handlers(message=message):

                    # Make sure handler hasn't expired yet.
                    if self._unregister_expired_handlers(
                            single_handler=handler):
                        # Was expired and now removed, go on with next one.
                        continue

                    call_callback(
                        callback_handler=handler.handler.handler_obj,
                        result=message,
                        callback_uuid=handler.handler_uuid,
                        callback_name=handler.handler.handler_name
                    )

                    # Remove the handler from the list if it was only to be
                    # called once.
                    if handler.handler.once:
                        self.unregister_handler(handler.handler_uuid)

                # Go through all handlers and remove expired ones IF
                # currently
                # nothing in the queue.
                if self._message_queue.empty():
                    # Go through list and remove all expired ones.
                    _LOGGER.debug("%s: Checking for expired handlers",
                                  self._name
                                  )
                    self._unregister_expired_handlers()

            except asyncio.CancelledError:
                _LOGGER.debug("%s: Received STOP for callback handler",
                              self._name
                              )
                break

            # Need to catch everything here to prevent an issue in a
            # from causing the handler to exit.
            except Exception as exc:
                _LOGGER.exception("%s: Exception in callback handler: %s",
                                  self._name,
                                  exc)

        # Reset the queue.
        self._message_queue = asyncio.Queue()

        _LOGGER.debug("%s: Callback handler stopped.", self._name)