mirror of
https://github.com/home-assistant/core.git
synced 2025-07-25 06:07:17 +00:00
Update sensors for Subaru integration (#66996)
* Update sensor.py * Change "EV Time to Fully Charged" type to datetime object (HA 2022.2) * Validate types before accessing dict entries * Test handling of invalid data from Subaru * Bump to subarulink 0.4.2 * Incorporate style suggestion * Update sensor.py to use SensorEntity * isort tests * Remove SubaruSensor.current_value * Fix isort errors * Resolve conflict from previous PR (add locks) * Fix linting errors in config_flow.py * Incorporate PR review comments for sensor * Incorporate PR review comments for sensor * Make 3rd party library responsible for API data parsing * Add type annotations to sensor.py * Incorporate PR review comments * Incorporate PR review comments * Set _attr_has_entity_name = True for sensors
This commit is contained in:
parent
35fa73eee9
commit
9058b5b9c3
@ -3,6 +3,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import logging
|
import logging
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from subarulink import (
|
from subarulink import (
|
||||||
Controller as SubaruAPI,
|
Controller as SubaruAPI,
|
||||||
@ -16,6 +17,7 @@ import voluptuous as vol
|
|||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
from homeassistant.const import CONF_DEVICE_ID, CONF_PASSWORD, CONF_PIN, CONF_USERNAME
|
from homeassistant.const import CONF_DEVICE_ID, CONF_PASSWORD, CONF_PIN, CONF_USERNAME
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
|
from homeassistant.data_entry_flow import FlowResult
|
||||||
from homeassistant.helpers import aiohttp_client, config_validation as cv
|
from homeassistant.helpers import aiohttp_client, config_validation as cv
|
||||||
|
|
||||||
from .const import CONF_COUNTRY, CONF_UPDATE_ENABLED, DOMAIN
|
from .const import CONF_COUNTRY, CONF_UPDATE_ENABLED, DOMAIN
|
||||||
@ -36,7 +38,9 @@ class SubaruConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
self.config_data = {CONF_PIN: None}
|
self.config_data = {CONF_PIN: None}
|
||||||
self.controller = None
|
self.controller = None
|
||||||
|
|
||||||
async def async_step_user(self, user_input=None):
|
async def async_step_user(
|
||||||
|
self, user_input: dict[str, Any] | None = None
|
||||||
|
) -> FlowResult:
|
||||||
"""Handle the start of the config flow."""
|
"""Handle the start of the config flow."""
|
||||||
error = None
|
error = None
|
||||||
|
|
||||||
@ -117,7 +121,9 @@ class SubaruConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
_LOGGER.debug("Successfully authenticated with Subaru API")
|
_LOGGER.debug("Successfully authenticated with Subaru API")
|
||||||
self.config_data.update(data)
|
self.config_data.update(data)
|
||||||
|
|
||||||
async def async_step_two_factor(self, user_input=None):
|
async def async_step_two_factor(
|
||||||
|
self, user_input: dict[str, Any] | None = None
|
||||||
|
) -> FlowResult:
|
||||||
"""Select contact method and request 2FA code from Subaru."""
|
"""Select contact method and request 2FA code from Subaru."""
|
||||||
error = None
|
error = None
|
||||||
if user_input:
|
if user_input:
|
||||||
@ -143,7 +149,9 @@ class SubaruConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
step_id="two_factor", data_schema=data_schema, errors=error
|
step_id="two_factor", data_schema=data_schema, errors=error
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_step_two_factor_validate(self, user_input=None):
|
async def async_step_two_factor_validate(
|
||||||
|
self, user_input: dict[str, Any] | None = None
|
||||||
|
) -> FlowResult:
|
||||||
"""Validate received 2FA code with Subaru."""
|
"""Validate received 2FA code with Subaru."""
|
||||||
error = None
|
error = None
|
||||||
if user_input:
|
if user_input:
|
||||||
@ -166,7 +174,9 @@ class SubaruConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
step_id="two_factor_validate", data_schema=data_schema, errors=error
|
step_id="two_factor_validate", data_schema=data_schema, errors=error
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_step_pin(self, user_input=None):
|
async def async_step_pin(
|
||||||
|
self, user_input: dict[str, Any] | None = None
|
||||||
|
) -> FlowResult:
|
||||||
"""Handle second part of config flow, if required."""
|
"""Handle second part of config flow, if required."""
|
||||||
error = None
|
error = None
|
||||||
if user_input and self.controller.update_saved_pin(user_input[CONF_PIN]):
|
if user_input and self.controller.update_saved_pin(user_input[CONF_PIN]):
|
||||||
@ -193,7 +203,9 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
"""Initialize options flow."""
|
"""Initialize options flow."""
|
||||||
self.config_entry = config_entry
|
self.config_entry = config_entry
|
||||||
|
|
||||||
async def async_step_init(self, user_input=None):
|
async def async_step_init(
|
||||||
|
self, user_input: dict[str, Any] | None = None
|
||||||
|
) -> FlowResult:
|
||||||
"""Handle options flow."""
|
"""Handle options flow."""
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
return self.async_create_entry(title="", data=user_input)
|
return self.async_create_entry(title="", data=user_input)
|
||||||
|
@ -31,7 +31,7 @@ VEHICLE_STATUS = "status"
|
|||||||
|
|
||||||
API_GEN_1 = "g1"
|
API_GEN_1 = "g1"
|
||||||
API_GEN_2 = "g2"
|
API_GEN_2 = "g2"
|
||||||
MANUFACTURER = "Subaru Corp."
|
MANUFACTURER = "Subaru"
|
||||||
|
|
||||||
PLATFORMS = [
|
PLATFORMS = [
|
||||||
Platform.LOCK,
|
Platform.LOCK,
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
"""Base class for all Subaru Entities."""
|
|
||||||
from homeassistant.helpers.entity import DeviceInfo
|
|
||||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
|
||||||
|
|
||||||
from .const import DOMAIN, MANUFACTURER, VEHICLE_NAME, VEHICLE_VIN
|
|
||||||
|
|
||||||
|
|
||||||
class SubaruEntity(CoordinatorEntity):
|
|
||||||
"""Representation of a Subaru Entity."""
|
|
||||||
|
|
||||||
def __init__(self, vehicle_info, coordinator):
|
|
||||||
"""Initialize the Subaru Entity."""
|
|
||||||
super().__init__(coordinator)
|
|
||||||
self.car_name = vehicle_info[VEHICLE_NAME]
|
|
||||||
self.vin = vehicle_info[VEHICLE_VIN]
|
|
||||||
self.entity_type = "entity"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def name(self):
|
|
||||||
"""Return name."""
|
|
||||||
return f"{self.car_name} {self.entity_type}"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def unique_id(self) -> str:
|
|
||||||
"""Return a unique ID."""
|
|
||||||
return f"{self.vin}_{self.entity_type}"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def device_info(self) -> DeviceInfo:
|
|
||||||
"""Return the device_info of the device."""
|
|
||||||
return DeviceInfo(
|
|
||||||
identifiers={(DOMAIN, self.vin)},
|
|
||||||
manufacturer=MANUFACTURER,
|
|
||||||
name=self.car_name,
|
|
||||||
)
|
|
@ -3,7 +3,7 @@
|
|||||||
"name": "Subaru",
|
"name": "Subaru",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/subaru",
|
"documentation": "https://www.home-assistant.io/integrations/subaru",
|
||||||
"requirements": ["subarulink==0.5.0"],
|
"requirements": ["subarulink==0.6.0"],
|
||||||
"codeowners": ["@G-Two"],
|
"codeowners": ["@G-Two"],
|
||||||
"iot_class": "cloud_polling",
|
"iot_class": "cloud_polling",
|
||||||
"loggers": ["stdiomask", "subarulink"]
|
"loggers": ["stdiomask", "subarulink"]
|
||||||
|
@ -1,10 +1,16 @@
|
|||||||
"""Support for Subaru sensors."""
|
"""Support for Subaru sensors."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
import subarulink.const as sc
|
import subarulink.const as sc
|
||||||
|
|
||||||
from homeassistant.components.sensor import (
|
from homeassistant.components.sensor import (
|
||||||
DEVICE_CLASSES,
|
|
||||||
SensorDeviceClass,
|
SensorDeviceClass,
|
||||||
SensorEntity,
|
SensorEntity,
|
||||||
|
SensorEntityDescription,
|
||||||
|
SensorStateClass,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
@ -14,128 +20,133 @@ from homeassistant.const import (
|
|||||||
PERCENTAGE,
|
PERCENTAGE,
|
||||||
PRESSURE_HPA,
|
PRESSURE_HPA,
|
||||||
TEMP_CELSIUS,
|
TEMP_CELSIUS,
|
||||||
TIME_MINUTES,
|
|
||||||
VOLUME_GALLONS,
|
VOLUME_GALLONS,
|
||||||
VOLUME_LITERS,
|
VOLUME_LITERS,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.util.unit_conversion import DistanceConverter, VolumeConverter
|
from homeassistant.helpers.update_coordinator import (
|
||||||
from homeassistant.util.unit_system import (
|
CoordinatorEntity,
|
||||||
IMPERIAL_SYSTEM,
|
DataUpdateCoordinator,
|
||||||
LENGTH_UNITS,
|
|
||||||
PRESSURE_UNITS,
|
|
||||||
TEMPERATURE_UNITS,
|
|
||||||
)
|
)
|
||||||
|
from homeassistant.util.unit_conversion import DistanceConverter, VolumeConverter
|
||||||
|
from homeassistant.util.unit_system import IMPERIAL_SYSTEM, LENGTH_UNITS, PRESSURE_UNITS
|
||||||
|
|
||||||
|
from . import get_device_info
|
||||||
from .const import (
|
from .const import (
|
||||||
API_GEN_2,
|
API_GEN_2,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
ENTRY_COORDINATOR,
|
ENTRY_COORDINATOR,
|
||||||
ENTRY_VEHICLES,
|
ENTRY_VEHICLES,
|
||||||
ICONS,
|
|
||||||
VEHICLE_API_GEN,
|
VEHICLE_API_GEN,
|
||||||
VEHICLE_HAS_EV,
|
VEHICLE_HAS_EV,
|
||||||
VEHICLE_HAS_SAFETY_SERVICE,
|
VEHICLE_HAS_SAFETY_SERVICE,
|
||||||
VEHICLE_STATUS,
|
VEHICLE_STATUS,
|
||||||
|
VEHICLE_VIN,
|
||||||
)
|
)
|
||||||
from .entity import SubaruEntity
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Fuel consumption units
|
||||||
|
FUEL_CONSUMPTION_LITERS_PER_HUNDRED_KILOMETERS = "L/100km"
|
||||||
|
FUEL_CONSUMPTION_MILES_PER_GALLON = "mi/gal"
|
||||||
|
|
||||||
L_PER_GAL = VolumeConverter.convert(1, VOLUME_GALLONS, VOLUME_LITERS)
|
L_PER_GAL = VolumeConverter.convert(1, VOLUME_GALLONS, VOLUME_LITERS)
|
||||||
KM_PER_MI = DistanceConverter.convert(1, LENGTH_MILES, LENGTH_KILOMETERS)
|
KM_PER_MI = DistanceConverter.convert(1, LENGTH_MILES, LENGTH_KILOMETERS)
|
||||||
|
|
||||||
# Fuel Economy Constants
|
# Sensor available to "Subaru Safety Plus" subscribers with Gen1 or Gen2 vehicles
|
||||||
FUEL_CONSUMPTION_L_PER_100KM = "L/100km"
|
|
||||||
FUEL_CONSUMPTION_MPG = "mi/gal"
|
|
||||||
FUEL_CONSUMPTION_UNITS = [FUEL_CONSUMPTION_L_PER_100KM, FUEL_CONSUMPTION_MPG]
|
|
||||||
|
|
||||||
SENSOR_TYPE = "type"
|
|
||||||
SENSOR_CLASS = "class"
|
|
||||||
SENSOR_FIELD = "field"
|
|
||||||
SENSOR_UNITS = "units"
|
|
||||||
|
|
||||||
# Sensor data available to "Subaru Safety Plus" subscribers with Gen1 or Gen2 vehicles
|
|
||||||
SAFETY_SENSORS = [
|
SAFETY_SENSORS = [
|
||||||
{
|
SensorEntityDescription(
|
||||||
SENSOR_TYPE: "Odometer",
|
key=sc.ODOMETER,
|
||||||
SENSOR_CLASS: None,
|
icon="mdi:road-variant",
|
||||||
SENSOR_FIELD: sc.ODOMETER,
|
name="Odometer",
|
||||||
SENSOR_UNITS: LENGTH_KILOMETERS,
|
native_unit_of_measurement=LENGTH_KILOMETERS,
|
||||||
},
|
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
# Sensor data available to "Subaru Safety Plus" subscribers with Gen2 vehicles
|
# Sensors available to "Subaru Safety Plus" subscribers with Gen2 vehicles
|
||||||
API_GEN_2_SENSORS = [
|
API_GEN_2_SENSORS = [
|
||||||
{
|
SensorEntityDescription(
|
||||||
SENSOR_TYPE: "Avg Fuel Consumption",
|
key=sc.AVG_FUEL_CONSUMPTION,
|
||||||
SENSOR_CLASS: None,
|
icon="mdi:leaf",
|
||||||
SENSOR_FIELD: sc.AVG_FUEL_CONSUMPTION,
|
name="Avg Fuel Consumption",
|
||||||
SENSOR_UNITS: FUEL_CONSUMPTION_L_PER_100KM,
|
native_unit_of_measurement=FUEL_CONSUMPTION_LITERS_PER_HUNDRED_KILOMETERS,
|
||||||
},
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
{
|
),
|
||||||
SENSOR_TYPE: "Range",
|
SensorEntityDescription(
|
||||||
SENSOR_CLASS: None,
|
key=sc.DIST_TO_EMPTY,
|
||||||
SENSOR_FIELD: sc.DIST_TO_EMPTY,
|
icon="mdi:gas-station",
|
||||||
SENSOR_UNITS: LENGTH_KILOMETERS,
|
name="Range",
|
||||||
},
|
native_unit_of_measurement=LENGTH_KILOMETERS,
|
||||||
{
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
SENSOR_TYPE: "Tire Pressure FL",
|
),
|
||||||
SENSOR_CLASS: SensorDeviceClass.PRESSURE,
|
SensorEntityDescription(
|
||||||
SENSOR_FIELD: sc.TIRE_PRESSURE_FL,
|
key=sc.TIRE_PRESSURE_FL,
|
||||||
SENSOR_UNITS: PRESSURE_HPA,
|
device_class=SensorDeviceClass.PRESSURE,
|
||||||
},
|
name="Tire Pressure FL",
|
||||||
{
|
native_unit_of_measurement=PRESSURE_HPA,
|
||||||
SENSOR_TYPE: "Tire Pressure FR",
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
SENSOR_CLASS: SensorDeviceClass.PRESSURE,
|
),
|
||||||
SENSOR_FIELD: sc.TIRE_PRESSURE_FR,
|
SensorEntityDescription(
|
||||||
SENSOR_UNITS: PRESSURE_HPA,
|
key=sc.TIRE_PRESSURE_FR,
|
||||||
},
|
device_class=SensorDeviceClass.PRESSURE,
|
||||||
{
|
name="Tire Pressure FR",
|
||||||
SENSOR_TYPE: "Tire Pressure RL",
|
native_unit_of_measurement=PRESSURE_HPA,
|
||||||
SENSOR_CLASS: SensorDeviceClass.PRESSURE,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
SENSOR_FIELD: sc.TIRE_PRESSURE_RL,
|
),
|
||||||
SENSOR_UNITS: PRESSURE_HPA,
|
SensorEntityDescription(
|
||||||
},
|
key=sc.TIRE_PRESSURE_RL,
|
||||||
{
|
device_class=SensorDeviceClass.PRESSURE,
|
||||||
SENSOR_TYPE: "Tire Pressure RR",
|
name="Tire Pressure RL",
|
||||||
SENSOR_CLASS: SensorDeviceClass.PRESSURE,
|
native_unit_of_measurement=PRESSURE_HPA,
|
||||||
SENSOR_FIELD: sc.TIRE_PRESSURE_RR,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
SENSOR_UNITS: PRESSURE_HPA,
|
),
|
||||||
},
|
SensorEntityDescription(
|
||||||
{
|
key=sc.TIRE_PRESSURE_RR,
|
||||||
SENSOR_TYPE: "External Temp",
|
device_class=SensorDeviceClass.PRESSURE,
|
||||||
SENSOR_CLASS: SensorDeviceClass.TEMPERATURE,
|
name="Tire Pressure RR",
|
||||||
SENSOR_FIELD: sc.EXTERNAL_TEMP,
|
native_unit_of_measurement=PRESSURE_HPA,
|
||||||
SENSOR_UNITS: TEMP_CELSIUS,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
},
|
),
|
||||||
{
|
SensorEntityDescription(
|
||||||
SENSOR_TYPE: "12V Battery Voltage",
|
key=sc.EXTERNAL_TEMP,
|
||||||
SENSOR_CLASS: SensorDeviceClass.VOLTAGE,
|
device_class=SensorDeviceClass.TEMPERATURE,
|
||||||
SENSOR_FIELD: sc.BATTERY_VOLTAGE,
|
name="External Temp",
|
||||||
SENSOR_UNITS: ELECTRIC_POTENTIAL_VOLT,
|
native_unit_of_measurement=TEMP_CELSIUS,
|
||||||
},
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
|
),
|
||||||
|
SensorEntityDescription(
|
||||||
|
key=sc.BATTERY_VOLTAGE,
|
||||||
|
device_class=SensorDeviceClass.VOLTAGE,
|
||||||
|
name="12V Battery Voltage",
|
||||||
|
native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT,
|
||||||
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
# Sensor data available to "Subaru Safety Plus" subscribers with PHEV vehicles
|
# Sensors available to "Subaru Safety Plus" subscribers with PHEV vehicles
|
||||||
EV_SENSORS = [
|
EV_SENSORS = [
|
||||||
{
|
SensorEntityDescription(
|
||||||
SENSOR_TYPE: "EV Range",
|
key=sc.EV_DISTANCE_TO_EMPTY,
|
||||||
SENSOR_CLASS: None,
|
icon="mdi:ev-station",
|
||||||
SENSOR_FIELD: sc.EV_DISTANCE_TO_EMPTY,
|
name="EV Range",
|
||||||
SENSOR_UNITS: LENGTH_MILES,
|
native_unit_of_measurement=LENGTH_MILES,
|
||||||
},
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
{
|
),
|
||||||
SENSOR_TYPE: "EV Battery Level",
|
SensorEntityDescription(
|
||||||
SENSOR_CLASS: SensorDeviceClass.BATTERY,
|
key=sc.EV_STATE_OF_CHARGE_PERCENT,
|
||||||
SENSOR_FIELD: sc.EV_STATE_OF_CHARGE_PERCENT,
|
device_class=SensorDeviceClass.BATTERY,
|
||||||
SENSOR_UNITS: PERCENTAGE,
|
name="EV Battery Level",
|
||||||
},
|
native_unit_of_measurement=PERCENTAGE,
|
||||||
{
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
SENSOR_TYPE: "EV Time to Full Charge",
|
),
|
||||||
SENSOR_CLASS: SensorDeviceClass.TIMESTAMP,
|
SensorEntityDescription(
|
||||||
SENSOR_FIELD: sc.EV_TIME_TO_FULLY_CHARGED,
|
key=sc.EV_TIME_TO_FULLY_CHARGED_UTC,
|
||||||
SENSOR_UNITS: TIME_MINUTES,
|
device_class=SensorDeviceClass.TIMESTAMP,
|
||||||
},
|
name="EV Time to Full Charge",
|
||||||
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -145,123 +156,111 @@ async def async_setup_entry(
|
|||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the Subaru sensors by config_entry."""
|
"""Set up the Subaru sensors by config_entry."""
|
||||||
coordinator = hass.data[DOMAIN][config_entry.entry_id][ENTRY_COORDINATOR]
|
entry = hass.data[DOMAIN][config_entry.entry_id]
|
||||||
vehicle_info = hass.data[DOMAIN][config_entry.entry_id][ENTRY_VEHICLES]
|
coordinator = entry[ENTRY_COORDINATOR]
|
||||||
|
vehicle_info = entry[ENTRY_VEHICLES]
|
||||||
entities = []
|
entities = []
|
||||||
for vin in vehicle_info:
|
for info in vehicle_info.values():
|
||||||
entities.extend(create_vehicle_sensors(vehicle_info[vin], coordinator))
|
entities.extend(create_vehicle_sensors(info, coordinator))
|
||||||
async_add_entities(entities, True)
|
async_add_entities(entities)
|
||||||
|
|
||||||
|
|
||||||
def create_vehicle_sensors(vehicle_info, coordinator):
|
def create_vehicle_sensors(
|
||||||
|
vehicle_info, coordinator: DataUpdateCoordinator
|
||||||
|
) -> list[SubaruSensor]:
|
||||||
"""Instantiate all available sensors for the vehicle."""
|
"""Instantiate all available sensors for the vehicle."""
|
||||||
sensors_to_add = []
|
sensor_descriptions_to_add = []
|
||||||
if vehicle_info[VEHICLE_HAS_SAFETY_SERVICE]:
|
if vehicle_info[VEHICLE_HAS_SAFETY_SERVICE]:
|
||||||
sensors_to_add.extend(SAFETY_SENSORS)
|
sensor_descriptions_to_add.extend(SAFETY_SENSORS)
|
||||||
|
|
||||||
if vehicle_info[VEHICLE_API_GEN] == API_GEN_2:
|
if vehicle_info[VEHICLE_API_GEN] == API_GEN_2:
|
||||||
sensors_to_add.extend(API_GEN_2_SENSORS)
|
sensor_descriptions_to_add.extend(API_GEN_2_SENSORS)
|
||||||
|
|
||||||
if vehicle_info[VEHICLE_HAS_EV]:
|
if vehicle_info[VEHICLE_HAS_EV]:
|
||||||
sensors_to_add.extend(EV_SENSORS)
|
sensor_descriptions_to_add.extend(EV_SENSORS)
|
||||||
|
|
||||||
return [
|
return [
|
||||||
SubaruSensor(
|
SubaruSensor(
|
||||||
vehicle_info,
|
vehicle_info,
|
||||||
coordinator,
|
coordinator,
|
||||||
s[SENSOR_TYPE],
|
description,
|
||||||
s[SENSOR_CLASS],
|
|
||||||
s[SENSOR_FIELD],
|
|
||||||
s[SENSOR_UNITS],
|
|
||||||
)
|
)
|
||||||
for s in sensors_to_add
|
for description in sensor_descriptions_to_add
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class SubaruSensor(SubaruEntity, SensorEntity):
|
class SubaruSensor(
|
||||||
|
CoordinatorEntity[DataUpdateCoordinator[dict[str, Any]]], SensorEntity
|
||||||
|
):
|
||||||
"""Class for Subaru sensors."""
|
"""Class for Subaru sensors."""
|
||||||
|
|
||||||
|
_attr_has_entity_name = True
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, vehicle_info, coordinator, entity_type, sensor_class, data_field, api_unit
|
self,
|
||||||
):
|
vehicle_info: dict,
|
||||||
|
coordinator: DataUpdateCoordinator,
|
||||||
|
description: SensorEntityDescription,
|
||||||
|
) -> None:
|
||||||
"""Initialize the sensor."""
|
"""Initialize the sensor."""
|
||||||
super().__init__(vehicle_info, coordinator)
|
super().__init__(coordinator)
|
||||||
self.hass_type = "sensor"
|
self.vin = vehicle_info[VEHICLE_VIN]
|
||||||
self.current_value = None
|
self.entity_description = description
|
||||||
self.entity_type = entity_type
|
self._attr_device_info = get_device_info(vehicle_info)
|
||||||
self.sensor_class = sensor_class
|
self._attr_unique_id = f"{self.vin}_{description.name}"
|
||||||
self.data_field = data_field
|
|
||||||
self.api_unit = api_unit
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def device_class(self):
|
def native_value(self) -> None | int | float:
|
||||||
"""Return the class of this device, from component DEVICE_CLASSES."""
|
|
||||||
if self.sensor_class in DEVICE_CLASSES:
|
|
||||||
return self.sensor_class
|
|
||||||
return None
|
|
||||||
|
|
||||||
@property
|
|
||||||
def icon(self):
|
|
||||||
"""Return the icon of the sensor."""
|
|
||||||
if not self.device_class:
|
|
||||||
return ICONS.get(self.entity_type)
|
|
||||||
return None
|
|
||||||
|
|
||||||
@property
|
|
||||||
def native_value(self):
|
|
||||||
"""Return the state of the sensor."""
|
"""Return the state of the sensor."""
|
||||||
self.current_value = self.get_current_value()
|
vehicle_data = self.coordinator.data[self.vin]
|
||||||
|
current_value = vehicle_data[VEHICLE_STATUS].get(self.entity_description.key)
|
||||||
|
unit = self.entity_description.native_unit_of_measurement
|
||||||
|
unit_system = self.hass.config.units
|
||||||
|
|
||||||
if self.current_value is None:
|
if current_value is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if self.api_unit in TEMPERATURE_UNITS:
|
if unit in LENGTH_UNITS:
|
||||||
return round(
|
return round(unit_system.length(current_value, unit), 1)
|
||||||
self.hass.config.units.temperature(self.current_value, self.api_unit), 1
|
|
||||||
)
|
|
||||||
|
|
||||||
if self.api_unit in LENGTH_UNITS:
|
if unit in PRESSURE_UNITS and unit_system == IMPERIAL_SYSTEM:
|
||||||
return round(
|
return round(
|
||||||
self.hass.config.units.length(self.current_value, self.api_unit), 1
|
unit_system.pressure(current_value, unit),
|
||||||
)
|
|
||||||
|
|
||||||
if (
|
|
||||||
self.api_unit in PRESSURE_UNITS
|
|
||||||
and self.hass.config.units == IMPERIAL_SYSTEM
|
|
||||||
):
|
|
||||||
return round(
|
|
||||||
self.hass.config.units.pressure(self.current_value, self.api_unit),
|
|
||||||
1,
|
1,
|
||||||
)
|
)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
self.api_unit in FUEL_CONSUMPTION_UNITS
|
unit
|
||||||
and self.hass.config.units == IMPERIAL_SYSTEM
|
in [
|
||||||
|
FUEL_CONSUMPTION_LITERS_PER_HUNDRED_KILOMETERS,
|
||||||
|
FUEL_CONSUMPTION_MILES_PER_GALLON,
|
||||||
|
]
|
||||||
|
and unit_system == IMPERIAL_SYSTEM
|
||||||
):
|
):
|
||||||
return round((100.0 * L_PER_GAL) / (KM_PER_MI * self.current_value), 1)
|
return round((100.0 * L_PER_GAL) / (KM_PER_MI * current_value), 1)
|
||||||
|
|
||||||
return self.current_value
|
return current_value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def native_unit_of_measurement(self):
|
def native_unit_of_measurement(self) -> str | None:
|
||||||
"""Return the unit_of_measurement of the device."""
|
"""Return the unit_of_measurement of the device."""
|
||||||
if self.api_unit in TEMPERATURE_UNITS:
|
unit = self.entity_description.native_unit_of_measurement
|
||||||
return self.hass.config.units.temperature_unit
|
|
||||||
|
|
||||||
if self.api_unit in LENGTH_UNITS:
|
if unit in LENGTH_UNITS:
|
||||||
return self.hass.config.units.length_unit
|
return self.hass.config.units.length_unit
|
||||||
|
|
||||||
if self.api_unit in PRESSURE_UNITS:
|
if unit in PRESSURE_UNITS:
|
||||||
if self.hass.config.units == IMPERIAL_SYSTEM:
|
if self.hass.config.units == IMPERIAL_SYSTEM:
|
||||||
return self.hass.config.units.pressure_unit
|
return self.hass.config.units.pressure_unit
|
||||||
return PRESSURE_HPA
|
|
||||||
|
|
||||||
if self.api_unit in FUEL_CONSUMPTION_UNITS:
|
if unit in [
|
||||||
|
FUEL_CONSUMPTION_LITERS_PER_HUNDRED_KILOMETERS,
|
||||||
|
FUEL_CONSUMPTION_MILES_PER_GALLON,
|
||||||
|
]:
|
||||||
if self.hass.config.units == IMPERIAL_SYSTEM:
|
if self.hass.config.units == IMPERIAL_SYSTEM:
|
||||||
return FUEL_CONSUMPTION_MPG
|
return FUEL_CONSUMPTION_MILES_PER_GALLON
|
||||||
return FUEL_CONSUMPTION_L_PER_100KM
|
|
||||||
|
|
||||||
return self.api_unit
|
return unit
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def available(self) -> bool:
|
def available(self) -> bool:
|
||||||
@ -270,15 +269,3 @@ class SubaruSensor(SubaruEntity, SensorEntity):
|
|||||||
if last_update_success and self.vin not in self.coordinator.data:
|
if last_update_success and self.vin not in self.coordinator.data:
|
||||||
return False
|
return False
|
||||||
return last_update_success
|
return last_update_success
|
||||||
|
|
||||||
def get_current_value(self):
|
|
||||||
"""Get raw value from the coordinator."""
|
|
||||||
value = self.coordinator.data[self.vin][VEHICLE_STATUS].get(self.data_field)
|
|
||||||
if value in sc.BAD_SENSOR_VALUES:
|
|
||||||
value = None
|
|
||||||
if isinstance(value, str):
|
|
||||||
if "." in value:
|
|
||||||
value = float(value)
|
|
||||||
else:
|
|
||||||
value = int(value)
|
|
||||||
return value
|
|
||||||
|
@ -2332,7 +2332,7 @@ streamlabswater==1.0.1
|
|||||||
stringcase==1.2.0
|
stringcase==1.2.0
|
||||||
|
|
||||||
# homeassistant.components.subaru
|
# homeassistant.components.subaru
|
||||||
subarulink==0.5.0
|
subarulink==0.6.0
|
||||||
|
|
||||||
# homeassistant.components.solarlog
|
# homeassistant.components.solarlog
|
||||||
sunwatcher==0.2.1
|
sunwatcher==0.2.1
|
||||||
|
@ -1611,7 +1611,7 @@ stookalert==0.1.4
|
|||||||
stringcase==1.2.0
|
stringcase==1.2.0
|
||||||
|
|
||||||
# homeassistant.components.subaru
|
# homeassistant.components.subaru
|
||||||
subarulink==0.5.0
|
subarulink==0.6.0
|
||||||
|
|
||||||
# homeassistant.components.solarlog
|
# homeassistant.components.solarlog
|
||||||
sunwatcher==0.2.1
|
sunwatcher==0.2.1
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
"""Sample API response data for tests."""
|
"""Sample API response data for tests."""
|
||||||
|
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
from homeassistant.components.subaru.const import (
|
from homeassistant.components.subaru.const import (
|
||||||
API_GEN_1,
|
API_GEN_1,
|
||||||
API_GEN_2,
|
API_GEN_2,
|
||||||
@ -46,10 +48,12 @@ VEHICLE_DATA = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MOCK_DATETIME = datetime.fromtimestamp(1595560000, timezone.utc)
|
||||||
|
|
||||||
VEHICLE_STATUS_EV = {
|
VEHICLE_STATUS_EV = {
|
||||||
"status": {
|
"status": {
|
||||||
"AVG_FUEL_CONSUMPTION": 2.3,
|
"AVG_FUEL_CONSUMPTION": 2.3,
|
||||||
"BATTERY_VOLTAGE": "12.0",
|
"BATTERY_VOLTAGE": 12.0,
|
||||||
"DISTANCE_TO_EMPTY_FUEL": 707,
|
"DISTANCE_TO_EMPTY_FUEL": 707,
|
||||||
"DOOR_BOOT_LOCK_STATUS": "UNKNOWN",
|
"DOOR_BOOT_LOCK_STATUS": "UNKNOWN",
|
||||||
"DOOR_BOOT_POSITION": "CLOSED",
|
"DOOR_BOOT_POSITION": "CLOSED",
|
||||||
@ -63,21 +67,17 @@ VEHICLE_STATUS_EV = {
|
|||||||
"DOOR_REAR_LEFT_POSITION": "CLOSED",
|
"DOOR_REAR_LEFT_POSITION": "CLOSED",
|
||||||
"DOOR_REAR_RIGHT_LOCK_STATUS": "UNKNOWN",
|
"DOOR_REAR_RIGHT_LOCK_STATUS": "UNKNOWN",
|
||||||
"DOOR_REAR_RIGHT_POSITION": "CLOSED",
|
"DOOR_REAR_RIGHT_POSITION": "CLOSED",
|
||||||
"EV_CHARGER_STATE_TYPE": "CHARGING_STOPPED",
|
"EV_CHARGER_STATE_TYPE": "CHARGING",
|
||||||
"EV_CHARGE_SETTING_AMPERE_TYPE": "MAXIMUM",
|
"EV_CHARGE_SETTING_AMPERE_TYPE": "MAXIMUM",
|
||||||
"EV_CHARGE_VOLT_TYPE": "CHARGE_LEVEL_1",
|
"EV_CHARGE_VOLT_TYPE": "CHARGE_LEVEL_1",
|
||||||
"EV_DISTANCE_TO_EMPTY": 17,
|
"EV_DISTANCE_TO_EMPTY": 1,
|
||||||
"EV_IS_PLUGGED_IN": "UNLOCKED_CONNECTED",
|
"EV_IS_PLUGGED_IN": "UNLOCKED_CONNECTED",
|
||||||
"EV_STATE_OF_CHARGE_MODE": "EV_MODE",
|
"EV_STATE_OF_CHARGE_MODE": "EV_MODE",
|
||||||
"EV_STATE_OF_CHARGE_PERCENT": "100",
|
"EV_STATE_OF_CHARGE_PERCENT": 20,
|
||||||
"EV_TIME_TO_FULLY_CHARGED": "65535",
|
"EV_TIME_TO_FULLY_CHARGED_UTC": MOCK_DATETIME,
|
||||||
"EV_VEHICLE_TIME_DAYOFWEEK": "6",
|
"EXT_EXTERNAL_TEMP": 21.5,
|
||||||
"EV_VEHICLE_TIME_HOUR": "14",
|
|
||||||
"EV_VEHICLE_TIME_MINUTE": "20",
|
|
||||||
"EV_VEHICLE_TIME_SECOND": "39",
|
|
||||||
"EXT_EXTERNAL_TEMP": "21.5",
|
|
||||||
"ODOMETER": 1234,
|
"ODOMETER": 1234,
|
||||||
"POSITION_HEADING_DEGREE": "150",
|
"POSITION_HEADING_DEGREE": 150,
|
||||||
"POSITION_SPEED_KMPH": "0",
|
"POSITION_SPEED_KMPH": "0",
|
||||||
"POSITION_TIMESTAMP": 1595560000.0,
|
"POSITION_TIMESTAMP": 1595560000.0,
|
||||||
"SEAT_BELT_STATUS_FRONT_LEFT": "BELTED",
|
"SEAT_BELT_STATUS_FRONT_LEFT": "BELTED",
|
||||||
@ -100,7 +100,7 @@ VEHICLE_STATUS_EV = {
|
|||||||
"SEAT_OCCUPATION_STATUS_THIRD_RIGHT": "UNKNOWN",
|
"SEAT_OCCUPATION_STATUS_THIRD_RIGHT": "UNKNOWN",
|
||||||
"TIMESTAMP": 1595560000.0,
|
"TIMESTAMP": 1595560000.0,
|
||||||
"TRANSMISSION_MODE": "UNKNOWN",
|
"TRANSMISSION_MODE": "UNKNOWN",
|
||||||
"TYRE_PRESSURE_FRONT_LEFT": 2550,
|
"TYRE_PRESSURE_FRONT_LEFT": 0,
|
||||||
"TYRE_PRESSURE_FRONT_RIGHT": 2550,
|
"TYRE_PRESSURE_FRONT_RIGHT": 2550,
|
||||||
"TYRE_PRESSURE_REAR_LEFT": 2450,
|
"TYRE_PRESSURE_REAR_LEFT": 2450,
|
||||||
"TYRE_PRESSURE_REAR_RIGHT": 2350,
|
"TYRE_PRESSURE_REAR_RIGHT": 2350,
|
||||||
@ -121,10 +121,11 @@ VEHICLE_STATUS_EV = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
VEHICLE_STATUS_G2 = {
|
VEHICLE_STATUS_G2 = {
|
||||||
"status": {
|
"status": {
|
||||||
"AVG_FUEL_CONSUMPTION": 2.3,
|
"AVG_FUEL_CONSUMPTION": 2.3,
|
||||||
"BATTERY_VOLTAGE": "12.0",
|
"BATTERY_VOLTAGE": 12.0,
|
||||||
"DISTANCE_TO_EMPTY_FUEL": 707,
|
"DISTANCE_TO_EMPTY_FUEL": 707,
|
||||||
"DOOR_BOOT_LOCK_STATUS": "UNKNOWN",
|
"DOOR_BOOT_LOCK_STATUS": "UNKNOWN",
|
||||||
"DOOR_BOOT_POSITION": "CLOSED",
|
"DOOR_BOOT_POSITION": "CLOSED",
|
||||||
@ -138,9 +139,9 @@ VEHICLE_STATUS_G2 = {
|
|||||||
"DOOR_REAR_LEFT_POSITION": "CLOSED",
|
"DOOR_REAR_LEFT_POSITION": "CLOSED",
|
||||||
"DOOR_REAR_RIGHT_LOCK_STATUS": "UNKNOWN",
|
"DOOR_REAR_RIGHT_LOCK_STATUS": "UNKNOWN",
|
||||||
"DOOR_REAR_RIGHT_POSITION": "CLOSED",
|
"DOOR_REAR_RIGHT_POSITION": "CLOSED",
|
||||||
"EXT_EXTERNAL_TEMP": "21.5",
|
"EXT_EXTERNAL_TEMP": None,
|
||||||
"ODOMETER": 1234,
|
"ODOMETER": 1234,
|
||||||
"POSITION_HEADING_DEGREE": "150",
|
"POSITION_HEADING_DEGREE": 150,
|
||||||
"POSITION_SPEED_KMPH": "0",
|
"POSITION_SPEED_KMPH": "0",
|
||||||
"POSITION_TIMESTAMP": 1595560000.0,
|
"POSITION_TIMESTAMP": 1595560000.0,
|
||||||
"SEAT_BELT_STATUS_FRONT_LEFT": "BELTED",
|
"SEAT_BELT_STATUS_FRONT_LEFT": "BELTED",
|
||||||
@ -188,18 +189,14 @@ EXPECTED_STATE_EV_IMPERIAL = {
|
|||||||
"AVG_FUEL_CONSUMPTION": "102.3",
|
"AVG_FUEL_CONSUMPTION": "102.3",
|
||||||
"BATTERY_VOLTAGE": "12.0",
|
"BATTERY_VOLTAGE": "12.0",
|
||||||
"DISTANCE_TO_EMPTY_FUEL": "439.3",
|
"DISTANCE_TO_EMPTY_FUEL": "439.3",
|
||||||
"EV_CHARGER_STATE_TYPE": "CHARGING_STOPPED",
|
"EV_CHARGER_STATE_TYPE": "CHARGING",
|
||||||
"EV_CHARGE_SETTING_AMPERE_TYPE": "MAXIMUM",
|
"EV_CHARGE_SETTING_AMPERE_TYPE": "MAXIMUM",
|
||||||
"EV_CHARGE_VOLT_TYPE": "CHARGE_LEVEL_1",
|
"EV_CHARGE_VOLT_TYPE": "CHARGE_LEVEL_1",
|
||||||
"EV_DISTANCE_TO_EMPTY": "17",
|
"EV_DISTANCE_TO_EMPTY": "1",
|
||||||
"EV_IS_PLUGGED_IN": "UNLOCKED_CONNECTED",
|
"EV_IS_PLUGGED_IN": "UNLOCKED_CONNECTED",
|
||||||
"EV_STATE_OF_CHARGE_MODE": "EV_MODE",
|
"EV_STATE_OF_CHARGE_MODE": "EV_MODE",
|
||||||
"EV_STATE_OF_CHARGE_PERCENT": "100",
|
"EV_STATE_OF_CHARGE_PERCENT": "20",
|
||||||
"EV_TIME_TO_FULLY_CHARGED": "unknown",
|
"EV_TIME_TO_FULLY_CHARGED_UTC": "2020-07-24T03:06:40+00:00",
|
||||||
"EV_VEHICLE_TIME_DAYOFWEEK": "6",
|
|
||||||
"EV_VEHICLE_TIME_HOUR": "14",
|
|
||||||
"EV_VEHICLE_TIME_MINUTE": "20",
|
|
||||||
"EV_VEHICLE_TIME_SECOND": "39",
|
|
||||||
"EXT_EXTERNAL_TEMP": "70.7",
|
"EXT_EXTERNAL_TEMP": "70.7",
|
||||||
"ODOMETER": "766.8",
|
"ODOMETER": "766.8",
|
||||||
"POSITION_HEADING_DEGREE": "150",
|
"POSITION_HEADING_DEGREE": "150",
|
||||||
@ -207,7 +204,7 @@ EXPECTED_STATE_EV_IMPERIAL = {
|
|||||||
"POSITION_TIMESTAMP": 1595560000.0,
|
"POSITION_TIMESTAMP": 1595560000.0,
|
||||||
"TIMESTAMP": 1595560000.0,
|
"TIMESTAMP": 1595560000.0,
|
||||||
"TRANSMISSION_MODE": "UNKNOWN",
|
"TRANSMISSION_MODE": "UNKNOWN",
|
||||||
"TYRE_PRESSURE_FRONT_LEFT": "37.0",
|
"TYRE_PRESSURE_FRONT_LEFT": "0.0",
|
||||||
"TYRE_PRESSURE_FRONT_RIGHT": "37.0",
|
"TYRE_PRESSURE_FRONT_RIGHT": "37.0",
|
||||||
"TYRE_PRESSURE_REAR_LEFT": "35.5",
|
"TYRE_PRESSURE_REAR_LEFT": "35.5",
|
||||||
"TYRE_PRESSURE_REAR_RIGHT": "34.1",
|
"TYRE_PRESSURE_REAR_RIGHT": "34.1",
|
||||||
@ -221,18 +218,14 @@ EXPECTED_STATE_EV_METRIC = {
|
|||||||
"AVG_FUEL_CONSUMPTION": "2.3",
|
"AVG_FUEL_CONSUMPTION": "2.3",
|
||||||
"BATTERY_VOLTAGE": "12.0",
|
"BATTERY_VOLTAGE": "12.0",
|
||||||
"DISTANCE_TO_EMPTY_FUEL": "707",
|
"DISTANCE_TO_EMPTY_FUEL": "707",
|
||||||
"EV_CHARGER_STATE_TYPE": "CHARGING_STOPPED",
|
"EV_CHARGER_STATE_TYPE": "CHARGING",
|
||||||
"EV_CHARGE_SETTING_AMPERE_TYPE": "MAXIMUM",
|
"EV_CHARGE_SETTING_AMPERE_TYPE": "MAXIMUM",
|
||||||
"EV_CHARGE_VOLT_TYPE": "CHARGE_LEVEL_1",
|
"EV_CHARGE_VOLT_TYPE": "CHARGE_LEVEL_1",
|
||||||
"EV_DISTANCE_TO_EMPTY": "27.4",
|
"EV_DISTANCE_TO_EMPTY": "1.6",
|
||||||
"EV_IS_PLUGGED_IN": "UNLOCKED_CONNECTED",
|
"EV_IS_PLUGGED_IN": "UNLOCKED_CONNECTED",
|
||||||
"EV_STATE_OF_CHARGE_MODE": "EV_MODE",
|
"EV_STATE_OF_CHARGE_MODE": "EV_MODE",
|
||||||
"EV_STATE_OF_CHARGE_PERCENT": "100",
|
"EV_STATE_OF_CHARGE_PERCENT": "20",
|
||||||
"EV_TIME_TO_FULLY_CHARGED": "unknown",
|
"EV_TIME_TO_FULLY_CHARGED_UTC": "2020-07-24T03:06:40+00:00",
|
||||||
"EV_VEHICLE_TIME_DAYOFWEEK": "6",
|
|
||||||
"EV_VEHICLE_TIME_HOUR": "14",
|
|
||||||
"EV_VEHICLE_TIME_MINUTE": "20",
|
|
||||||
"EV_VEHICLE_TIME_SECOND": "39",
|
|
||||||
"EXT_EXTERNAL_TEMP": "21.5",
|
"EXT_EXTERNAL_TEMP": "21.5",
|
||||||
"ODOMETER": "1234",
|
"ODOMETER": "1234",
|
||||||
"POSITION_HEADING_DEGREE": "150",
|
"POSITION_HEADING_DEGREE": "150",
|
||||||
@ -240,7 +233,7 @@ EXPECTED_STATE_EV_METRIC = {
|
|||||||
"POSITION_TIMESTAMP": 1595560000.0,
|
"POSITION_TIMESTAMP": 1595560000.0,
|
||||||
"TIMESTAMP": 1595560000.0,
|
"TIMESTAMP": 1595560000.0,
|
||||||
"TRANSMISSION_MODE": "UNKNOWN",
|
"TRANSMISSION_MODE": "UNKNOWN",
|
||||||
"TYRE_PRESSURE_FRONT_LEFT": "2550",
|
"TYRE_PRESSURE_FRONT_LEFT": "0",
|
||||||
"TYRE_PRESSURE_FRONT_RIGHT": "2550",
|
"TYRE_PRESSURE_FRONT_RIGHT": "2550",
|
||||||
"TYRE_PRESSURE_REAR_LEFT": "2450",
|
"TYRE_PRESSURE_REAR_LEFT": "2450",
|
||||||
"TYRE_PRESSURE_REAR_RIGHT": "2350",
|
"TYRE_PRESSURE_REAR_RIGHT": "2350",
|
||||||
@ -250,6 +243,7 @@ EXPECTED_STATE_EV_METRIC = {
|
|||||||
"longitude": -100.0,
|
"longitude": -100.0,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
EXPECTED_STATE_EV_UNAVAILABLE = {
|
EXPECTED_STATE_EV_UNAVAILABLE = {
|
||||||
"AVG_FUEL_CONSUMPTION": "unavailable",
|
"AVG_FUEL_CONSUMPTION": "unavailable",
|
||||||
"BATTERY_VOLTAGE": "unavailable",
|
"BATTERY_VOLTAGE": "unavailable",
|
||||||
@ -261,11 +255,7 @@ EXPECTED_STATE_EV_UNAVAILABLE = {
|
|||||||
"EV_IS_PLUGGED_IN": "unavailable",
|
"EV_IS_PLUGGED_IN": "unavailable",
|
||||||
"EV_STATE_OF_CHARGE_MODE": "unavailable",
|
"EV_STATE_OF_CHARGE_MODE": "unavailable",
|
||||||
"EV_STATE_OF_CHARGE_PERCENT": "unavailable",
|
"EV_STATE_OF_CHARGE_PERCENT": "unavailable",
|
||||||
"EV_TIME_TO_FULLY_CHARGED": "unavailable",
|
"EV_TIME_TO_FULLY_CHARGED_UTC": "unavailable",
|
||||||
"EV_VEHICLE_TIME_DAYOFWEEK": "unavailable",
|
|
||||||
"EV_VEHICLE_TIME_HOUR": "unavailable",
|
|
||||||
"EV_VEHICLE_TIME_MINUTE": "unavailable",
|
|
||||||
"EV_VEHICLE_TIME_SECOND": "unavailable",
|
|
||||||
"EXT_EXTERNAL_TEMP": "unavailable",
|
"EXT_EXTERNAL_TEMP": "unavailable",
|
||||||
"ODOMETER": "unavailable",
|
"ODOMETER": "unavailable",
|
||||||
"POSITION_HEADING_DEGREE": "unavailable",
|
"POSITION_HEADING_DEGREE": "unavailable",
|
||||||
|
@ -71,7 +71,8 @@ TEST_OPTIONS = {
|
|||||||
CONF_UPDATE_ENABLED: True,
|
CONF_UPDATE_ENABLED: True,
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_ENTITY_ID = "sensor.test_vehicle_2_odometer"
|
TEST_DEVICE_NAME = "test_vehicle_2"
|
||||||
|
TEST_ENTITY_ID = f"sensor.{TEST_DEVICE_NAME}_odometer"
|
||||||
|
|
||||||
|
|
||||||
def advance_time_to_next_fetch(hass):
|
def advance_time_to_next_fetch(hass):
|
||||||
|
@ -1,13 +1,10 @@
|
|||||||
"""Test Subaru sensors."""
|
"""Test Subaru sensors."""
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from homeassistant.components.subaru.const import VEHICLE_NAME
|
|
||||||
from homeassistant.components.subaru.sensor import (
|
from homeassistant.components.subaru.sensor import (
|
||||||
API_GEN_2_SENSORS,
|
API_GEN_2_SENSORS,
|
||||||
EV_SENSORS,
|
EV_SENSORS,
|
||||||
SAFETY_SENSORS,
|
SAFETY_SENSORS,
|
||||||
SENSOR_FIELD,
|
|
||||||
SENSOR_TYPE,
|
|
||||||
)
|
)
|
||||||
from homeassistant.util import slugify
|
from homeassistant.util import slugify
|
||||||
from homeassistant.util.unit_system import IMPERIAL_SYSTEM
|
from homeassistant.util.unit_system import IMPERIAL_SYSTEM
|
||||||
@ -16,13 +13,14 @@ from .api_responses import (
|
|||||||
EXPECTED_STATE_EV_IMPERIAL,
|
EXPECTED_STATE_EV_IMPERIAL,
|
||||||
EXPECTED_STATE_EV_METRIC,
|
EXPECTED_STATE_EV_METRIC,
|
||||||
EXPECTED_STATE_EV_UNAVAILABLE,
|
EXPECTED_STATE_EV_UNAVAILABLE,
|
||||||
TEST_VIN_2_EV,
|
|
||||||
VEHICLE_DATA,
|
|
||||||
VEHICLE_STATUS_EV,
|
VEHICLE_STATUS_EV,
|
||||||
)
|
)
|
||||||
from .conftest import MOCK_API_FETCH, MOCK_API_GET_DATA, advance_time_to_next_fetch
|
from .conftest import (
|
||||||
|
MOCK_API_FETCH,
|
||||||
VEHICLE_NAME = VEHICLE_DATA[TEST_VIN_2_EV][VEHICLE_NAME]
|
MOCK_API_GET_DATA,
|
||||||
|
TEST_DEVICE_NAME,
|
||||||
|
advance_time_to_next_fetch,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_sensors_ev_imperial(hass, ev_entry):
|
async def test_sensors_ev_imperial(hass, ev_entry):
|
||||||
@ -59,9 +57,9 @@ def _assert_data(hass, expected_state):
|
|||||||
expected_states = {}
|
expected_states = {}
|
||||||
for item in sensor_list:
|
for item in sensor_list:
|
||||||
expected_states[
|
expected_states[
|
||||||
f"sensor.{slugify(f'{VEHICLE_NAME} {item[SENSOR_TYPE]}')}"
|
f"sensor.{slugify(f'{TEST_DEVICE_NAME} {item.name}')}"
|
||||||
] = expected_state[item[SENSOR_FIELD]]
|
] = expected_state[item.key]
|
||||||
|
|
||||||
for sensor in expected_states:
|
for sensor, value in expected_states.items():
|
||||||
actual = hass.states.get(sensor)
|
actual = hass.states.get(sensor)
|
||||||
assert actual.state == expected_states[sensor]
|
assert actual.state == value
|
||||||
|
Loading…
x
Reference in New Issue
Block a user