Source code for aiobosest.connection

# Copyright 2016 Wagner Sartori Junior
#
# This file is part of aiobosest.
#
# aiobosest is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License.
#
# aiobosest is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with aiobosest.  If not, see <http://www.gnu.org/licenses/>.

from .log import logger
from .errors import (
    RESTConnectionError,
    WSConnectionError,
    RequestError,
    CallError,
)

import asyncio
import logging

import aiohttp
from lxml import etree

__all__ = [
    'Connection',
]

HTTP_REST_TIMEOUT = 5


[docs]class Connection: """Connection class to connect on the system. Also provides REST methods to get or post. Args: address: Bose SoundTouch IP address loop: asyncio loop if you want to provide one Attributes: websocket: websocket connection""" def __init__(self, address, loop=None): if loop is None: loop = asyncio.get_event_loop() self._address = address self._loop = loop self._session = aiohttp.ClientSession() self.websocket = None
[docs] async def connect_websocket(self): """Connect to the websocket.""" self.websocket = await self._session.ws_connect( 'http://{address}:8080'.format(address=self._address), protocols=['gabbo'])
[docs] async def shutdown(self): """Needs to be called before application quit. This is called by main the class""" if self.websocket: await self.websocket.close() await self._session.close()
async def _status_xml_parse(self, data): """Parse the XML status message. Args: data: string data to be parsed Returns: True if status, False if error""" xmltree = etree.XML(data) errors = xmltree.xpath('/errors') if errors: logging.error('XML Error: {errors}'.format(errors=errors)) return False status = xmltree.xpath('/status/text()') if status: logging.debug('XML Status: {status}'.format(status=status)) return True
[docs] async def get(self, uri): """GET data using the REST API Args: uri: URI to get the data Returns: XML data string Raises: CallError: malformed requests RequestError: request didn't receive HTTP 200 RestConnectionError: other errors""" logging.debug('>>> REST URI: http://{address}:8090{uri}'.format( address=self._address, uri=uri)) try: with aiohttp.Timeout(HTTP_REST_TIMEOUT, loop=self._loop): async with self._session.get('http://{address}:8090{uri}'.format( address=self._address, uri=uri)) as resp: data = await resp.read() logging.debug('<<< RECEIVED FROM URI http://{address}:8090{uri}: {message}' .format(address=self._address, uri=uri, message=data)) status = await self._status_xml_parse(data) if status: return data else: raise CallError except CallError: raise except RequestError: raise except Exception as e: logging.error('Error connecting to http://{address}:8090: {error!r}'.format( address=self._address, error=e)) raise RESTConnectionError(e)
[docs] async def post(self, uri, message): """POST data using the REST API Args: uri: URI to get the data message: XML message to POST Returns: XML data string Raises: CallError: malformed requests RequestError: request didn't receive HTTP 200 RestConnectionError: other errors""" logging.debug('>>> POST {message} ON URI http://{address}:8090{uri}'.format( address=self._address, uri=uri, message=message)) try: with aiohttp.Timeout(HTTP_REST_TIMEOUT, loop=self._loop): async with self._session.post('http://{address}:8090{uri}'.format( address=self._address, uri=uri, data=message), data=message) as resp: if resp.status != 200: raise RequestError data = await resp.read() logging.debug('<<< RECEIVED FROM URI http://{address}:8090{uri}: {message}' .format(address=self._address, uri=uri, message=data)) status = await self._status_xml_parse(data) if status: return data else: raise CallError except CallError: raise except RequestError: raise except Exception as e: logging.error('Error connecting to http://{address}:8090: {error!r}'.format( address=self._address, error=e)) raise RESTConnectionError(e)