Skip to content

Commit

Permalink
Use objects instead of dicts (#38)
Browse files Browse the repository at this point in the history
* Return objects instead of dicts

* Bump version

* Better handling of http errors and error's occurring at toyota's end

* Update README.md

* bump version
  • Loading branch information
DurgNomis-drol authored Aug 31, 2021
1 parent 29861cf commit f7c4f06
Show file tree
Hide file tree
Showing 11 changed files with 517 additions and 240 deletions.
13 changes: 13 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ repos:
language: system
types: [python]
require_serial: true
- id: isort
name: isort
entry: poetry run isort
language: system
types: [python]
require_serial: true
- id: flake8
name: flake8
entry: poetry run flake8
Expand All @@ -22,8 +28,15 @@ repos:
- id: pylint
name: pylint
entry: poetry run pylint
exclude: ^glocaltokens/google/
language: system
types: [python]
- id: codespell
name: codespell
entry: poetry run codespell --skip="./*"
language: system
pass_filenames: false
always_run: true
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v2.2.1
hooks:
Expand Down
62 changes: 50 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pip install mytoyota
## Usage

```python
import arrow
import json
import asyncio
from mytoyota.client import MyT

Expand All @@ -41,29 +41,67 @@ async def get_information():
cars = await client.get_vehicles()

for car in cars:

# Returns live data from car/last time you used it as an object.
vehicle = await client.get_vehicle_status(car)


# You can either get them all async (Recommended) or sync (Look further down).
data = await asyncio.gather(
*[
client.get_driving_statistics(vehicle.vin, interval="day"),
client.get_driving_statistics(vehicle.vin, interval="isoweek"),
client.get_driving_statistics(vehicle.vin),
client.get_driving_statistics(vehicle.vin, interval="year"),
]
)

# You can deposit each result into premade object holder for statistics. This will make it easier to use in your code.

vehicle.statistics.daily = data[0]
vehicle.statistics.weekly = data[1]
vehicle.statistics.monthly = data[2]
vehicle.statistics.yearly = data[3]


# You can access odometer data like this:
fuel = vehicle.odometer.fuel
# Or Parking information:
latitude = vehicle.parking.latitude


# Pretty print the object. This will provide you with all available information.
print(json.dumps(vehicle.as_dict(), indent=3))


# -------------------------------
# All data is return in an object.
# -------------------------------

# Returns live data from car/last time you used it.
vehicle = await client.get_vehicle_status(car)
print(vehicle)
print(vehicle.as_dict())

# Stats returned in a dict
daily_stats = await client.get_driving_statistics(cars[0]['vin'], interval="day")
print(daily_stats)
daily_stats = await client.get_driving_statistics(vehicle.vin, interval="day")
print(daily_stats.as_list())

# Stats returned in json.
weekly_stats = await client.get_driving_statistics_json(cars[0]['vin'], interval="week")
print(weekly_stats)
weekly_stats = await client.get_driving_statistics(vehicle.vin, interval="isoweek")
print(weekly_stats.as_list())

# ISO 8601 week stats
iso_weekly_stats = await client.get_driving_statistics(cars[0]['vin'], interval="isoweek")
print(iso_weekly_stats)
iso_weekly_stats = await client.get_driving_statistics(vehicle.vin, interval="isoweek")
print(iso_weekly_stats.as_list)

# Monthly stats is returned by default
monthly_stats = await client.get_driving_statistics(cars[0]['vin'])
print(monthly_stats)
monthly_stats = await client.get_driving_statistics(vehicle.vin)
print(monthly_stats.as_list())

#Get year to date stats.
yearly_stats = await client.get_driving_statistics(car['vin'], interval="year")
print(yearly_stats)
yearly_stats = await client.get_driving_statistics(vehicle.vin, interval="year")
print(yearly_stats.as_list())


loop = asyncio.get_event_loop()
loop.run_until_complete(get_information())
Expand Down
78 changes: 47 additions & 31 deletions mytoyota/api.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,29 @@
"""Toyota Connected Services Controller"""
from datetime import datetime
import logging
from typing import Optional, Union
from datetime import datetime

import httpx

from .const import (
BASE_URL,
BASE_URL_CARS,
CUSTOMERPROFILE,
ENDPOINT_AUTH,
HTTP_INTERNAL,
HTTP_NO_CONTENT,
HTTP_OK,
HTTP_SERVICE_UNAVAILABLE,
PASSWORD,
SUPPORTED_REGIONS,
TIMEOUT,
TOKEN,
TOKEN_DURATION,
TOKEN_VALID_URL,
USERNAME,
UUID,
HTTP_OK,
HTTP_NO_CONTENT,
SUPPORTED_REGIONS,
TOKEN_VALID_URL,
TOKEN_DURATION,
HTTP_INTERNAL,
)
from .exceptions import (
ToyotaLoginError,
ToyotaInternalServerError,
)
from .exceptions import ToyotaApiError, ToyotaInternalError, ToyotaLoginError
from .utils import is_valid_token

_LOGGER: logging.Logger = logging.getLogger(__package__)
Expand Down Expand Up @@ -77,7 +76,7 @@ async def get_uuid(self) -> str:
return self.uuid

@staticmethod
def _has_expired(creation_dt, duration) -> bool:
def _has_expired(creation_dt: datetime, duration: int) -> bool:
"""Checks if an specified token/object has expired"""
return datetime.now().timestamp() - creation_dt.timestamp() > duration

Expand Down Expand Up @@ -124,7 +123,7 @@ async def get_new_token(self) -> str:
result = response.json()

if TOKEN not in result or UUID not in result[CUSTOMERPROFILE]:
_LOGGER.error("[!] Could not get token or UUID.")
raise ToyotaLoginError("Could not get token or UUID from result")

token = result.get(TOKEN)
uuid = result[CUSTOMERPROFILE][UUID]
Expand All @@ -141,7 +140,10 @@ async def get_new_token(self) -> str:
return self.token

async def get(
self, endpoint: str, headers=None, params=None
self,
endpoint: str,
headers: Optional[dict] = None,
params: Optional[dict] = None,
) -> Union[dict, list, None]:
"""Make the request."""

Expand Down Expand Up @@ -172,26 +174,32 @@ async def get(

if resp.status_code == HTTP_OK:
result = resp.json()
elif resp.status_code == HTTP_NO_CONTENT:
elif resp.status_code is HTTP_NO_CONTENT:
# This prevents raising or logging an error
# if the user have not setup Connected Services
result = None
elif resp.status_code == HTTP_INTERNAL:
_LOGGER.debug("Connected services is disabled")
elif resp.status_code is HTTP_INTERNAL:
response = resp.json()
raise ToyotaInternalServerError(
raise ToyotaInternalError(
"Internal server error occurred! Code: "
+ response["code"]
+ " - "
+ response["message"],
)
elif resp.status_code is HTTP_SERVICE_UNAVAILABLE:
raise ToyotaApiError(
"Toyota Connected Services are temporarily unavailable"
)
else:
_LOGGER.error("HTTP: %i - %s", resp.status_code, resp.text)
result = None
raise ToyotaInternalError(
"HTTP: " + resp.status_code + " - " + resp.text
)

return result

async def put(
self, endpoint: str, body: dict, headers=None
self, endpoint: str, body: dict, headers: Optional[dict] = None
) -> Union[dict, list, None]:
"""Make the request."""

Expand Down Expand Up @@ -224,29 +232,37 @@ async def put(
# This prevents raising or logging an error
# if the user have not setup Connected Services
result = None
_LOGGER.debug("Connected services is disabled")
elif resp.status_code == HTTP_INTERNAL:
response = resp.json()
raise ToyotaInternalServerError(
raise ToyotaInternalError(
"Internal server error occurred! Code: "
+ response["code"]
+ " - "
+ response["message"],
)
elif resp.status_code is HTTP_SERVICE_UNAVAILABLE:
raise ToyotaApiError(
"Toyota Connected Services are temporarily unavailable"
)
else:
_LOGGER.error("HTTP: %i - %s", resp.status_code, resp.text)
result = None
raise ToyotaInternalError(
"HTTP: " + resp.status_code + " - " + resp.text
)

return result

async def set_vehicle_alias_endpoint(self, new_alias: str, vehicle_id: int) -> dict:
async def set_vehicle_alias_endpoint(
self, new_alias: str, vehicle_id: int
) -> Optional[dict]:
"""Set vehicle alias."""

return await self.put(
f"{self.get_base_url_cars()}/api/users/{self.uuid}/vehicles/{vehicle_id}",
{"id": vehicle_id, "alias": new_alias},
)

async def get_vehicles_endpoint(self) -> list:
async def get_vehicles_endpoint(self) -> Optional[list]:
"""Retrieves list of cars you have registered with MyT"""

arguments = "?services=uio&legacy=true"
Expand All @@ -255,7 +271,7 @@ async def get_vehicles_endpoint(self) -> list:
f"{self.get_base_url_cars()}/vehicle/user/{self.uuid}/vehicles{arguments}"
)

async def get_connected_services_endpoint(self, vin: str) -> dict:
async def get_connected_services_endpoint(self, vin: str) -> Optional[dict]:
"""Get information about connected services for the given car."""

arguments = "?legacy=true&services=fud,connected"
Expand All @@ -264,28 +280,28 @@ async def get_connected_services_endpoint(self, vin: str) -> dict:
f"{self.get_base_url_cars()}/vehicle/user/{self.uuid}/vehicle/{vin}{arguments}"
)

async def get_odometer_endpoint(self, vin: str) -> list:
async def get_odometer_endpoint(self, vin: str) -> Optional[list]:
"""Get information from odometer."""

return await self.get(f"{self.get_base_url()}/vehicle/{vin}/addtionalInfo")

async def get_parking_endpoint(self, vin: str) -> dict:
async def get_parking_endpoint(self, vin: str) -> Optional[dict]:
"""Get where you have parked your car."""

return await self.get(
f"{self.get_base_url()}/users/{self.uuid}/vehicle/location", {"VIN": vin}
)

async def get_vehicle_status_endpoint(self, vin: str) -> dict:
async def get_vehicle_status_endpoint(self, vin: str) -> Optional[dict]:
"""Get information about the vehicle."""

return await self.get(
f"{self.get_base_url()}/vehicles/{vin}/remoteControl/status"
)

async def get_driving_statistics_endpoint(
self, vin: str, from_date: str, interval: str = None
) -> dict:
self, vin: str, from_date: str, interval: Optional[str] = None
) -> Optional[dict]:
"""Get driving statistic"""

params = {"from": from_date, "calendarInterval": interval}
Expand Down
Loading

0 comments on commit f7c4f06

Please sign in to comment.