Address review comments and minor fix for Mazda integration (#47702)

* Address comments from code review

* Fix handling of missing sensor values

* Use default timeout for get_vehicles

* Fix test_update_auth_failure
This commit is contained in:
Brandon Rothweiler 2021-03-15 01:57:39 -04:00 committed by GitHub
parent bcadccf7aa
commit fbf3225234
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 64 additions and 46 deletions

View file

@ -32,6 +32,12 @@ _LOGGER = logging.getLogger(__name__)
PLATFORMS = ["sensor"] PLATFORMS = ["sensor"]
async def with_timeout(task, timeout_seconds=10):
"""Run an async task with a timeout."""
async with async_timeout.timeout(timeout_seconds):
return await task
async def async_setup(hass: HomeAssistant, config: dict): async def async_setup(hass: HomeAssistant, config: dict):
"""Set up the Mazda Connected Services component.""" """Set up the Mazda Connected Services component."""
hass.data[DOMAIN] = {} hass.data[DOMAIN] = {}
@ -69,11 +75,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
async def async_update_data(): async def async_update_data():
"""Fetch data from Mazda API.""" """Fetch data from Mazda API."""
async def with_timeout(task):
async with async_timeout.timeout(10):
return await task
try: try:
vehicles = await with_timeout(mazda_client.get_vehicles()) vehicles = await with_timeout(mazda_client.get_vehicles())

View file

@ -40,15 +40,15 @@ class MazdaConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
if user_input is not None: if user_input is not None:
await self.async_set_unique_id(user_input[CONF_EMAIL].lower()) await self.async_set_unique_id(user_input[CONF_EMAIL].lower())
websession = aiohttp_client.async_get_clientsession(self.hass)
mazda_client = MazdaAPI(
user_input[CONF_EMAIL],
user_input[CONF_PASSWORD],
user_input[CONF_REGION],
websession,
)
try: try:
websession = aiohttp_client.async_get_clientsession(self.hass)
mazda_client = MazdaAPI(
user_input[CONF_EMAIL],
user_input[CONF_PASSWORD],
user_input[CONF_REGION],
websession,
)
await mazda_client.validate_credentials() await mazda_client.validate_credentials()
except MazdaAuthenticationException: except MazdaAuthenticationException:
errors["base"] = "invalid_auth" errors["base"] = "invalid_auth"

View file

@ -3,7 +3,7 @@
"name": "Mazda Connected Services", "name": "Mazda Connected Services",
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/mazda", "documentation": "https://www.home-assistant.io/integrations/mazda",
"requirements": ["pymazda==0.0.8"], "requirements": ["pymazda==0.0.9"],
"codeowners": ["@bdr99"], "codeowners": ["@bdr99"],
"quality_scale": "platinum" "quality_scale": "platinum"
} }

View file

@ -91,7 +91,13 @@ class MazdaFuelDistanceSensor(MazdaEntity):
fuel_distance_km = self.coordinator.data[self.index]["status"][ fuel_distance_km = self.coordinator.data[self.index]["status"][
"fuelDistanceRemainingKm" "fuelDistanceRemainingKm"
] ]
return round(self.hass.config.units.length(fuel_distance_km, LENGTH_KILOMETERS)) return (
None
if fuel_distance_km is None
else round(
self.hass.config.units.length(fuel_distance_km, LENGTH_KILOMETERS)
)
)
class MazdaOdometerSensor(MazdaEntity): class MazdaOdometerSensor(MazdaEntity):
@ -124,7 +130,11 @@ class MazdaOdometerSensor(MazdaEntity):
def state(self): def state(self):
"""Return the state of the sensor.""" """Return the state of the sensor."""
odometer_km = self.coordinator.data[self.index]["status"]["odometerKm"] odometer_km = self.coordinator.data[self.index]["status"]["odometerKm"]
return round(self.hass.config.units.length(odometer_km, LENGTH_KILOMETERS)) return (
None
if odometer_km is None
else round(self.hass.config.units.length(odometer_km, LENGTH_KILOMETERS))
)
class MazdaFrontLeftTirePressureSensor(MazdaEntity): class MazdaFrontLeftTirePressureSensor(MazdaEntity):
@ -154,11 +164,10 @@ class MazdaFrontLeftTirePressureSensor(MazdaEntity):
@property @property
def state(self): def state(self):
"""Return the state of the sensor.""" """Return the state of the sensor."""
return round( tire_pressure = self.coordinator.data[self.index]["status"]["tirePressure"][
self.coordinator.data[self.index]["status"]["tirePressure"][ "frontLeftTirePressurePsi"
"frontLeftTirePressurePsi" ]
] return None if tire_pressure is None else round(tire_pressure)
)
class MazdaFrontRightTirePressureSensor(MazdaEntity): class MazdaFrontRightTirePressureSensor(MazdaEntity):
@ -188,11 +197,10 @@ class MazdaFrontRightTirePressureSensor(MazdaEntity):
@property @property
def state(self): def state(self):
"""Return the state of the sensor.""" """Return the state of the sensor."""
return round( tire_pressure = self.coordinator.data[self.index]["status"]["tirePressure"][
self.coordinator.data[self.index]["status"]["tirePressure"][ "frontRightTirePressurePsi"
"frontRightTirePressurePsi" ]
] return None if tire_pressure is None else round(tire_pressure)
)
class MazdaRearLeftTirePressureSensor(MazdaEntity): class MazdaRearLeftTirePressureSensor(MazdaEntity):
@ -222,11 +230,10 @@ class MazdaRearLeftTirePressureSensor(MazdaEntity):
@property @property
def state(self): def state(self):
"""Return the state of the sensor.""" """Return the state of the sensor."""
return round( tire_pressure = self.coordinator.data[self.index]["status"]["tirePressure"][
self.coordinator.data[self.index]["status"]["tirePressure"][ "rearLeftTirePressurePsi"
"rearLeftTirePressurePsi" ]
] return None if tire_pressure is None else round(tire_pressure)
)
class MazdaRearRightTirePressureSensor(MazdaEntity): class MazdaRearRightTirePressureSensor(MazdaEntity):
@ -256,8 +263,7 @@ class MazdaRearRightTirePressureSensor(MazdaEntity):
@property @property
def state(self): def state(self):
"""Return the state of the sensor.""" """Return the state of the sensor."""
return round( tire_pressure = self.coordinator.data[self.index]["status"]["tirePressure"][
self.coordinator.data[self.index]["status"]["tirePressure"][ "rearRightTirePressurePsi"
"rearRightTirePressurePsi" ]
] return None if tire_pressure is None else round(tire_pressure)
)

View file

@ -1518,7 +1518,7 @@ pymailgunner==1.4
pymata-express==1.19 pymata-express==1.19
# homeassistant.components.mazda # homeassistant.components.mazda
pymazda==0.0.8 pymazda==0.0.9
# homeassistant.components.mediaroom # homeassistant.components.mediaroom
pymediaroom==0.6.4.1 pymediaroom==0.6.4.1

View file

@ -802,7 +802,7 @@ pymailgunner==1.4
pymata-express==1.19 pymata-express==1.19
# homeassistant.components.mazda # homeassistant.components.mazda
pymazda==0.0.8 pymazda==0.0.9
# homeassistant.components.melcloud # homeassistant.components.melcloud
pymelcloud==2.5.2 pymelcloud==2.5.2

View file

@ -1,18 +1,22 @@
"""Tests for the Mazda Connected Services integration.""" """Tests for the Mazda Connected Services integration."""
from datetime import timedelta
import json
from unittest.mock import patch from unittest.mock import patch
from pymazda import MazdaAuthenticationException, MazdaException from pymazda import MazdaAuthenticationException, MazdaException
from homeassistant.components.mazda.const import DATA_COORDINATOR, DOMAIN from homeassistant.components.mazda.const import DOMAIN
from homeassistant.config_entries import ( from homeassistant.config_entries import (
ENTRY_STATE_LOADED, ENTRY_STATE_LOADED,
ENTRY_STATE_NOT_LOADED,
ENTRY_STATE_SETUP_ERROR, ENTRY_STATE_SETUP_ERROR,
ENTRY_STATE_SETUP_RETRY, ENTRY_STATE_SETUP_RETRY,
) )
from homeassistant.const import CONF_EMAIL, CONF_PASSWORD, CONF_REGION from homeassistant.const import CONF_EMAIL, CONF_PASSWORD, CONF_REGION
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.util import dt as dt_util
from tests.common import MockConfigEntry from tests.common import MockConfigEntry, async_fire_time_changed, load_fixture
from tests.components.mazda import init_integration from tests.components.mazda import init_integration
FIXTURE_USER_INPUT = { FIXTURE_USER_INPUT = {
@ -60,10 +64,21 @@ async def test_init_auth_failure(hass: HomeAssistant):
async def test_update_auth_failure(hass: HomeAssistant): async def test_update_auth_failure(hass: HomeAssistant):
"""Test auth failure during data update.""" """Test auth failure during data update."""
get_vehicles_fixture = json.loads(load_fixture("mazda/get_vehicles.json"))
get_vehicle_status_fixture = json.loads(
load_fixture("mazda/get_vehicle_status.json")
)
with patch( with patch(
"homeassistant.components.mazda.MazdaAPI.validate_credentials", "homeassistant.components.mazda.MazdaAPI.validate_credentials",
return_value=True, return_value=True,
), patch("homeassistant.components.mazda.MazdaAPI.get_vehicles", return_value={}): ), patch(
"homeassistant.components.mazda.MazdaAPI.get_vehicles",
return_value=get_vehicles_fixture,
), patch(
"homeassistant.components.mazda.MazdaAPI.get_vehicle_status",
return_value=get_vehicle_status_fixture,
):
config_entry = MockConfigEntry(domain=DOMAIN, data=FIXTURE_USER_INPUT) config_entry = MockConfigEntry(domain=DOMAIN, data=FIXTURE_USER_INPUT)
config_entry.add_to_hass(hass) config_entry.add_to_hass(hass)
@ -74,15 +89,11 @@ async def test_update_auth_failure(hass: HomeAssistant):
assert len(entries) == 1 assert len(entries) == 1
assert entries[0].state == ENTRY_STATE_LOADED assert entries[0].state == ENTRY_STATE_LOADED
coordinator = hass.data[DOMAIN][config_entry.entry_id][DATA_COORDINATOR]
with patch( with patch(
"homeassistant.components.mazda.MazdaAPI.validate_credentials",
side_effect=MazdaAuthenticationException("Login failed"),
), patch(
"homeassistant.components.mazda.MazdaAPI.get_vehicles", "homeassistant.components.mazda.MazdaAPI.get_vehicles",
side_effect=MazdaAuthenticationException("Login failed"), side_effect=MazdaAuthenticationException("Login failed"),
): ):
await coordinator.async_refresh() async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=61))
await hass.async_block_till_done() await hass.async_block_till_done()
flows = hass.config_entries.flow.async_progress() flows = hass.config_entries.flow.async_progress()
@ -97,4 +108,4 @@ async def test_unload_config_entry(hass: HomeAssistant) -> None:
await hass.config_entries.async_unload(entry.entry_id) await hass.config_entries.async_unload(entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
assert not hass.data.get(DOMAIN) assert entry.state == ENTRY_STATE_NOT_LOADED