From 8a9688ff8482c5ef654b1efde30f51784db7c86c Mon Sep 17 00:00:00 2001 From: richtier Date: Tue, 26 Nov 2019 08:38:31 +0000 Subject: [PATCH] Allow setting base_url when instantiating the client --- CHANGELOG.md | 5 +++++ README.md | 21 ++++++++++++++++++- alexa_client/alexa_client/client.py | 6 ++++-- alexa_client/alexa_client/connection.py | 10 +++++---- alexa_client/alexa_client/constants.py | 4 ++++ setup.py | 2 +- tests/alexa_client/test_client.py | 27 +++++++++++++++++++++++++ tests/alexa_client/test_connection.py | 16 ++++++++++----- 8 files changed, 78 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 14cb7d7..cd4355f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 1.5.0 +[Full Changelog](https://github.com/richtier/alexa-voice-service-client/pull/35) +### Implemented enhancements +- Allow setting base_url when instantiating the client + ## 1.4.1 [Full Changelog](https://github.com/richtier/alexa-voice-service-client/pull/34) ### Implemented enhancements diff --git a/README.md b/README.md index d3d1fd7..b88b568 100644 --- a/README.md +++ b/README.md @@ -112,7 +112,7 @@ Run the streaming microphone audio demo to use this feature: pip install alexa_client[demo] python -m alexa_client.demo.streaming_microphone \ --client-id="{enter-client-id-here}" \ - --client-secret="{enter-client-secret-here"} \ + --client-secret="{enter-client-secret-here}" \ --refresh-token="{enter-refresh-token-here}" ``` @@ -145,6 +145,25 @@ When PCM format is specified the audio should be 16bit Linear PCM (LPCM16), 16kH When OPUS forat is specified the audio should be 16bit Opus, 16kHz sample rate, 32k bit rate, and little endian. +### Base URL + +`base_url` can be set to improve latency. Choose a region closest to your location. + +``` +from alexa_client.alexa_client import constants + +client = AlexaClient( + client_id='my-client-id', + secret='my-secret', + refresh_token='my-refresh-token', + base_url=constants.BASE_URL_ASIA +) +``` + +The default base URL is Europe. The available constants are BASE_URL_EUROPE, BASE_URL_ASIA and BASE_URL_NORTH_AMERICA but you can pass any string if required. + +[Read more](https://developer.amazon.com/docs/alexa-voice-service/api-overview.html#endpoints) + ## Authentication To use AVS you must first have a [developer account](http://developer.amazon.com). Then register your product [here](https://developer.amazon.com/avs/home.html#/avs/products/new). Choose "Application" under "Is your product an app or a device"? diff --git a/alexa_client/alexa_client/client.py b/alexa_client/alexa_client/client.py index 204a8aa..24c7c8c 100644 --- a/alexa_client/alexa_client/client.py +++ b/alexa_client/alexa_client/client.py @@ -17,18 +17,20 @@ class AlexaClient: authentication_manager = None connection_manager = None device_manager = None + base_url = None - def __init__(self, client_id, secret, refresh_token): + def __init__(self, client_id, secret, refresh_token, base_url=None): self.authentication_manager = self.authentication_manager_class( client_id=client_id, secret=secret, refresh_token=refresh_token, ) self.device_manager = self.device_manager_class() self.connection_manager = self.connection_manager_class() self.ping_manager = self.ping_manager_class(60*4, self.ping) + self.base_url = base_url def connect(self): self.authentication_manager.prefetch_api_token() - self.connection_manager.create_connection() + self.connection_manager.create_connection(base_url=self.base_url) self.establish_downchannel_stream() self.synchronise_device_state() self.ping_manager.start() diff --git a/alexa_client/alexa_client/connection.py b/alexa_client/alexa_client/connection.py index 0496d0a..61cd291 100644 --- a/alexa_client/alexa_client/connection.py +++ b/alexa_client/alexa_client/connection.py @@ -5,16 +5,18 @@ from requests.exceptions import HTTPError from hyper import HTTP20Connection -from alexa_client.alexa_client import helpers +from alexa_client.alexa_client import constants, helpers class ConnectionManager: - host = 'avs-alexa-eu.amazon.com' connection = None + # TODO: in next breaking change release rename to `base_url` and move to + # client.connect default kwargs + host = constants.BASE_URL_EUROPE - def create_connection(self): + def create_connection(self, base_url=None): self.connection = HTTP20Connection( - host=self.host, secure=True, force_proto='h2', + host=base_url or self.host, secure=True, force_proto='h2' ) def establish_downchannel_stream(self, authentication_headers): diff --git a/alexa_client/alexa_client/constants.py b/alexa_client/alexa_client/constants.py index d3b9e68..83a1ef4 100644 --- a/alexa_client/alexa_client/constants.py +++ b/alexa_client/alexa_client/constants.py @@ -7,3 +7,7 @@ # format of captured audio PCM = 'AUDIO_L16_RATE_16000_CHANNELS_1' OPUS = 'OPUS' + +BASE_URL_ASIA = 'alexa.fe.gateway.devices.a2z.com' +BASE_URL_EUROPE = 'alexa.eu.gateway.devices.a2z.com' +BASE_URL_NORTH_AMERICA = 'alexa.na.gateway.devices.a2z.com' diff --git a/setup.py b/setup.py index 0de568a..c583784 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup( name='alexa_client', - version='1.4.1', + version='1.5.0', packages=find_packages(exclude=["tests.*", "tests"]), url='https://github.com/richtier/alexa-voice-service-client', license='MIT', diff --git a/tests/alexa_client/test_client.py b/tests/alexa_client/test_client.py index 678580e..7d280f8 100644 --- a/tests/alexa_client/test_client.py +++ b/tests/alexa_client/test_client.py @@ -34,6 +34,30 @@ def test_client_authentication_manager(client): ) +def test_base_url(client): + + class TestAlexaClient(AlexaClient): + authentication_manager_class = mock.Mock() + device_manager_class = mock.Mock() + connection_manager_class = mock.Mock() + ping_manager_class = mock.Mock() + + client = TestAlexaClient( + client_id='test_client_id', + secret='test_secret', + refresh_token='test_refresh_token', + base_url=constants.BASE_URL_NORTH_AMERICA + ) + client.ping_manager.update_ping_deadline = mock.MagicMock() + + client.connect() + + assert client.connection_manager.create_connection.call_count == 1 + assert client.connection_manager.create_connection.call_args == mock.call( + base_url=constants.BASE_URL_NORTH_AMERICA + ) + + def test_client_connect(client): client.connect() @@ -41,6 +65,9 @@ def test_client_connect(client): assert ( client.connection_manager.establish_downchannel_stream.call_count == 1 ) + assert client.connection_manager.create_connection.call_args == mock.call( + base_url=None + ) assert client.connection_manager.synchronise_device_state.call_count == 1 diff --git a/tests/alexa_client/test_connection.py b/tests/alexa_client/test_connection.py index 171e54c..c462169 100644 --- a/tests/alexa_client/test_connection.py +++ b/tests/alexa_client/test_connection.py @@ -59,10 +59,16 @@ def audio_file(): def test_create_connection(manager): manager.create_connection() - assert manager.connection.host == 'avs-alexa-eu.amazon.com' + assert manager.connection.host == constants.BASE_URL_EUROPE assert manager.connection.secure is True +def test_create_connection_explicit_base_url(manager): + manager.create_connection(base_url=constants.BASE_URL_NORTH_AMERICA) + + assert manager.connection.host == constants.BASE_URL_NORTH_AMERICA + + def test_establish_downstream_conncetion(manager, authentication_headers): manager.create_connection() @@ -76,7 +82,7 @@ def test_establish_downstream_conncetion(manager, authentication_headers): b':scheme': b'https', b':method': b'GET', b':path': b'/v20160207/directives', - b':authority': b'avs-alexa-eu.amazon.com', + b':authority': b'alexa.eu.gateway.devices.a2z.com', b'auth': b'value', } @@ -97,7 +103,7 @@ def test_synchronise_device_state( assert headers == { b':method': b'POST', b':scheme': b'https', - b':authority': b'avs-alexa-eu.amazon.com', + b':authority': b'alexa.eu.gateway.devices.a2z.com', b':path': b'/v20160207/events', b'content-type': b'multipart/form-data; boundary=boundary', b'auth': b'value', @@ -148,7 +154,7 @@ def test_send_audio_file( assert headers == { b':method': b'POST', b':scheme': b'https', - b':authority': b'avs-alexa-eu.amazon.com', + b':authority': b'alexa.eu.gateway.devices.a2z.com', b':path': b'/v20160207/events', b'content-type': b'multipart/form-data; boundary=boundary', b'auth': b'value', @@ -319,6 +325,6 @@ def test_ping(manager, authentication_headers): b':scheme': b'https', b':method': b'GET', b':path': b'/ping', - b':authority': b'avs-alexa-eu.amazon.com', + b':authority': b'alexa.eu.gateway.devices.a2z.com', b'auth': b'value', }