mirror of
https://github.com/home-assistant/core
synced 2024-08-02 23:40:32 +02:00
Add device tracker platform to Renault integration (#54745)
This commit is contained in:
parent
04a052a37d
commit
bcf97cb308
@ -1,5 +1,6 @@
|
||||
"""Constants for the Renault component."""
|
||||
from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN
|
||||
from homeassistant.components.device_tracker import DOMAIN as DEVICE_TRACKER_DOMAIN
|
||||
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
||||
|
||||
DOMAIN = "renault"
|
||||
@ -11,6 +12,7 @@ DEFAULT_SCAN_INTERVAL = 300 # 5 minutes
|
||||
|
||||
PLATFORMS = [
|
||||
BINARY_SENSOR_DOMAIN,
|
||||
DEVICE_TRACKER_DOMAIN,
|
||||
SENSOR_DOMAIN,
|
||||
]
|
||||
|
||||
|
61
homeassistant/components/renault/device_tracker.py
Normal file
61
homeassistant/components/renault/device_tracker.py
Normal file
@ -0,0 +1,61 @@
|
||||
"""Support for Renault device trackers."""
|
||||
from __future__ import annotations
|
||||
|
||||
from renault_api.kamereon.models import KamereonVehicleLocationData
|
||||
|
||||
from homeassistant.components.device_tracker import SOURCE_TYPE_GPS
|
||||
from homeassistant.components.device_tracker.config_entry import TrackerEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .renault_entities import RenaultDataEntity, RenaultEntityDescription
|
||||
from .renault_hub import RenaultHub
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the Renault entities from config entry."""
|
||||
proxy: RenaultHub = hass.data[DOMAIN][config_entry.entry_id]
|
||||
entities: list[RenaultDeviceTracker] = [
|
||||
RenaultDeviceTracker(vehicle, description)
|
||||
for vehicle in proxy.vehicles.values()
|
||||
for description in DEVICE_TRACKER_TYPES
|
||||
if description.coordinator in vehicle.coordinators
|
||||
]
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
class RenaultDeviceTracker(
|
||||
RenaultDataEntity[KamereonVehicleLocationData], TrackerEntity
|
||||
):
|
||||
"""Mixin for device tracker specific attributes."""
|
||||
|
||||
@property
|
||||
def latitude(self) -> float | None:
|
||||
"""Return latitude value of the device."""
|
||||
return self.coordinator.data.gpsLatitude if self.coordinator.data else None
|
||||
|
||||
@property
|
||||
def longitude(self) -> float | None:
|
||||
"""Return longitude value of the device."""
|
||||
return self.coordinator.data.gpsLongitude if self.coordinator.data else None
|
||||
|
||||
@property
|
||||
def source_type(self) -> str:
|
||||
"""Return the source type of the device."""
|
||||
return SOURCE_TYPE_GPS
|
||||
|
||||
|
||||
DEVICE_TRACKER_TYPES: tuple[RenaultEntityDescription, ...] = (
|
||||
RenaultEntityDescription(
|
||||
key="location",
|
||||
coordinator="location",
|
||||
icon="mdi:car",
|
||||
name="Location",
|
||||
),
|
||||
)
|
@ -3,11 +3,12 @@ from __future__ import annotations
|
||||
|
||||
from collections.abc import Mapping
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Optional, cast
|
||||
from typing import TYPE_CHECKING, Any, Optional, cast
|
||||
|
||||
from homeassistant.helpers.entity import Entity, EntityDescription
|
||||
from homeassistant.helpers.typing import StateType
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
from homeassistant.util.dt import as_utc, parse_datetime
|
||||
|
||||
from .renault_coordinator import T
|
||||
from .renault_vehicle import RenaultVehicleProxy
|
||||
@ -54,8 +55,19 @@ class RenaultDataEntity(CoordinatorEntity[Optional[T]], Entity):
|
||||
@property
|
||||
def extra_state_attributes(self) -> Mapping[str, Any] | None:
|
||||
"""Return the state attributes of this entity."""
|
||||
last_update: str | None = None
|
||||
if self.entity_description.coordinator == "battery":
|
||||
last_update = self._get_data_attr("timestamp")
|
||||
if last_update:
|
||||
return {ATTR_LAST_UPDATE: last_update}
|
||||
last_update = cast(str, self._get_data_attr("timestamp"))
|
||||
elif self.entity_description.coordinator == "location":
|
||||
last_update = cast(str, self._get_data_attr("lastUpdateTime"))
|
||||
if last_update:
|
||||
return {ATTR_LAST_UPDATE: _convert_to_utc_string(last_update)}
|
||||
return None
|
||||
|
||||
|
||||
def _convert_to_utc_string(value: str) -> str:
|
||||
"""Convert date to UTC iso format."""
|
||||
original_dt = parse_datetime(value)
|
||||
if TYPE_CHECKING:
|
||||
assert original_dt is not None
|
||||
return as_utc(original_dt).isoformat()
|
||||
|
@ -138,6 +138,11 @@ COORDINATORS: tuple[RenaultCoordinatorDescription, ...] = (
|
||||
key="hvac_status",
|
||||
update_method=lambda x: x.get_hvac_status,
|
||||
),
|
||||
RenaultCoordinatorDescription(
|
||||
endpoint="location",
|
||||
key="location",
|
||||
update_method=lambda x: x.get_location,
|
||||
),
|
||||
RenaultCoordinatorDescription(
|
||||
endpoint="battery-status",
|
||||
key="battery",
|
||||
|
@ -63,6 +63,11 @@ def get_fixtures(vehicle_type: str) -> dict[str, Any]:
|
||||
if "hvac_status" in mock_vehicle["endpoints"]
|
||||
else load_fixture("renault/no_data.json")
|
||||
).get_attributes(schemas.KamereonVehicleHvacStatusDataSchema),
|
||||
"location": schemas.KamereonVehicleDataResponseSchema.loads(
|
||||
load_fixture(f"renault/{mock_vehicle['endpoints']['location']}")
|
||||
if "location" in mock_vehicle["endpoints"]
|
||||
else load_fixture("renault/no_data.json")
|
||||
).get_attributes(schemas.KamereonVehicleLocationDataSchema),
|
||||
}
|
||||
|
||||
|
||||
@ -132,6 +137,9 @@ async def setup_renault_integration_vehicle(hass: HomeAssistant, vehicle_type: s
|
||||
), patch(
|
||||
"renault_api.renault_vehicle.RenaultVehicle.get_hvac_status",
|
||||
return_value=mock_fixtures["hvac_status"],
|
||||
), patch(
|
||||
"renault_api.renault_vehicle.RenaultVehicle.get_location",
|
||||
return_value=mock_fixtures["location"],
|
||||
):
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
@ -181,6 +189,9 @@ async def setup_renault_integration_vehicle_with_no_data(
|
||||
), patch(
|
||||
"renault_api.renault_vehicle.RenaultVehicle.get_hvac_status",
|
||||
return_value=mock_fixtures["hvac_status"],
|
||||
), patch(
|
||||
"renault_api.renault_vehicle.RenaultVehicle.get_location",
|
||||
return_value=mock_fixtures["location"],
|
||||
):
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
@ -229,6 +240,9 @@ async def setup_renault_integration_vehicle_with_side_effect(
|
||||
), patch(
|
||||
"renault_api.renault_vehicle.RenaultVehicle.get_hvac_status",
|
||||
side_effect=side_effect,
|
||||
), patch(
|
||||
"renault_api.renault_vehicle.RenaultVehicle.get_location",
|
||||
side_effect=side_effect,
|
||||
):
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
@ -4,6 +4,7 @@ from homeassistant.components.binary_sensor import (
|
||||
DEVICE_CLASS_PLUG,
|
||||
DOMAIN as BINARY_SENSOR_DOMAIN,
|
||||
)
|
||||
from homeassistant.components.device_tracker import DOMAIN as DEVICE_TRACKER_DOMAIN
|
||||
from homeassistant.components.renault.const import (
|
||||
CONF_KAMEREON_ACCOUNT_ID,
|
||||
CONF_LOCALE,
|
||||
@ -33,6 +34,7 @@ from homeassistant.const import (
|
||||
LENGTH_KILOMETERS,
|
||||
PERCENTAGE,
|
||||
POWER_KILO_WATT,
|
||||
STATE_NOT_HOME,
|
||||
STATE_OFF,
|
||||
STATE_ON,
|
||||
STATE_UNKNOWN,
|
||||
@ -77,6 +79,7 @@ MOCK_VEHICLES = {
|
||||
"endpoints_available": [
|
||||
True, # cockpit
|
||||
True, # hvac-status
|
||||
False, # location
|
||||
True, # battery-status
|
||||
True, # charge-mode
|
||||
],
|
||||
@ -92,23 +95,24 @@ MOCK_VEHICLES = {
|
||||
"unique_id": "vf1aaaaa555777999_plugged_in",
|
||||
"result": STATE_ON,
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_PLUG,
|
||||
ATTR_LAST_UPDATE: "2020-01-12T21:40:16Z",
|
||||
ATTR_LAST_UPDATE: "2020-01-12T21:40:16+00:00",
|
||||
},
|
||||
{
|
||||
"entity_id": "binary_sensor.charging",
|
||||
"unique_id": "vf1aaaaa555777999_charging",
|
||||
"result": STATE_ON,
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_BATTERY_CHARGING,
|
||||
ATTR_LAST_UPDATE: "2020-01-12T21:40:16Z",
|
||||
ATTR_LAST_UPDATE: "2020-01-12T21:40:16+00:00",
|
||||
},
|
||||
],
|
||||
DEVICE_TRACKER_DOMAIN: [],
|
||||
SENSOR_DOMAIN: [
|
||||
{
|
||||
"entity_id": "sensor.battery_autonomy",
|
||||
"unique_id": "vf1aaaaa555777999_battery_autonomy",
|
||||
"result": "141",
|
||||
ATTR_ICON: "mdi:ev-station",
|
||||
ATTR_LAST_UPDATE: "2020-01-12T21:40:16Z",
|
||||
ATTR_LAST_UPDATE: "2020-01-12T21:40:16+00:00",
|
||||
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||
ATTR_UNIT_OF_MEASUREMENT: LENGTH_KILOMETERS,
|
||||
},
|
||||
@ -117,7 +121,7 @@ MOCK_VEHICLES = {
|
||||
"unique_id": "vf1aaaaa555777999_battery_available_energy",
|
||||
"result": "31",
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_ENERGY,
|
||||
ATTR_LAST_UPDATE: "2020-01-12T21:40:16Z",
|
||||
ATTR_LAST_UPDATE: "2020-01-12T21:40:16+00:00",
|
||||
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||
ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR,
|
||||
},
|
||||
@ -126,7 +130,7 @@ MOCK_VEHICLES = {
|
||||
"unique_id": "vf1aaaaa555777999_battery_level",
|
||||
"result": "60",
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_BATTERY,
|
||||
ATTR_LAST_UPDATE: "2020-01-12T21:40:16Z",
|
||||
ATTR_LAST_UPDATE: "2020-01-12T21:40:16+00:00",
|
||||
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||
ATTR_UNIT_OF_MEASUREMENT: PERCENTAGE,
|
||||
},
|
||||
@ -135,7 +139,7 @@ MOCK_VEHICLES = {
|
||||
"unique_id": "vf1aaaaa555777999_battery_temperature",
|
||||
"result": "20",
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
|
||||
ATTR_LAST_UPDATE: "2020-01-12T21:40:16Z",
|
||||
ATTR_LAST_UPDATE: "2020-01-12T21:40:16+00:00",
|
||||
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||
ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS,
|
||||
},
|
||||
@ -152,14 +156,14 @@ MOCK_VEHICLES = {
|
||||
"result": "charge_in_progress",
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_CHARGE_STATE,
|
||||
ATTR_ICON: "mdi:flash",
|
||||
ATTR_LAST_UPDATE: "2020-01-12T21:40:16Z",
|
||||
ATTR_LAST_UPDATE: "2020-01-12T21:40:16+00:00",
|
||||
},
|
||||
{
|
||||
"entity_id": "sensor.charging_power",
|
||||
"unique_id": "vf1aaaaa555777999_charging_power",
|
||||
"result": "0.027",
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_POWER,
|
||||
ATTR_LAST_UPDATE: "2020-01-12T21:40:16Z",
|
||||
ATTR_LAST_UPDATE: "2020-01-12T21:40:16+00:00",
|
||||
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||
ATTR_UNIT_OF_MEASUREMENT: POWER_KILO_WATT,
|
||||
},
|
||||
@ -168,7 +172,7 @@ MOCK_VEHICLES = {
|
||||
"unique_id": "vf1aaaaa555777999_charging_remaining_time",
|
||||
"result": "145",
|
||||
ATTR_ICON: "mdi:timer",
|
||||
ATTR_LAST_UPDATE: "2020-01-12T21:40:16Z",
|
||||
ATTR_LAST_UPDATE: "2020-01-12T21:40:16+00:00",
|
||||
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||
ATTR_UNIT_OF_MEASUREMENT: TIME_MINUTES,
|
||||
},
|
||||
@ -194,7 +198,7 @@ MOCK_VEHICLES = {
|
||||
"result": "plugged",
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_PLUG_STATE,
|
||||
ATTR_ICON: "mdi:power-plug",
|
||||
ATTR_LAST_UPDATE: "2020-01-12T21:40:16Z",
|
||||
ATTR_LAST_UPDATE: "2020-01-12T21:40:16+00:00",
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -209,6 +213,7 @@ MOCK_VEHICLES = {
|
||||
"endpoints_available": [
|
||||
True, # cockpit
|
||||
False, # hvac-status
|
||||
True, # location
|
||||
True, # battery-status
|
||||
True, # charge-mode
|
||||
],
|
||||
@ -216,6 +221,7 @@ MOCK_VEHICLES = {
|
||||
"battery_status": "battery_status_not_charging.json",
|
||||
"charge_mode": "charge_mode_schedule.json",
|
||||
"cockpit": "cockpit_ev.json",
|
||||
"location": "location.json",
|
||||
},
|
||||
BINARY_SENSOR_DOMAIN: [
|
||||
{
|
||||
@ -223,23 +229,32 @@ MOCK_VEHICLES = {
|
||||
"unique_id": "vf1aaaaa555777999_plugged_in",
|
||||
"result": STATE_OFF,
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_PLUG,
|
||||
ATTR_LAST_UPDATE: "2020-11-17T09:06:48+01:00",
|
||||
ATTR_LAST_UPDATE: "2020-11-17T08:06:48+00:00",
|
||||
},
|
||||
{
|
||||
"entity_id": "binary_sensor.charging",
|
||||
"unique_id": "vf1aaaaa555777999_charging",
|
||||
"result": STATE_OFF,
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_BATTERY_CHARGING,
|
||||
ATTR_LAST_UPDATE: "2020-11-17T09:06:48+01:00",
|
||||
ATTR_LAST_UPDATE: "2020-11-17T08:06:48+00:00",
|
||||
},
|
||||
],
|
||||
DEVICE_TRACKER_DOMAIN: [
|
||||
{
|
||||
"entity_id": "device_tracker.location",
|
||||
"unique_id": "vf1aaaaa555777999_location",
|
||||
"result": STATE_NOT_HOME,
|
||||
ATTR_ICON: "mdi:car",
|
||||
ATTR_LAST_UPDATE: "2020-02-18T16:58:38+00:00",
|
||||
}
|
||||
],
|
||||
SENSOR_DOMAIN: [
|
||||
{
|
||||
"entity_id": "sensor.battery_autonomy",
|
||||
"unique_id": "vf1aaaaa555777999_battery_autonomy",
|
||||
"result": "128",
|
||||
ATTR_ICON: "mdi:ev-station",
|
||||
ATTR_LAST_UPDATE: "2020-11-17T09:06:48+01:00",
|
||||
ATTR_LAST_UPDATE: "2020-11-17T08:06:48+00:00",
|
||||
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||
ATTR_UNIT_OF_MEASUREMENT: LENGTH_KILOMETERS,
|
||||
},
|
||||
@ -248,7 +263,7 @@ MOCK_VEHICLES = {
|
||||
"unique_id": "vf1aaaaa555777999_battery_available_energy",
|
||||
"result": "0",
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_ENERGY,
|
||||
ATTR_LAST_UPDATE: "2020-11-17T09:06:48+01:00",
|
||||
ATTR_LAST_UPDATE: "2020-11-17T08:06:48+00:00",
|
||||
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||
ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR,
|
||||
},
|
||||
@ -257,7 +272,7 @@ MOCK_VEHICLES = {
|
||||
"unique_id": "vf1aaaaa555777999_battery_level",
|
||||
"result": "50",
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_BATTERY,
|
||||
ATTR_LAST_UPDATE: "2020-11-17T09:06:48+01:00",
|
||||
ATTR_LAST_UPDATE: "2020-11-17T08:06:48+00:00",
|
||||
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||
ATTR_UNIT_OF_MEASUREMENT: PERCENTAGE,
|
||||
},
|
||||
@ -266,7 +281,7 @@ MOCK_VEHICLES = {
|
||||
"unique_id": "vf1aaaaa555777999_battery_temperature",
|
||||
"result": STATE_UNKNOWN,
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
|
||||
ATTR_LAST_UPDATE: "2020-11-17T09:06:48+01:00",
|
||||
ATTR_LAST_UPDATE: "2020-11-17T08:06:48+00:00",
|
||||
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||
ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS,
|
||||
},
|
||||
@ -283,14 +298,14 @@ MOCK_VEHICLES = {
|
||||
"result": "charge_error",
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_CHARGE_STATE,
|
||||
ATTR_ICON: "mdi:flash-off",
|
||||
ATTR_LAST_UPDATE: "2020-11-17T09:06:48+01:00",
|
||||
ATTR_LAST_UPDATE: "2020-11-17T08:06:48+00:00",
|
||||
},
|
||||
{
|
||||
"entity_id": "sensor.charging_power",
|
||||
"unique_id": "vf1aaaaa555777999_charging_power",
|
||||
"result": STATE_UNKNOWN,
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_POWER,
|
||||
ATTR_LAST_UPDATE: "2020-11-17T09:06:48+01:00",
|
||||
ATTR_LAST_UPDATE: "2020-11-17T08:06:48+00:00",
|
||||
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||
ATTR_UNIT_OF_MEASUREMENT: POWER_KILO_WATT,
|
||||
},
|
||||
@ -299,7 +314,7 @@ MOCK_VEHICLES = {
|
||||
"unique_id": "vf1aaaaa555777999_charging_remaining_time",
|
||||
"result": STATE_UNKNOWN,
|
||||
ATTR_ICON: "mdi:timer",
|
||||
ATTR_LAST_UPDATE: "2020-11-17T09:06:48+01:00",
|
||||
ATTR_LAST_UPDATE: "2020-11-17T08:06:48+00:00",
|
||||
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||
ATTR_UNIT_OF_MEASUREMENT: TIME_MINUTES,
|
||||
},
|
||||
@ -317,7 +332,7 @@ MOCK_VEHICLES = {
|
||||
"result": "unplugged",
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_PLUG_STATE,
|
||||
ATTR_ICON: "mdi:power-plug-off",
|
||||
ATTR_LAST_UPDATE: "2020-11-17T09:06:48+01:00",
|
||||
ATTR_LAST_UPDATE: "2020-11-17T08:06:48+00:00",
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -332,6 +347,7 @@ MOCK_VEHICLES = {
|
||||
"endpoints_available": [
|
||||
True, # cockpit
|
||||
False, # hvac-status
|
||||
True, # location
|
||||
True, # battery-status
|
||||
True, # charge-mode
|
||||
],
|
||||
@ -339,6 +355,7 @@ MOCK_VEHICLES = {
|
||||
"battery_status": "battery_status_charging.json",
|
||||
"charge_mode": "charge_mode_always.json",
|
||||
"cockpit": "cockpit_fuel.json",
|
||||
"location": "location.json",
|
||||
},
|
||||
BINARY_SENSOR_DOMAIN: [
|
||||
{
|
||||
@ -346,23 +363,32 @@ MOCK_VEHICLES = {
|
||||
"unique_id": "vf1aaaaa555777123_plugged_in",
|
||||
"result": STATE_ON,
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_PLUG,
|
||||
ATTR_LAST_UPDATE: "2020-01-12T21:40:16Z",
|
||||
ATTR_LAST_UPDATE: "2020-01-12T21:40:16+00:00",
|
||||
},
|
||||
{
|
||||
"entity_id": "binary_sensor.charging",
|
||||
"unique_id": "vf1aaaaa555777123_charging",
|
||||
"result": STATE_ON,
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_BATTERY_CHARGING,
|
||||
ATTR_LAST_UPDATE: "2020-01-12T21:40:16Z",
|
||||
ATTR_LAST_UPDATE: "2020-01-12T21:40:16+00:00",
|
||||
},
|
||||
],
|
||||
DEVICE_TRACKER_DOMAIN: [
|
||||
{
|
||||
"entity_id": "device_tracker.location",
|
||||
"unique_id": "vf1aaaaa555777123_location",
|
||||
"result": STATE_NOT_HOME,
|
||||
ATTR_ICON: "mdi:car",
|
||||
ATTR_LAST_UPDATE: "2020-02-18T16:58:38+00:00",
|
||||
}
|
||||
],
|
||||
SENSOR_DOMAIN: [
|
||||
{
|
||||
"entity_id": "sensor.battery_autonomy",
|
||||
"unique_id": "vf1aaaaa555777123_battery_autonomy",
|
||||
"result": "141",
|
||||
ATTR_ICON: "mdi:ev-station",
|
||||
ATTR_LAST_UPDATE: "2020-01-12T21:40:16Z",
|
||||
ATTR_LAST_UPDATE: "2020-01-12T21:40:16+00:00",
|
||||
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||
ATTR_UNIT_OF_MEASUREMENT: LENGTH_KILOMETERS,
|
||||
},
|
||||
@ -371,7 +397,7 @@ MOCK_VEHICLES = {
|
||||
"unique_id": "vf1aaaaa555777123_battery_available_energy",
|
||||
"result": "31",
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_ENERGY,
|
||||
ATTR_LAST_UPDATE: "2020-01-12T21:40:16Z",
|
||||
ATTR_LAST_UPDATE: "2020-01-12T21:40:16+00:00",
|
||||
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||
ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR,
|
||||
},
|
||||
@ -380,7 +406,7 @@ MOCK_VEHICLES = {
|
||||
"unique_id": "vf1aaaaa555777123_battery_level",
|
||||
"result": "60",
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_BATTERY,
|
||||
ATTR_LAST_UPDATE: "2020-01-12T21:40:16Z",
|
||||
ATTR_LAST_UPDATE: "2020-01-12T21:40:16+00:00",
|
||||
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||
ATTR_UNIT_OF_MEASUREMENT: PERCENTAGE,
|
||||
},
|
||||
@ -389,7 +415,7 @@ MOCK_VEHICLES = {
|
||||
"unique_id": "vf1aaaaa555777123_battery_temperature",
|
||||
"result": "20",
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
|
||||
ATTR_LAST_UPDATE: "2020-01-12T21:40:16Z",
|
||||
ATTR_LAST_UPDATE: "2020-01-12T21:40:16+00:00",
|
||||
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||
ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS,
|
||||
},
|
||||
@ -406,14 +432,14 @@ MOCK_VEHICLES = {
|
||||
"result": "charge_in_progress",
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_CHARGE_STATE,
|
||||
ATTR_ICON: "mdi:flash",
|
||||
ATTR_LAST_UPDATE: "2020-01-12T21:40:16Z",
|
||||
ATTR_LAST_UPDATE: "2020-01-12T21:40:16+00:00",
|
||||
},
|
||||
{
|
||||
"entity_id": "sensor.charging_power",
|
||||
"unique_id": "vf1aaaaa555777123_charging_power",
|
||||
"result": "27.0",
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_POWER,
|
||||
ATTR_LAST_UPDATE: "2020-01-12T21:40:16Z",
|
||||
ATTR_LAST_UPDATE: "2020-01-12T21:40:16+00:00",
|
||||
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||
ATTR_UNIT_OF_MEASUREMENT: POWER_KILO_WATT,
|
||||
},
|
||||
@ -422,7 +448,7 @@ MOCK_VEHICLES = {
|
||||
"unique_id": "vf1aaaaa555777123_charging_remaining_time",
|
||||
"result": "145",
|
||||
ATTR_ICON: "mdi:timer",
|
||||
ATTR_LAST_UPDATE: "2020-01-12T21:40:16Z",
|
||||
ATTR_LAST_UPDATE: "2020-01-12T21:40:16+00:00",
|
||||
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||
ATTR_UNIT_OF_MEASUREMENT: TIME_MINUTES,
|
||||
},
|
||||
@ -456,7 +482,7 @@ MOCK_VEHICLES = {
|
||||
"result": "plugged",
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_PLUG_STATE,
|
||||
ATTR_ICON: "mdi:power-plug",
|
||||
ATTR_LAST_UPDATE: "2020-01-12T21:40:16Z",
|
||||
ATTR_LAST_UPDATE: "2020-01-12T21:40:16+00:00",
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -471,11 +497,24 @@ MOCK_VEHICLES = {
|
||||
"endpoints_available": [
|
||||
True, # cockpit
|
||||
False, # hvac-status
|
||||
True, # location
|
||||
# Ignore, # battery-status
|
||||
# Ignore, # charge-mode
|
||||
],
|
||||
"endpoints": {"cockpit": "cockpit_fuel.json"},
|
||||
"endpoints": {
|
||||
"cockpit": "cockpit_fuel.json",
|
||||
"location": "location.json",
|
||||
},
|
||||
BINARY_SENSOR_DOMAIN: [],
|
||||
DEVICE_TRACKER_DOMAIN: [
|
||||
{
|
||||
"entity_id": "device_tracker.location",
|
||||
"unique_id": "vf1aaaaa555777123_location",
|
||||
"result": STATE_NOT_HOME,
|
||||
ATTR_ICON: "mdi:car",
|
||||
ATTR_LAST_UPDATE: "2020-02-18T16:58:38+00:00",
|
||||
}
|
||||
],
|
||||
SENSOR_DOMAIN: [
|
||||
{
|
||||
"entity_id": "sensor.fuel_autonomy",
|
||||
|
164
tests/components/renault/test_device_tracker.py
Normal file
164
tests/components/renault/test_device_tracker.py
Normal file
@ -0,0 +1,164 @@
|
||||
"""Tests for Renault sensors."""
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from renault_api.kamereon import exceptions
|
||||
|
||||
from homeassistant.components.device_tracker import DOMAIN as DEVICE_TRACKER_DOMAIN
|
||||
from homeassistant.components.renault.renault_entities import ATTR_LAST_UPDATE
|
||||
from homeassistant.const import ATTR_ICON, STATE_UNAVAILABLE, STATE_UNKNOWN
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from . import (
|
||||
check_device_registry,
|
||||
get_no_data_icon,
|
||||
setup_renault_integration_vehicle,
|
||||
setup_renault_integration_vehicle_with_no_data,
|
||||
setup_renault_integration_vehicle_with_side_effect,
|
||||
)
|
||||
from .const import DYNAMIC_ATTRIBUTES, FIXED_ATTRIBUTES, MOCK_VEHICLES
|
||||
|
||||
from tests.common import mock_device_registry, mock_registry
|
||||
|
||||
|
||||
@pytest.mark.parametrize("vehicle_type", MOCK_VEHICLES.keys())
|
||||
async def test_device_trackers(hass: HomeAssistant, vehicle_type: str):
|
||||
"""Test for Renault device trackers."""
|
||||
await async_setup_component(hass, "persistent_notification", {})
|
||||
entity_registry = mock_registry(hass)
|
||||
device_registry = mock_device_registry(hass)
|
||||
|
||||
with patch("homeassistant.components.renault.PLATFORMS", [DEVICE_TRACKER_DOMAIN]):
|
||||
await setup_renault_integration_vehicle(hass, vehicle_type)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
mock_vehicle = MOCK_VEHICLES[vehicle_type]
|
||||
check_device_registry(device_registry, mock_vehicle["expected_device"])
|
||||
|
||||
expected_entities = mock_vehicle[DEVICE_TRACKER_DOMAIN]
|
||||
assert len(entity_registry.entities) == len(expected_entities)
|
||||
for expected_entity in expected_entities:
|
||||
entity_id = expected_entity["entity_id"]
|
||||
registry_entry = entity_registry.entities.get(entity_id)
|
||||
assert registry_entry is not None
|
||||
assert registry_entry.unique_id == expected_entity["unique_id"]
|
||||
state = hass.states.get(entity_id)
|
||||
assert state.state == expected_entity["result"]
|
||||
for attr in FIXED_ATTRIBUTES + DYNAMIC_ATTRIBUTES:
|
||||
assert state.attributes.get(attr) == expected_entity.get(attr)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("vehicle_type", MOCK_VEHICLES.keys())
|
||||
async def test_device_tracker_empty(hass: HomeAssistant, vehicle_type: str):
|
||||
"""Test for Renault device trackers with empty data from Renault."""
|
||||
await async_setup_component(hass, "persistent_notification", {})
|
||||
entity_registry = mock_registry(hass)
|
||||
device_registry = mock_device_registry(hass)
|
||||
|
||||
with patch("homeassistant.components.renault.PLATFORMS", [DEVICE_TRACKER_DOMAIN]):
|
||||
await setup_renault_integration_vehicle_with_no_data(hass, vehicle_type)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
mock_vehicle = MOCK_VEHICLES[vehicle_type]
|
||||
check_device_registry(device_registry, mock_vehicle["expected_device"])
|
||||
|
||||
expected_entities = mock_vehicle[DEVICE_TRACKER_DOMAIN]
|
||||
assert len(entity_registry.entities) == len(expected_entities)
|
||||
for expected_entity in expected_entities:
|
||||
entity_id = expected_entity["entity_id"]
|
||||
registry_entry = entity_registry.entities.get(entity_id)
|
||||
assert registry_entry is not None
|
||||
assert registry_entry.unique_id == expected_entity["unique_id"]
|
||||
state = hass.states.get(entity_id)
|
||||
assert state.state == STATE_UNKNOWN
|
||||
for attr in FIXED_ATTRIBUTES:
|
||||
assert state.attributes.get(attr) == expected_entity.get(attr)
|
||||
# Check dynamic attributes:
|
||||
assert state.attributes.get(ATTR_ICON) == get_no_data_icon(expected_entity)
|
||||
assert ATTR_LAST_UPDATE not in state.attributes
|
||||
|
||||
|
||||
@pytest.mark.parametrize("vehicle_type", MOCK_VEHICLES.keys())
|
||||
async def test_device_tracker_errors(hass: HomeAssistant, vehicle_type: str):
|
||||
"""Test for Renault device trackers with temporary failure."""
|
||||
await async_setup_component(hass, "persistent_notification", {})
|
||||
entity_registry = mock_registry(hass)
|
||||
device_registry = mock_device_registry(hass)
|
||||
|
||||
invalid_upstream_exception = exceptions.InvalidUpstreamException(
|
||||
"err.tech.500",
|
||||
"Invalid response from the upstream server (The request sent to the GDC is erroneous) ; 502 Bad Gateway",
|
||||
)
|
||||
|
||||
with patch("homeassistant.components.renault.PLATFORMS", [DEVICE_TRACKER_DOMAIN]):
|
||||
await setup_renault_integration_vehicle_with_side_effect(
|
||||
hass, vehicle_type, invalid_upstream_exception
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
mock_vehicle = MOCK_VEHICLES[vehicle_type]
|
||||
check_device_registry(device_registry, mock_vehicle["expected_device"])
|
||||
|
||||
expected_entities = mock_vehicle[DEVICE_TRACKER_DOMAIN]
|
||||
assert len(entity_registry.entities) == len(expected_entities)
|
||||
for expected_entity in expected_entities:
|
||||
entity_id = expected_entity["entity_id"]
|
||||
registry_entry = entity_registry.entities.get(entity_id)
|
||||
assert registry_entry is not None
|
||||
assert registry_entry.unique_id == expected_entity["unique_id"]
|
||||
state = hass.states.get(entity_id)
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
for attr in FIXED_ATTRIBUTES:
|
||||
assert state.attributes.get(attr) == expected_entity.get(attr)
|
||||
# Check dynamic attributes:
|
||||
assert state.attributes.get(ATTR_ICON) == get_no_data_icon(expected_entity)
|
||||
assert ATTR_LAST_UPDATE not in state.attributes
|
||||
|
||||
|
||||
async def test_device_tracker_access_denied(hass: HomeAssistant):
|
||||
"""Test for Renault device trackers with access denied failure."""
|
||||
await async_setup_component(hass, "persistent_notification", {})
|
||||
entity_registry = mock_registry(hass)
|
||||
device_registry = mock_device_registry(hass)
|
||||
|
||||
vehicle_type = "zoe_40"
|
||||
access_denied_exception = exceptions.AccessDeniedException(
|
||||
"err.func.403",
|
||||
"Access is denied for this resource",
|
||||
)
|
||||
|
||||
with patch("homeassistant.components.renault.PLATFORMS", [DEVICE_TRACKER_DOMAIN]):
|
||||
await setup_renault_integration_vehicle_with_side_effect(
|
||||
hass, vehicle_type, access_denied_exception
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
mock_vehicle = MOCK_VEHICLES[vehicle_type]
|
||||
check_device_registry(device_registry, mock_vehicle["expected_device"])
|
||||
|
||||
assert len(entity_registry.entities) == 0
|
||||
|
||||
|
||||
async def test_device_tracker_not_supported(hass: HomeAssistant):
|
||||
"""Test for Renault device trackers with not supported failure."""
|
||||
await async_setup_component(hass, "persistent_notification", {})
|
||||
entity_registry = mock_registry(hass)
|
||||
device_registry = mock_device_registry(hass)
|
||||
|
||||
vehicle_type = "zoe_40"
|
||||
not_supported_exception = exceptions.NotSupportedException(
|
||||
"err.tech.501",
|
||||
"This feature is not technically supported by this gateway",
|
||||
)
|
||||
|
||||
with patch("homeassistant.components.renault.PLATFORMS", [DEVICE_TRACKER_DOMAIN]):
|
||||
await setup_renault_integration_vehicle_with_side_effect(
|
||||
hass, vehicle_type, not_supported_exception
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
mock_vehicle = MOCK_VEHICLES[vehicle_type]
|
||||
check_device_registry(device_registry, mock_vehicle["expected_device"])
|
||||
|
||||
assert len(entity_registry.entities) == 0
|
11
tests/fixtures/renault/location.json
vendored
Normal file
11
tests/fixtures/renault/location.json
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"data": {
|
||||
"type": "Car",
|
||||
"id": "VF1AAAAA555777999",
|
||||
"attributes": {
|
||||
"gpsLatitude": 48.1234567,
|
||||
"gpsLongitude": 11.1234567,
|
||||
"lastUpdateTime": "2020-02-18T16:58:38Z"
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user