Skip to content

Commit

Permalink
Code rewrite and new statistical sensors (#28)
Browse files Browse the repository at this point in the history
* Minor code rewrite + New statistical sensors. Also release 1.0.0 upcoming...

* Better handling of statistics when none is available.

* Should fix date problems when going form one week/month/year to another. Also other minor fixes.

* Patch 1 - Should now show no data on the first of each week/month/year.

* Code refactor

* Update github workflow to support python 3.9

* Add HACS validation

* Changed week to ISO week instead. Code refactor to support changes in underling mytoyota. Added support for async polling of endpoints.

* Fixed TypeError with get_average_fuel_consumed

* Fixed average_fuel_consumed to only show value.

* Update README.md, add info.md and hacs.json.

* Added supported regions to info.md

* Better handling of server errors
  • Loading branch information
DurgNomis-drol committed Aug 25, 2021
1 parent d3ed59d commit 34cab3b
Show file tree
Hide file tree
Showing 14 changed files with 1,907 additions and 1,587 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/linting.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
name: Pre-commit
strategy:
matrix:
python-version: [3.8]
python-version: [3.8, 3.9]
runs-on: ubuntu-latest
steps:
- name: Check out the repository
Expand Down
19 changes: 19 additions & 0 deletions .github/workflows/validate.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: Validate

on:
push:
branches:
- master
pull_request:
schedule:
- cron: "0 0 * * *"

jobs:
validate:
runs-on: "ubuntu-latest"
steps:
- uses: "actions/checkout@v2"
- name: HACS validation
uses: "hacs/action@main"
with:
category: "integration"
43 changes: 26 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,36 @@
This custom integration aims to provide plug-and-play integration for your Toyota vehicle.
</p>

**[!] Beta version alert.**
Please note this integration is in the early stage of its development.
See [Contribution](#contribution) section for more information on how to contribute.

## About

This is a custom component the retrieves' data from the
Toyota MyT API and makes them available in Home Assistant as sensors.
This is a custom integration the retrieves' data from the
Toyota MyT API and makes them available in Home Assistant as different types of sensors.
As there is no official API from Toyota, I will try my best to keep
it working but there are no promises.
it working, but there are no promises.

## Features

Only Europe is supported right now. If you want to help add other regions, please open an issue over at [`mytoyota`](https://github.com/DurgNomis-drol/mytoyota).

This component will set up the following platforms:

| Platform | Sample sensor | Description |
| ---------------- | ----------------------------- | --------------------------------- |
| `sensor` | `sensor.aygo` | Static data about your car |
| `sensor` | `sensor.aygo_odometer` | Odometer information |
| `sensor` | `sensor.aygo_fuel_tank` | Fuel tank information |
| `sensor` | `sensor.aygo_starter_battery` | Starter battery health |
| `device_tracker` | `device_tracker.aygo` | Shows you last parked information |
| Platform | Name | Description |
| ---------------- | --------------------------------- | --------------------------------- |
| `sensor` | `sensor.aygo` | Static data about your car |
| `sensor` | `sensor.aygo_odometer` | Odometer information |
| `sensor` | `sensor.aygo_fuel_tank` | Fuel tank information |
| `sensor` | `sensor.aygo_starter_battery` | Starter battery health |
| `sensor` | `sensor.aygo_current_week_stats` | Statistics for current week |
| `sensor` | `sensor.aygo_current_month_stats` | Statistics for current month |
| `sensor` | `sensor.aygo_current_year_stats` | Statistics for current year |
| `device_tracker` | `device_tracker.aygo` | Shows you last parked information |

### Notes about statistics sensors

Be aware that weeks start on Sundays and not Mondays. This is not possible to change due to limitation on Toyota's end.

**Sensors displaying statistical information are coming soon...**
When starting a new week, month or year, it will not show any information before your first trip. Even though a new month starts on the 1, you will need to wait for the 2 of the month before it is able to show you current month stats. This due to a limitation in Toyota API. This limitation also applies to weeks.
Due to this, this integration will list sensors as unavailable when no data is available.

## Getting started

Expand All @@ -43,9 +47,14 @@ Use Home Assistant build 2021.3 or above.

If you can confirm that it is working as advertised on older version please open a PR.

**Note: It is _only_ tested against latest, but should work on older versions too.**
**Note:** It is **_only_** tested against latest, but should work on older versions too.

**Note:** Future updates may change which version are required.

### HACS installation (Recommended)

**Note: Future updates may change which version are required.**
Open HACS and search for `Toyota Connected Services` under integrations.
You can choose to install a specific version or from master (Not recommended).

### Manual Installation

Expand Down
117 changes: 63 additions & 54 deletions custom_components/toyota/__init__.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,32 @@
"""Toyota integration"""
import asyncio
import asyncio.exceptions as asyncioexceptions
from datetime import timedelta
import logging

import async_timeout
import httpx
from mytoyota.client import MyT
from mytoyota.exceptions import ToyotaLoginError
from mytoyota.exceptions import ToyotaInternalServerError, ToyotaLoginError

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_EMAIL, CONF_PASSWORD, CONF_REGION
from homeassistant.core import Config, HomeAssistant
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
DataUpdateCoordinator,
)
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed

from .const import (
ALIAS,
CONF_LOCALE,
DATA_CLIENT,
DATA_COORDINATOR,
DETAILS,
DOMAIN,
MODEL,
MONTHLY,
PLATFORMS,
STARTUP_MESSAGE,
STATISTICS,
VIN,
WEEKLY,
YEARLY,
)

_LOGGER = logging.getLogger(__name__)
Expand All @@ -34,9 +35,10 @@
UPDATE_INTERVAL = timedelta(seconds=300)


async def async_setup(_hass: HomeAssistant, _config: Config) -> bool:
"""Set up this integration using YAML is not supported."""
return True
async def with_timeout(task, timeout_seconds=15):
"""Run an async task with a timeout."""
async with async_timeout.timeout(timeout_seconds):
return await task


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
Expand All @@ -62,17 +64,55 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
async def async_update_data():
"""Fetch data from Toyota API."""

vehicles = []

try:
vehicles = await client.gather_all_information()

vehicles = []

cars = await with_timeout(client.get_vehicles())

for car in cars:
# Use parallel request to get car data and statistics.

vehicle_data = await asyncio.gather(
*[
client.get_vehicle_status(car),
client.get_driving_statistics(car[VIN], interval="isoweek"),
client.get_driving_statistics(car[VIN]),
client.get_driving_statistics(car[VIN], interval="year"),
]
)

# Vehicle status
vehicle = vehicle_data[0]

# Vehicle statistics
vehicle[STATISTICS] = {
WEEKLY: vehicle_data[1],
MONTHLY: vehicle_data[2],
YEARLY: vehicle_data[3],
}

vehicles.append(vehicle)

_LOGGER.debug(vehicles)
return vehicles

except ToyotaLoginError as ex:
_LOGGER.error(ex)
except Exception as ex: # pylint: disable=broad-except
_LOGGER.error(ex)

_LOGGER.debug(vehicles)
return vehicles
except ToyotaInternalServerError as ex:
raise UpdateFailed(ex) from ex
except httpx.ConnectTimeout as ex:
raise UpdateFailed("Unable to connect to Toyota Connected Services") from ex
except (
asyncioexceptions.CancelledError,
asyncioexceptions.TimeoutError,
httpx.ReadTimeout,
) as ex:

raise UpdateFailed(
"Update canceled! Toyota's API was too slow to respond."
" Will try again later..."
) from ex

coordinator = DataUpdateCoordinator(
hass,
Expand All @@ -94,47 +134,16 @@ async def async_update_data():
raise ConfigEntryNotReady

# Setup components
for platform in PLATFORMS:
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(entry, platform)
)
hass.config_entries.async_setup_platforms(entry, PLATFORMS)

return True


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
"""Unload a config entry."""
unload_ok = all(
await asyncio.gather(
*[
hass.config_entries.async_forward_entry_unload(entry, platform)
for platform in PLATFORMS
]
)
)
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)

if unload_ok:
hass.data[DOMAIN].pop(entry.entry_id)

return unload_ok


class ToyotaEntity(CoordinatorEntity):
"""Defines a base Toyota entity."""

def __init__(self, coordinator, index):
"""Initialize the Toyota entity."""
super().__init__(coordinator)
self.index = index
self.vin = self.coordinator.data[self.index][VIN]
self.alias = self.coordinator.data[self.index][ALIAS]
self.details = self.coordinator.data[self.index][DETAILS]

@property
def device_info(self):
"""Return device info for the Toyota entity."""
return {
"identifiers": {(DOMAIN, self.vin)},
"name": self.alias,
"model": self.details[MODEL],
"manufacturer": DOMAIN,
}
6 changes: 3 additions & 3 deletions custom_components/toyota/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@
from homeassistant import config_entries
from homeassistant.const import CONF_EMAIL, CONF_PASSWORD, CONF_REGION

from .const import CONF_LOCALE, CONF_REGION_SUPPORTED
from .const import CONF_LOCALE

# https://github.com/PyCQA/pylint/issues/3202
from .const import DOMAIN # pylint: disable=unused-import

_LOGGER = logging.getLogger(__name__)

supported_regions = CONF_REGION_SUPPORTED
supported_regions = MyT.get_supported_regions()

DATA_SCHEMA = vol.Schema(
{
Expand All @@ -34,7 +34,7 @@
)


class MazdaConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
class ToyotaConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Toyota Connected Services."""

VERSION = 1
Expand Down
18 changes: 17 additions & 1 deletion custom_components/toyota/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,37 @@

# DATA COORDINATOR ATTRIBUTES
ALIAS = "alias"
AVERAGE_SPEED = "averageSpeedInKmph"
BATTERY_HEALTH = "batteryHealth"
BUCKET = "bucket"
CONNECTED_SERVICES = "connectedServices"
DATA = "data"
MONTHLY = "monthly"
WEEKLY = "weekly"
YEARLY = "yearly"
DETAILS = "details"
ENGINE = "engine"
EV_DISTANCE = "evDistanceInKm"
EV_DISTANCE_PERCENTAGE = "evDistancePercentage"
FUEL = "Fuel"
FUEL_CONSUMED = "totalFuelConsumedInL"
FUEL_TYPE = "fuel"
HYBRID = "hybrid"
IMAGE = "imageUrl"
LICENSE_PLATE = "licensePlate"
MAX_SPEED = "maxSpeedInKmph"
MILEAGE = "mileage"
MILEAGE_UNIT = "mileage_unit"
MODEL = "modelName"
NIGHT_TRIPS = "nightTripsCount"
ODOMETER = "odometer"
PARKING = "parking"
STATUS = "status"
SERVICES = "servicesEnabled"
STATISTICS = "statistics"
STATUS = "status"
TOTAL_DURATION = "totalDurationInSec"
TOTAL_DISTANCE = "totalDistanceInKm"
TRIPS = "tripCount"
VIN = "vin"

# ICONS
Expand All @@ -42,6 +57,7 @@
ICON_FUEL = "mdi:gas-station"
ICON_ODOMETER = "mdi:counter"
ICON_PARKING = "mdi:map-marker"
ICON_HISTORY = "mdi:history"


STARTUP_MESSAGE = f"""
Expand Down
Loading

0 comments on commit 34cab3b

Please sign in to comment.