Add diagnostics to Subaru integration (#81169)

Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
Garrett 2022-10-29 16:02:32 -04:00 committed by GitHub
parent 7f047cf95c
commit 494cbf0dbe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 296 additions and 0 deletions

View file

@ -0,0 +1,56 @@
"""Diagnostics for the Subaru integration."""
from __future__ import annotations
from typing import Any
from subarulink.const import LATITUDE, LONGITUDE, ODOMETER, VEHICLE_NAME
from homeassistant.components.diagnostics.util import async_redact_data
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_DEVICE_ID, CONF_PASSWORD, CONF_PIN, CONF_USERNAME
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.device_registry import DeviceEntry
from .const import DOMAIN, ENTRY_COORDINATOR, VEHICLE_VIN
CONFIG_FIELDS_TO_REDACT = [CONF_USERNAME, CONF_PASSWORD, CONF_PIN, CONF_DEVICE_ID]
DATA_FIELDS_TO_REDACT = [VEHICLE_VIN, VEHICLE_NAME, LATITUDE, LONGITUDE, ODOMETER]
async def async_get_config_entry_diagnostics(
hass: HomeAssistant, config_entry: ConfigEntry
) -> dict[str, Any]:
"""Return diagnostics for a config entry."""
coordinator = hass.data[DOMAIN][config_entry.entry_id][ENTRY_COORDINATOR]
diagnostics_data = {
"config_entry": async_redact_data(config_entry.data, CONFIG_FIELDS_TO_REDACT),
"options": async_redact_data(config_entry.options, []),
"data": [
async_redact_data(info, DATA_FIELDS_TO_REDACT)
for info in coordinator.data.values()
],
}
return diagnostics_data
async def async_get_device_diagnostics(
hass: HomeAssistant, config_entry: ConfigEntry, device: DeviceEntry
) -> dict[str, Any]:
"""Return diagnostics for a device."""
coordinator = hass.data[DOMAIN][config_entry.entry_id][ENTRY_COORDINATOR]
vin = next(iter(device.identifiers))[1]
if info := coordinator.data.get(vin):
return {
"config_entry": async_redact_data(
config_entry.data, CONFIG_FIELDS_TO_REDACT
),
"options": async_redact_data(config_entry.options, []),
"data": async_redact_data(info, DATA_FIELDS_TO_REDACT),
}
raise HomeAssistantError("Device not found")

View file

@ -0,0 +1,82 @@
{
"config_entry": {
"username": "**REDACTED**",
"password": "**REDACTED**",
"country": "USA",
"pin": "**REDACTED**",
"device_id": "**REDACTED**"
},
"options": { "update_enabled": true },
"data": [
{
"status": {
"AVG_FUEL_CONSUMPTION": 2.3,
"BATTERY_VOLTAGE": 12.0,
"DISTANCE_TO_EMPTY_FUEL": 707,
"DOOR_BOOT_LOCK_STATUS": "UNKNOWN",
"DOOR_BOOT_POSITION": "CLOSED",
"DOOR_ENGINE_HOOD_LOCK_STATUS": "UNKNOWN",
"DOOR_ENGINE_HOOD_POSITION": "CLOSED",
"DOOR_FRONT_LEFT_LOCK_STATUS": "UNKNOWN",
"DOOR_FRONT_LEFT_POSITION": "CLOSED",
"DOOR_FRONT_RIGHT_LOCK_STATUS": "UNKNOWN",
"DOOR_FRONT_RIGHT_POSITION": "CLOSED",
"DOOR_REAR_LEFT_LOCK_STATUS": "UNKNOWN",
"DOOR_REAR_LEFT_POSITION": "CLOSED",
"DOOR_REAR_RIGHT_LOCK_STATUS": "UNKNOWN",
"DOOR_REAR_RIGHT_POSITION": "CLOSED",
"EV_CHARGER_STATE_TYPE": "CHARGING",
"EV_CHARGE_SETTING_AMPERE_TYPE": "MAXIMUM",
"EV_CHARGE_VOLT_TYPE": "CHARGE_LEVEL_1",
"EV_DISTANCE_TO_EMPTY": 1,
"EV_IS_PLUGGED_IN": "UNLOCKED_CONNECTED",
"EV_STATE_OF_CHARGE_MODE": "EV_MODE",
"EV_STATE_OF_CHARGE_PERCENT": 20,
"EV_TIME_TO_FULLY_CHARGED_UTC": "2020-07-24T03:06:40+00:00",
"EXT_EXTERNAL_TEMP": 21.5,
"ODOMETER": "**REDACTED**",
"POSITION_HEADING_DEGREE": 150,
"POSITION_SPEED_KMPH": "0",
"POSITION_TIMESTAMP": 1595560000.0,
"SEAT_BELT_STATUS_FRONT_LEFT": "BELTED",
"SEAT_BELT_STATUS_FRONT_MIDDLE": "NOT_EQUIPPED",
"SEAT_BELT_STATUS_FRONT_RIGHT": "BELTED",
"SEAT_BELT_STATUS_SECOND_LEFT": "UNKNOWN",
"SEAT_BELT_STATUS_SECOND_MIDDLE": "UNKNOWN",
"SEAT_BELT_STATUS_SECOND_RIGHT": "UNKNOWN",
"SEAT_BELT_STATUS_THIRD_LEFT": "UNKNOWN",
"SEAT_BELT_STATUS_THIRD_MIDDLE": "UNKNOWN",
"SEAT_BELT_STATUS_THIRD_RIGHT": "UNKNOWN",
"SEAT_OCCUPATION_STATUS_FRONT_LEFT": "UNKNOWN",
"SEAT_OCCUPATION_STATUS_FRONT_MIDDLE": "NOT_EQUIPPED",
"SEAT_OCCUPATION_STATUS_FRONT_RIGHT": "UNKNOWN",
"SEAT_OCCUPATION_STATUS_SECOND_LEFT": "UNKNOWN",
"SEAT_OCCUPATION_STATUS_SECOND_MIDDLE": "UNKNOWN",
"SEAT_OCCUPATION_STATUS_SECOND_RIGHT": "UNKNOWN",
"SEAT_OCCUPATION_STATUS_THIRD_LEFT": "UNKNOWN",
"SEAT_OCCUPATION_STATUS_THIRD_MIDDLE": "UNKNOWN",
"SEAT_OCCUPATION_STATUS_THIRD_RIGHT": "UNKNOWN",
"TIMESTAMP": 1595560000.0,
"TRANSMISSION_MODE": "UNKNOWN",
"TYRE_PRESSURE_FRONT_LEFT": 0,
"TYRE_PRESSURE_FRONT_RIGHT": 2550,
"TYRE_PRESSURE_REAR_LEFT": 2450,
"TYRE_PRESSURE_REAR_RIGHT": 2350,
"TYRE_STATUS_FRONT_LEFT": "UNKNOWN",
"TYRE_STATUS_FRONT_RIGHT": "UNKNOWN",
"TYRE_STATUS_REAR_LEFT": "UNKNOWN",
"TYRE_STATUS_REAR_RIGHT": "UNKNOWN",
"VEHICLE_STATE_TYPE": "IGNITION_OFF",
"WINDOW_BACK_STATUS": "UNKNOWN",
"WINDOW_FRONT_LEFT_STATUS": "VENTED",
"WINDOW_FRONT_RIGHT_STATUS": "VENTED",
"WINDOW_REAR_LEFT_STATUS": "UNKNOWN",
"WINDOW_REAR_RIGHT_STATUS": "UNKNOWN",
"WINDOW_SUNROOF_STATUS": "UNKNOWN",
"heading": 170,
"latitude": "**REDACTED**",
"longitude": "**REDACTED**"
}
}
]
}

View file

@ -0,0 +1,80 @@
{
"config_entry": {
"username": "**REDACTED**",
"password": "**REDACTED**",
"country": "USA",
"pin": "**REDACTED**",
"device_id": "**REDACTED**"
},
"options": { "update_enabled": true },
"data": {
"status": {
"AVG_FUEL_CONSUMPTION": 2.3,
"BATTERY_VOLTAGE": 12.0,
"DISTANCE_TO_EMPTY_FUEL": 707,
"DOOR_BOOT_LOCK_STATUS": "UNKNOWN",
"DOOR_BOOT_POSITION": "CLOSED",
"DOOR_ENGINE_HOOD_LOCK_STATUS": "UNKNOWN",
"DOOR_ENGINE_HOOD_POSITION": "CLOSED",
"DOOR_FRONT_LEFT_LOCK_STATUS": "UNKNOWN",
"DOOR_FRONT_LEFT_POSITION": "CLOSED",
"DOOR_FRONT_RIGHT_LOCK_STATUS": "UNKNOWN",
"DOOR_FRONT_RIGHT_POSITION": "CLOSED",
"DOOR_REAR_LEFT_LOCK_STATUS": "UNKNOWN",
"DOOR_REAR_LEFT_POSITION": "CLOSED",
"DOOR_REAR_RIGHT_LOCK_STATUS": "UNKNOWN",
"DOOR_REAR_RIGHT_POSITION": "CLOSED",
"EV_CHARGER_STATE_TYPE": "CHARGING",
"EV_CHARGE_SETTING_AMPERE_TYPE": "MAXIMUM",
"EV_CHARGE_VOLT_TYPE": "CHARGE_LEVEL_1",
"EV_DISTANCE_TO_EMPTY": 1,
"EV_IS_PLUGGED_IN": "UNLOCKED_CONNECTED",
"EV_STATE_OF_CHARGE_MODE": "EV_MODE",
"EV_STATE_OF_CHARGE_PERCENT": 20,
"EV_TIME_TO_FULLY_CHARGED_UTC": "2020-07-24T03:06:40+00:00",
"EXT_EXTERNAL_TEMP": 21.5,
"ODOMETER": "**REDACTED**",
"POSITION_HEADING_DEGREE": 150,
"POSITION_SPEED_KMPH": "0",
"POSITION_TIMESTAMP": 1595560000.0,
"SEAT_BELT_STATUS_FRONT_LEFT": "BELTED",
"SEAT_BELT_STATUS_FRONT_MIDDLE": "NOT_EQUIPPED",
"SEAT_BELT_STATUS_FRONT_RIGHT": "BELTED",
"SEAT_BELT_STATUS_SECOND_LEFT": "UNKNOWN",
"SEAT_BELT_STATUS_SECOND_MIDDLE": "UNKNOWN",
"SEAT_BELT_STATUS_SECOND_RIGHT": "UNKNOWN",
"SEAT_BELT_STATUS_THIRD_LEFT": "UNKNOWN",
"SEAT_BELT_STATUS_THIRD_MIDDLE": "UNKNOWN",
"SEAT_BELT_STATUS_THIRD_RIGHT": "UNKNOWN",
"SEAT_OCCUPATION_STATUS_FRONT_LEFT": "UNKNOWN",
"SEAT_OCCUPATION_STATUS_FRONT_MIDDLE": "NOT_EQUIPPED",
"SEAT_OCCUPATION_STATUS_FRONT_RIGHT": "UNKNOWN",
"SEAT_OCCUPATION_STATUS_SECOND_LEFT": "UNKNOWN",
"SEAT_OCCUPATION_STATUS_SECOND_MIDDLE": "UNKNOWN",
"SEAT_OCCUPATION_STATUS_SECOND_RIGHT": "UNKNOWN",
"SEAT_OCCUPATION_STATUS_THIRD_LEFT": "UNKNOWN",
"SEAT_OCCUPATION_STATUS_THIRD_MIDDLE": "UNKNOWN",
"SEAT_OCCUPATION_STATUS_THIRD_RIGHT": "UNKNOWN",
"TIMESTAMP": 1595560000.0,
"TRANSMISSION_MODE": "UNKNOWN",
"TYRE_PRESSURE_FRONT_LEFT": 0,
"TYRE_PRESSURE_FRONT_RIGHT": 2550,
"TYRE_PRESSURE_REAR_LEFT": 2450,
"TYRE_PRESSURE_REAR_RIGHT": 2350,
"TYRE_STATUS_FRONT_LEFT": "UNKNOWN",
"TYRE_STATUS_FRONT_RIGHT": "UNKNOWN",
"TYRE_STATUS_REAR_LEFT": "UNKNOWN",
"TYRE_STATUS_REAR_RIGHT": "UNKNOWN",
"VEHICLE_STATE_TYPE": "IGNITION_OFF",
"WINDOW_BACK_STATUS": "UNKNOWN",
"WINDOW_FRONT_LEFT_STATUS": "VENTED",
"WINDOW_FRONT_RIGHT_STATUS": "VENTED",
"WINDOW_REAR_LEFT_STATUS": "UNKNOWN",
"WINDOW_REAR_RIGHT_STATUS": "UNKNOWN",
"WINDOW_SUNROOF_STATUS": "UNKNOWN",
"heading": 170,
"latitude": "**REDACTED**",
"longitude": "**REDACTED**"
}
}
}

View file

@ -0,0 +1,78 @@
"""Test Subaru diagnostics."""
import json
from unittest.mock import patch
import pytest
from homeassistant.components.subaru.const import DOMAIN
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr
from .api_responses import TEST_VIN_2_EV
from tests.common import load_fixture
from tests.components.diagnostics import (
get_diagnostics_for_config_entry,
get_diagnostics_for_device,
)
from tests.components.subaru.conftest import (
MOCK_API_FETCH,
MOCK_API_GET_DATA,
advance_time_to_next_fetch,
)
async def test_config_entry_diagnostics(hass: HomeAssistant, hass_client, ev_entry):
"""Test config entry diagnostics."""
config_entry = hass.config_entries.async_entries(DOMAIN)[0]
diagnostics_fixture = json.loads(
load_fixture("subaru/diagnostics_config_entry.json")
)
assert (
await get_diagnostics_for_config_entry(hass, hass_client, config_entry)
== diagnostics_fixture
)
async def test_device_diagnostics(hass: HomeAssistant, hass_client, ev_entry):
"""Test device diagnostics."""
config_entry = hass.config_entries.async_entries(DOMAIN)[0]
device_registry = dr.async_get(hass)
reg_device = device_registry.async_get_device(
identifiers={(DOMAIN, TEST_VIN_2_EV)},
)
assert reg_device is not None
diagnostics_fixture = json.loads(load_fixture("subaru/diagnostics_device.json"))
assert (
await get_diagnostics_for_device(hass, hass_client, config_entry, reg_device)
== diagnostics_fixture
)
async def test_device_diagnostics_vehicle_not_found(
hass: HomeAssistant, hass_client, ev_entry
):
"""Test device diagnostics when the vehicle cannot be found."""
config_entry = hass.config_entries.async_entries(DOMAIN)[0]
device_registry = dr.async_get(hass)
reg_device = device_registry.async_get_device(
identifiers={(DOMAIN, TEST_VIN_2_EV)},
)
assert reg_device is not None
# Simulate case where Subaru API does not return vehicle data
with patch(MOCK_API_FETCH), patch(MOCK_API_GET_DATA, return_value=None):
advance_time_to_next_fetch(hass)
await hass.async_block_till_done()
with pytest.raises(AssertionError):
await get_diagnostics_for_device(hass, hass_client, config_entry, reg_device)