-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
16 changed files
with
274 additions
and
4 deletions.
There are no files selected for viewing
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from LibreView.utils import * |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
from dataclasses import dataclass | ||
from uuid import UUID | ||
from dataclass_wizard import JSONWizard | ||
from LibreView.models.Sensor import Sensor | ||
from LibreView.models.GlucoseMeasurement import GlucoseMeasurement | ||
|
||
|
||
@dataclass | ||
class Connection(JSONWizard): | ||
id: UUID | ||
patient_id: UUID | ||
country: str | ||
status: int | ||
first_name: str | ||
last_name: str | ||
target_low: int | ||
target_high: int | ||
uom: int | ||
sensor: Sensor | ||
glucose_measurement: GlucoseMeasurement |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
from dataclasses import dataclass | ||
from uuid import UUID | ||
from dataclass_wizard import JSONWizard | ||
|
||
|
||
@dataclass | ||
class Device(JSONWizard): | ||
id: UUID | ||
nickname: str | ||
sn: UUID | ||
type: int | ||
upload_date: int |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
from dataclasses import dataclass | ||
from datetime import datetime | ||
from dataclass_wizard import JSONWizard, json_field | ||
|
||
|
||
@dataclass | ||
class GlucoseMeasurement(JSONWizard): | ||
_timestamp: str | ||
type: int | ||
value_in_mg_per_dl: int | ||
trend_arrow: int | ||
measurement_color: int | ||
glucose_units: int | ||
value: float | ||
is_high: bool | ||
is_low: bool | ||
_factory_timestamp: str = json_field("FactoryTimestamp") # type: ignore | ||
|
||
@property | ||
def factory_timestamp(self) -> datetime: | ||
return self.parse_dt(self._factory_timestamp) | ||
|
||
_timestamp: str = json_field("Timestamp") # type: ignore | ||
|
||
@property | ||
def timestamp(self) -> datetime: | ||
return self.parse_dt(self._timestamp) | ||
|
||
trend_message: str | None = None | ||
|
||
def parse_dt(self, val: str) -> datetime: | ||
splitted = val.split("/") | ||
splitted[0] = splitted[0].zfill(2) | ||
splitted[1] = splitted[1].zfill(2) | ||
return datetime.strptime("/".join(splitted), "%m/%d/%Y %I:%M:%S %p") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
from dataclasses import dataclass | ||
from uuid import UUID | ||
from dataclass_wizard import JSONWizard | ||
|
||
|
||
@dataclass | ||
class Practice(JSONWizard): | ||
id: UUID | ||
practice_id: str | ||
name: str | ||
address1: str | ||
city: str | ||
state: str | ||
zip: str | ||
phone_number: str | ||
address2: str | None = None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
from dataclasses import dataclass | ||
from dataclass_wizard import JSONWizard | ||
|
||
|
||
@dataclass | ||
class Sensor(JSONWizard): | ||
device_id: str | ||
sn: str | ||
a: int | ||
w: int | ||
pt: int | ||
s: bool | ||
lj: bool |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
from dataclasses import dataclass | ||
from typing import Any, List, Dict | ||
from uuid import UUID | ||
from dataclass_wizard import JSONWizard | ||
from LibreView.models.Device import Device | ||
from LibreView.models.Practice import Practice | ||
|
||
|
||
@dataclass | ||
class User(JSONWizard): | ||
id: UUID | ||
first_name: str | ||
last_name: str | ||
email: str | ||
country: str | ||
ui_language: str | ||
communication_language: str | ||
account_type: str | ||
uom: int | ||
date_format: int | ||
time_format: int | ||
email_day: List[int] | ||
created: int | ||
last_login: int | ||
date_of_birth: int | ||
practices: Dict[UUID, Practice] | ||
devices: Dict[str, Device] |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
from typing import List | ||
import requests | ||
from LibreView.models.User import User | ||
from LibreView.models.Connection import Connection | ||
|
||
|
||
def reauth_on_fail(func): | ||
def wrapper(*args): | ||
try: | ||
return func(*args) | ||
except requests.HTTPError as e: | ||
if e.response.status_code == 401: | ||
api: API = args[0] | ||
api.authenticate() | ||
return func(*args) | ||
raise e | ||
|
||
return wrapper | ||
|
||
|
||
class API: | ||
username: str | ||
password: str | ||
base_url = "https://api.libreview.io" | ||
client = requests.session() | ||
product = "llu.android" | ||
version = "4.7" | ||
|
||
def __init__(self, username: str, password: str): | ||
self.username = username | ||
self.password = password | ||
self.client.headers["product"] = self.product | ||
self.client.headers["version"] = self.version | ||
self.authenticate() | ||
|
||
def authenticate(self): | ||
r = self.client.post( | ||
f"{self.base_url}/llu/auth/login", | ||
json={ | ||
"email": self.username, | ||
"password": self.password, | ||
}, | ||
) | ||
r.raise_for_status() | ||
content = r.json() | ||
|
||
# status 0 == login successfull | ||
if content and content.get("status") == 0: | ||
self.set_token(content["data"]["authTicket"]["token"]) | ||
return | ||
|
||
# status 4 == missing term accepts | ||
if content and content.get("status") == 4: | ||
self.accept_terms(content["data"]["authTicket"]["token"]) | ||
return | ||
|
||
error = "Unknown error occured during authentication" | ||
if content and content.get("error") and content["error"].get("message"): | ||
error = content["error"]["message"] | ||
|
||
raise Exception(error) | ||
|
||
def set_token(self, token): | ||
self.client.headers["Authorization"] = f"Bearer {token}" | ||
|
||
def accept_terms(self, token): | ||
r = self.client.post( | ||
f"{self.base_url}/llu/auth/login", | ||
headers={ | ||
"Authorization": f"Bearer {token}", | ||
}, | ||
) | ||
r.raise_for_status() | ||
content = r.json() | ||
if content and content.get("status") == 0: | ||
self.set_token(content["data"]["authTicket"]["token"]) | ||
return | ||
|
||
@reauth_on_fail | ||
def get_user(self) -> User: | ||
r = self.client.get( | ||
f"{self.base_url}/user", | ||
) | ||
r.raise_for_status() | ||
return User.from_dict(r.json()["data"]["user"]) | ||
|
||
@reauth_on_fail | ||
def get_connections(self) -> List[Connection]: | ||
r = self.client.get( | ||
f"{self.base_url}/llu/connections", | ||
) | ||
r.raise_for_status() | ||
return Connection.from_list(r.json()["data"]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from .API import API |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
[[source]] | ||
url = "https://pypi.org/simple" | ||
verify_ssl = true | ||
name = "pypi" | ||
|
||
[packages] | ||
requests = "*" | ||
dataclass-wizard = "*" | ||
|
||
[dev-packages] | ||
black = "*" | ||
twine = "*" | ||
pytest = "*" | ||
pylint = "*" | ||
pip-system-certs = "*" | ||
pytest-cov = "*" | ||
|
||
[requires] | ||
python_version = "3.11" |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
from datetime import datetime | ||
import os | ||
from uuid import UUID | ||
import LibreView | ||
import logging | ||
|
||
LOGGER = logging.getLogger(__name__) | ||
USERNAME: str = os.environ["libre_username"] | ||
PASSWORD: str = os.environ["libre_password"] | ||
|
||
|
||
def test_logon(): | ||
api = LibreView.API(USERNAME, PASSWORD) | ||
assert api.client.headers.get("Authorization") != None | ||
|
||
|
||
def test_get_user(): | ||
api = LibreView.API(USERNAME, PASSWORD) | ||
usr = api.get_user() | ||
assert isinstance(usr.first_name, str) | ||
assert len(usr.first_name) > 0 | ||
|
||
|
||
def test_get_connections(): | ||
api = LibreView.API(USERNAME, PASSWORD) | ||
cons = api.get_connections() | ||
assert len(cons) > 0 | ||
con = cons[0] | ||
assert isinstance(con.patient_id, UUID) | ||
assert isinstance(con.glucose_measurement.timestamp, datetime) | ||
assert len(con.first_name) > 0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters