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
2 changes: 0 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ jobs:
python: 3.8
- if: branch IN (master, develop)
python: 3.6
- if: branch IN (master, develop)
python: pypy
- if: branch IN (master, develop)
python: pypy3

Expand Down
22 changes: 4 additions & 18 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ Carson Living Python API

Python Carson Living is a library written in Python that exposes the carson.live devices as Python objects.

Please note, that `Carson <https://carson.live>`_ does not provide an official API documentation, therefore this project
is solely based on reverse engineering.
Disclaimer
----------
Please use this library at your own risk and make sure that you do not violate the
`Terms of Service of Carson <https://www.carson.live/terms>`_.

Getting started
---------------
Expand Down Expand Up @@ -157,15 +159,6 @@ Checkout ``./scripts/carsoncli.py`` for further API implementation examples.

Development Notes
-----------------
Request Headers
~~~~~~~~~~~~~~~
The library currently works with the following base headers:

.. code-block::

User-Agent: Carson/1.0.171 (live.carson.app; build:245; iOS 13.1.0) Alamofire/1.0.171
X-Device-Type: ios
X-App-Version: 1.0.171(245)

Code Documentation
~~~~~~~~~~~~~~~~~~
Expand All @@ -188,13 +181,6 @@ The following is not supported by the API yet and remains TODO.
- Expand and extract EagleEye API (into separate project?).



License
-------

python-carson-living is released under the Apache License Version 2.0. See the LICENSE_ file for more
details.

Credits && Thanks
-----------------

Expand Down
5 changes: 3 additions & 2 deletions carson_living/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@

from carson_living.const import (BASE_HEADERS,
C_API_URI,
C_AUTH_ENDPOINT)
C_AUTH_ENDPOINT,
RETRY_TOKEN)
from carson_living.util import default_carson_response_handler
from carson_living.error import (CarsonAPIError,
CarsonAuthenticationError,
Expand Down Expand Up @@ -152,7 +153,7 @@ def valid_token(self):
return self._token_expiration_time > int(time.time())

def authenticated_query(self, url, method='get', params=None,
json=None, retry_auth=1,
json=None, retry_auth=RETRY_TOKEN,
response_handler=default_carson_response_handler):
"""Perform an authenticated Query against Carson Living

Expand Down
25 changes: 5 additions & 20 deletions carson_living/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,19 @@
'MSG': 'msg'
}

"""Pretend to be Carson iOS Installation v1.0.171"""
BASE_HEADERS = {
'User-Agent': 'Carson/1.0.200 (live.carson.app; build:315; iOS 13.3.0) '
'Alamofire/1.0.200',
'X-App-Version': '1.0.200(315)',
'X-Device-Type': 'ios'
'User-Agent': 'okhttp/4.9.0',
'X-App-Version': '2.1.5',
'X-Device-Type': 'android'
}

# number of attempts to refresh token
# RETRY_TOKEN = 3

# default suffix for session cache file
# CACHE_ATTRS = {'account': None, 'alerts': None, 'token': None}
#
# try:
# CACHE_FILE = os.path.join(os.getenv("HOME"),
# '.carson_living-session.cache')
# except (AttributeError, TypeError):
# CACHE_FILE = os.path.join('.', '.carson_living-session.cache')


# code when item was not found
# NOT_FOUND = -1
RETRY_TOKEN = 1

# Carson API endpoints
# Beware URLs end in '/', otherwise it returns a
# HTTP/1.1 301 Moved Permanently to the correct version.
C_API_VERSION = 'v1.4.3'
C_API_VERSION = 'v1.4.4'
C_API_URI = 'https://api.carson.live/api/' + C_API_VERSION

C_AUTH_ENDPOINT = '/auth/login/'
Expand Down
4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
requests
pyjwt
requests==2.25.1
PyJWT==2.4.0
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"""Python Carson Living setup script."""
from setuptools import setup

_VERSION = '0.0.5'
_VERSION = '0.0.6'


def readme():
Expand Down
18 changes: 9 additions & 9 deletions tests/test_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def test_auth_valid_token_init(self):
@requests_mock.Mocker()
def test_update_token_success(self, mock):
"""Test token update"""
mock.post('https://api.carson.live/api/v1.4.3/auth/login/',
mock.post('https://api.carson.live/api/v1.4.4/auth/login/',
text=load_fixture('carson.live', 'carson_login.json'))

mock_token_update_cb = Mock()
Expand All @@ -89,7 +89,7 @@ def test_update_token_success(self, mock):
@requests_mock.Mocker()
def test_update_token_fail(self, mock):
"""Test authentication failure in token update"""
mock.post('https://api.carson.live/api/v1.4.3/auth/login/',
mock.post('https://api.carson.live/api/v1.4.4/auth/login/',
text=load_fixture('carson.live', 'carson_auth_failure.json'),
status_code=401)

Expand All @@ -110,9 +110,9 @@ def test_expired_token_is_invalid(self):
@requests_mock.Mocker()
def test_successful_query_without_initial_token(self, mock):
"""Test automatic authentication on query without initial token"""
mock.post('https://api.carson.live/api/v1.4.3/auth/login/',
mock.post('https://api.carson.live/api/v1.4.4/auth/login/',
text=load_fixture('carson.live', 'carson_login.json'))
query_url = 'https://api.carson.live/api/v1.4.3/me/'
query_url = 'https://api.carson.live/api/v1.4.4/me/'
mock.get(query_url,
text=load_fixture('carson.live', 'carson_me.json'))

Expand All @@ -129,7 +129,7 @@ def test_successful_query_without_initial_token(self, mock):
@requests_mock.Mocker()
def test_successful_query_with_initial_token(self, mock):
"""Test query with initial valid token"""
query_url = 'https://api.carson.live/api/v1.4.3/me/'
query_url = 'https://api.carson.live/api/v1.4.4/me/'
mock.get(query_url,
text=load_fixture('carson.live', 'carson_me.json'))

Expand All @@ -148,9 +148,9 @@ def test_successful_query_with_initial_token(self, mock):
@requests_mock.Mocker()
def test_recursive_retry(self, mock):
""""Test recursive query retry on Authentication Failure"""
mock.post('https://api.carson.live/api/v1.4.3/auth/login/',
mock.post('https://api.carson.live/api/v1.4.4/auth/login/',
text=load_fixture('carson.live', 'carson_login.json'))
query_url = 'https://api.carson.live/api/v1.4.3/me/'
query_url = 'https://api.carson.live/api/v1.4.4/me/'
mock.get(query_url,
text=load_fixture('carson.live', 'carson_auth_failure.json'),
status_code=401)
Expand All @@ -169,7 +169,7 @@ def test_recursive_retry(self, mock):
@requests_mock.Mocker()
def test_raise_communication_error_on_empty(self, mock):
"""Test failure on empty response"""
query_url = 'https://api.carson.live/api/v1.4.3/me/'
query_url = 'https://api.carson.live/api/v1.4.4/me/'
mock.get(query_url,
status_code=500)

Expand All @@ -185,7 +185,7 @@ def test_raise_communication_error_on_empty(self, mock):
@requests_mock.Mocker()
def test_raise_communication_error_wrong_json(self, mock):
"""Test failure on response with missing keys"""
query_url = 'https://api.carson.live/api/v1.4.3/me/'
query_url = 'https://api.carson.live/api/v1.4.4/me/'
mock.get(query_url,
text=load_fixture('carson.live', 'carson_missing_keys.json'))

Expand Down