mirror of
https://github.com/home-assistant/core.git
synced 2025-07-24 21:57:51 +00:00
Use EntityDescription - renault (#55061)
* Cleanup sensor.py * Add EntityDescription * Add checks for state attributes * Fix pylint * Simplify checks * Add icon checks * Update data type * Use mixin for required keys, and review class initialisation * Add constraint to TypeVar("T") * Enable lambda for icon handling * Enable lambda for value handling * Enable lambda for value handling
This commit is contained in:
parent
35d943ba56
commit
9315f3bdd9
@ -1,22 +1,44 @@
|
|||||||
"""Support for Renault binary sensors."""
|
"""Support for Renault binary sensors."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
from renault_api.kamereon.enums import ChargeState, PlugState
|
from renault_api.kamereon.enums import ChargeState, PlugState
|
||||||
|
from renault_api.kamereon.models import KamereonVehicleBatteryStatusData
|
||||||
|
|
||||||
from homeassistant.components.binary_sensor import (
|
from homeassistant.components.binary_sensor import (
|
||||||
DEVICE_CLASS_BATTERY_CHARGING,
|
DEVICE_CLASS_BATTERY_CHARGING,
|
||||||
DEVICE_CLASS_PLUG,
|
DEVICE_CLASS_PLUG,
|
||||||
BinarySensorEntity,
|
BinarySensorEntity,
|
||||||
|
BinarySensorEntityDescription,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
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.helpers.typing import StateType
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
from .renault_entities import RenaultBatteryDataEntity, RenaultDataEntity
|
from .renault_entities import RenaultDataEntity, RenaultEntityDescription, T
|
||||||
from .renault_hub import RenaultHub
|
from .renault_hub import RenaultHub
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class RenaultBinarySensorRequiredKeysMixin:
|
||||||
|
"""Mixin for required keys."""
|
||||||
|
|
||||||
|
entity_class: type[RenaultBinarySensor]
|
||||||
|
on_value: StateType
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class RenaultBinarySensorEntityDescription(
|
||||||
|
BinarySensorEntityDescription,
|
||||||
|
RenaultEntityDescription,
|
||||||
|
RenaultBinarySensorRequiredKeysMixin,
|
||||||
|
):
|
||||||
|
"""Class describing Renault binary sensor entities."""
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config_entry: ConfigEntry,
|
config_entry: ConfigEntry,
|
||||||
@ -24,35 +46,43 @@ async def async_setup_entry(
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the Renault entities from config entry."""
|
"""Set up the Renault entities from config entry."""
|
||||||
proxy: RenaultHub = hass.data[DOMAIN][config_entry.entry_id]
|
proxy: RenaultHub = hass.data[DOMAIN][config_entry.entry_id]
|
||||||
entities: list[RenaultDataEntity] = []
|
entities: list[RenaultBinarySensor] = [
|
||||||
for vehicle in proxy.vehicles.values():
|
description.entity_class(vehicle, description)
|
||||||
if "battery" in vehicle.coordinators:
|
for vehicle in proxy.vehicles.values()
|
||||||
entities.append(RenaultPluggedInSensor(vehicle, "Plugged In"))
|
for description in BINARY_SENSOR_TYPES
|
||||||
entities.append(RenaultChargingSensor(vehicle, "Charging"))
|
if description.coordinator in vehicle.coordinators
|
||||||
|
]
|
||||||
async_add_entities(entities)
|
async_add_entities(entities)
|
||||||
|
|
||||||
|
|
||||||
class RenaultPluggedInSensor(RenaultBatteryDataEntity, BinarySensorEntity):
|
class RenaultBinarySensor(RenaultDataEntity[T], BinarySensorEntity):
|
||||||
"""Plugged In binary sensor."""
|
"""Mixin for binary sensor specific attributes."""
|
||||||
|
|
||||||
_attr_device_class = DEVICE_CLASS_PLUG
|
entity_description: RenaultBinarySensorEntityDescription
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self) -> bool | None:
|
def is_on(self) -> bool | None:
|
||||||
"""Return true if the binary sensor is on."""
|
"""Return true if the binary sensor is on."""
|
||||||
if (not self.data) or (self.data.plugStatus is None):
|
return self.data == self.entity_description.on_value
|
||||||
return None
|
|
||||||
return self.data.get_plug_status() == PlugState.PLUGGED
|
|
||||||
|
|
||||||
|
|
||||||
class RenaultChargingSensor(RenaultBatteryDataEntity, BinarySensorEntity):
|
BINARY_SENSOR_TYPES: tuple[RenaultBinarySensorEntityDescription, ...] = (
|
||||||
"""Charging binary sensor."""
|
RenaultBinarySensorEntityDescription(
|
||||||
|
key="plugged_in",
|
||||||
_attr_device_class = DEVICE_CLASS_BATTERY_CHARGING
|
coordinator="battery",
|
||||||
|
data_key="plugStatus",
|
||||||
@property
|
device_class=DEVICE_CLASS_PLUG,
|
||||||
def is_on(self) -> bool | None:
|
entity_class=RenaultBinarySensor[KamereonVehicleBatteryStatusData],
|
||||||
"""Return true if the binary sensor is on."""
|
name="Plugged In",
|
||||||
if (not self.data) or (self.data.chargingStatus is None):
|
on_value=PlugState.PLUGGED.value,
|
||||||
return None
|
),
|
||||||
return self.data.get_charging_status() == ChargeState.CHARGE_IN_PROGRESS
|
RenaultBinarySensorEntityDescription(
|
||||||
|
key="charging",
|
||||||
|
coordinator="battery",
|
||||||
|
data_key="chargingStatus",
|
||||||
|
device_class=DEVICE_CLASS_BATTERY_CHARGING,
|
||||||
|
entity_class=RenaultBinarySensor[KamereonVehicleBatteryStatusData],
|
||||||
|
name="Charging",
|
||||||
|
on_value=ChargeState.CHARGE_IN_PROGRESS.value,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
@ -1,103 +1,73 @@
|
|||||||
"""Base classes for Renault entities."""
|
"""Base classes for Renault entities."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Any, Generic, Optional, TypeVar
|
from collections.abc import Mapping
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Any, Optional, TypeVar, cast
|
||||||
|
|
||||||
from renault_api.kamereon.enums import ChargeState, PlugState
|
from renault_api.kamereon.models import KamereonVehicleDataAttributes
|
||||||
from renault_api.kamereon.models import (
|
|
||||||
KamereonVehicleBatteryStatusData,
|
|
||||||
KamereonVehicleChargeModeData,
|
|
||||||
KamereonVehicleCockpitData,
|
|
||||||
KamereonVehicleHvacStatusData,
|
|
||||||
)
|
|
||||||
|
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity, EntityDescription
|
||||||
|
from homeassistant.helpers.typing import StateType
|
||||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
from homeassistant.util import slugify
|
|
||||||
|
|
||||||
from .renault_vehicle import RenaultVehicleProxy
|
from .renault_vehicle import RenaultVehicleProxy
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class RenaultRequiredKeysMixin:
|
||||||
|
"""Mixin for required keys."""
|
||||||
|
|
||||||
|
coordinator: str
|
||||||
|
data_key: str
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class RenaultEntityDescription(EntityDescription, RenaultRequiredKeysMixin):
|
||||||
|
"""Class describing Renault entities."""
|
||||||
|
|
||||||
|
requires_fuel: bool | None = None
|
||||||
|
|
||||||
|
|
||||||
ATTR_LAST_UPDATE = "last_update"
|
ATTR_LAST_UPDATE = "last_update"
|
||||||
|
|
||||||
T = TypeVar("T")
|
T = TypeVar("T", bound=KamereonVehicleDataAttributes)
|
||||||
|
|
||||||
|
|
||||||
class RenaultDataEntity(Generic[T], CoordinatorEntity[Optional[T]], Entity):
|
class RenaultDataEntity(CoordinatorEntity[Optional[T]], Entity):
|
||||||
"""Implementation of a Renault entity with a data coordinator."""
|
"""Implementation of a Renault entity with a data coordinator."""
|
||||||
|
|
||||||
|
entity_description: RenaultEntityDescription
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, vehicle: RenaultVehicleProxy, entity_type: str, coordinator_key: str
|
self,
|
||||||
|
vehicle: RenaultVehicleProxy,
|
||||||
|
description: RenaultEntityDescription,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialise entity."""
|
"""Initialise entity."""
|
||||||
super().__init__(vehicle.coordinators[coordinator_key])
|
super().__init__(vehicle.coordinators[description.coordinator])
|
||||||
self.vehicle = vehicle
|
self.vehicle = vehicle
|
||||||
self._entity_type = entity_type
|
self.entity_description = description
|
||||||
self._attr_device_info = self.vehicle.device_info
|
self._attr_device_info = self.vehicle.device_info
|
||||||
self._attr_name = entity_type
|
self._attr_unique_id = f"{self.vehicle.details.vin}_{description.key}".lower()
|
||||||
self._attr_unique_id = slugify(
|
|
||||||
f"{self.vehicle.details.vin}-{self._entity_type}"
|
@property
|
||||||
|
def data(self) -> StateType:
|
||||||
|
"""Return the state of this entity."""
|
||||||
|
if self.coordinator.data is None:
|
||||||
|
return None
|
||||||
|
return cast(
|
||||||
|
StateType, getattr(self.coordinator.data, self.entity_description.data_key)
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def available(self) -> bool:
|
def extra_state_attributes(self) -> Mapping[str, Any] | None:
|
||||||
"""Return if entity is available."""
|
|
||||||
# Data can succeed, but be empty
|
|
||||||
return super().available and self.coordinator.data is not None
|
|
||||||
|
|
||||||
@property
|
|
||||||
def data(self) -> T | None:
|
|
||||||
"""Return collected data."""
|
|
||||||
return self.coordinator.data
|
|
||||||
|
|
||||||
|
|
||||||
class RenaultBatteryDataEntity(RenaultDataEntity[KamereonVehicleBatteryStatusData]):
|
|
||||||
"""Implementation of a Renault entity with battery coordinator."""
|
|
||||||
|
|
||||||
def __init__(self, vehicle: RenaultVehicleProxy, entity_type: str) -> None:
|
|
||||||
"""Initialise entity."""
|
|
||||||
super().__init__(vehicle, entity_type, "battery")
|
|
||||||
|
|
||||||
@property
|
|
||||||
def extra_state_attributes(self) -> dict[str, Any]:
|
|
||||||
"""Return the state attributes of this entity."""
|
"""Return the state attributes of this entity."""
|
||||||
last_update = self.data.timestamp if self.data else None
|
if self.entity_description.coordinator == "battery":
|
||||||
return {ATTR_LAST_UPDATE: last_update}
|
last_update = (
|
||||||
|
getattr(self.coordinator.data, "timestamp")
|
||||||
@property
|
if self.coordinator.data
|
||||||
def is_charging(self) -> bool:
|
else None
|
||||||
"""Return charge state as boolean."""
|
)
|
||||||
return (
|
return {ATTR_LAST_UPDATE: last_update}
|
||||||
self.data is not None
|
return None
|
||||||
and self.data.get_charging_status() == ChargeState.CHARGE_IN_PROGRESS
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_plugged_in(self) -> bool:
|
|
||||||
"""Return plug state as boolean."""
|
|
||||||
return (
|
|
||||||
self.data is not None and self.data.get_plug_status() == PlugState.PLUGGED
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class RenaultChargeModeDataEntity(RenaultDataEntity[KamereonVehicleChargeModeData]):
|
|
||||||
"""Implementation of a Renault entity with charge_mode coordinator."""
|
|
||||||
|
|
||||||
def __init__(self, vehicle: RenaultVehicleProxy, entity_type: str) -> None:
|
|
||||||
"""Initialise entity."""
|
|
||||||
super().__init__(vehicle, entity_type, "charge_mode")
|
|
||||||
|
|
||||||
|
|
||||||
class RenaultCockpitDataEntity(RenaultDataEntity[KamereonVehicleCockpitData]):
|
|
||||||
"""Implementation of a Renault entity with cockpit coordinator."""
|
|
||||||
|
|
||||||
def __init__(self, vehicle: RenaultVehicleProxy, entity_type: str) -> None:
|
|
||||||
"""Initialise entity."""
|
|
||||||
super().__init__(vehicle, entity_type, "cockpit")
|
|
||||||
|
|
||||||
|
|
||||||
class RenaultHVACDataEntity(RenaultDataEntity[KamereonVehicleHvacStatusData]):
|
|
||||||
"""Implementation of a Renault entity with hvac_status coordinator."""
|
|
||||||
|
|
||||||
def __init__(self, vehicle: RenaultVehicleProxy, entity_type: str) -> None:
|
|
||||||
"""Initialise entity."""
|
|
||||||
super().__init__(vehicle, entity_type, "hvac_status")
|
|
||||||
|
@ -1,7 +1,23 @@
|
|||||||
"""Support for Renault sensors."""
|
"""Support for Renault sensors."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from homeassistant.components.sensor import SensorEntity
|
from dataclasses import dataclass
|
||||||
|
from typing import Callable, cast
|
||||||
|
|
||||||
|
from renault_api.kamereon.enums import ChargeState, PlugState
|
||||||
|
from renault_api.kamereon.models import (
|
||||||
|
KamereonVehicleBatteryStatusData,
|
||||||
|
KamereonVehicleChargeModeData,
|
||||||
|
KamereonVehicleCockpitData,
|
||||||
|
KamereonVehicleHvacStatusData,
|
||||||
|
)
|
||||||
|
|
||||||
|
from homeassistant.components.sensor import (
|
||||||
|
STATE_CLASS_MEASUREMENT,
|
||||||
|
STATE_CLASS_TOTAL_INCREASING,
|
||||||
|
SensorEntity,
|
||||||
|
SensorEntityDescription,
|
||||||
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
DEVICE_CLASS_BATTERY,
|
DEVICE_CLASS_BATTERY,
|
||||||
@ -18,6 +34,7 @@ from homeassistant.const import (
|
|||||||
)
|
)
|
||||||
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.helpers.typing import StateType
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
DEVICE_CLASS_CHARGE_MODE,
|
DEVICE_CLASS_CHARGE_MODE,
|
||||||
@ -25,17 +42,25 @@ from .const import (
|
|||||||
DEVICE_CLASS_PLUG_STATE,
|
DEVICE_CLASS_PLUG_STATE,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
)
|
)
|
||||||
from .renault_entities import (
|
from .renault_entities import RenaultDataEntity, RenaultEntityDescription, T
|
||||||
RenaultBatteryDataEntity,
|
|
||||||
RenaultChargeModeDataEntity,
|
|
||||||
RenaultCockpitDataEntity,
|
|
||||||
RenaultDataEntity,
|
|
||||||
RenaultHVACDataEntity,
|
|
||||||
)
|
|
||||||
from .renault_hub import RenaultHub
|
from .renault_hub import RenaultHub
|
||||||
from .renault_vehicle import RenaultVehicleProxy
|
|
||||||
|
|
||||||
ATTR_BATTERY_AVAILABLE_ENERGY = "battery_available_energy"
|
|
||||||
|
@dataclass
|
||||||
|
class RenaultSensorRequiredKeysMixin:
|
||||||
|
"""Mixin for required keys."""
|
||||||
|
|
||||||
|
entity_class: type[RenaultSensor]
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class RenaultSensorEntityDescription(
|
||||||
|
SensorEntityDescription, RenaultEntityDescription, RenaultSensorRequiredKeysMixin
|
||||||
|
):
|
||||||
|
"""Class describing Renault sensor entities."""
|
||||||
|
|
||||||
|
icon_lambda: Callable[[RenaultDataEntity[T]], str] | None = None
|
||||||
|
value_lambda: Callable[[RenaultDataEntity[T]], StateType] | None = None
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
@ -45,224 +70,208 @@ async def async_setup_entry(
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the Renault entities from config entry."""
|
"""Set up the Renault entities from config entry."""
|
||||||
proxy: RenaultHub = hass.data[DOMAIN][config_entry.entry_id]
|
proxy: RenaultHub = hass.data[DOMAIN][config_entry.entry_id]
|
||||||
entities = get_entities(proxy)
|
entities: list[RenaultSensor] = [
|
||||||
|
description.entity_class(vehicle, description)
|
||||||
|
for vehicle in proxy.vehicles.values()
|
||||||
|
for description in SENSOR_TYPES
|
||||||
|
if description.coordinator in vehicle.coordinators
|
||||||
|
and (not description.requires_fuel or vehicle.details.uses_fuel())
|
||||||
|
]
|
||||||
async_add_entities(entities)
|
async_add_entities(entities)
|
||||||
|
|
||||||
|
|
||||||
def get_entities(proxy: RenaultHub) -> list[RenaultDataEntity]:
|
class RenaultSensor(RenaultDataEntity[T], SensorEntity):
|
||||||
"""Create Renault entities for all vehicles."""
|
"""Mixin for sensor specific attributes."""
|
||||||
entities = []
|
|
||||||
for vehicle in proxy.vehicles.values():
|
|
||||||
entities.extend(get_vehicle_entities(vehicle))
|
|
||||||
return entities
|
|
||||||
|
|
||||||
|
entity_description: RenaultSensorEntityDescription
|
||||||
def get_vehicle_entities(vehicle: RenaultVehicleProxy) -> list[RenaultDataEntity]:
|
|
||||||
"""Create Renault entities for single vehicle."""
|
|
||||||
entities: list[RenaultDataEntity] = []
|
|
||||||
if "cockpit" in vehicle.coordinators:
|
|
||||||
entities.append(RenaultMileageSensor(vehicle, "Mileage"))
|
|
||||||
if vehicle.details.uses_fuel():
|
|
||||||
entities.append(RenaultFuelAutonomySensor(vehicle, "Fuel Autonomy"))
|
|
||||||
entities.append(RenaultFuelQuantitySensor(vehicle, "Fuel Quantity"))
|
|
||||||
if "hvac_status" in vehicle.coordinators:
|
|
||||||
entities.append(RenaultOutsideTemperatureSensor(vehicle, "Outside Temperature"))
|
|
||||||
if "battery" in vehicle.coordinators:
|
|
||||||
entities.append(RenaultBatteryLevelSensor(vehicle, "Battery Level"))
|
|
||||||
entities.append(RenaultChargeStateSensor(vehicle, "Charge State"))
|
|
||||||
entities.append(
|
|
||||||
RenaultChargingRemainingTimeSensor(vehicle, "Charging Remaining Time")
|
|
||||||
)
|
|
||||||
entities.append(RenaultChargingPowerSensor(vehicle, "Charging Power"))
|
|
||||||
entities.append(RenaultPlugStateSensor(vehicle, "Plug State"))
|
|
||||||
entities.append(RenaultBatteryAutonomySensor(vehicle, "Battery Autonomy"))
|
|
||||||
entities.append(
|
|
||||||
RenaultBatteryAvailableEnergySensor(vehicle, "Battery Available Energy")
|
|
||||||
)
|
|
||||||
entities.append(RenaultBatteryTemperatureSensor(vehicle, "Battery Temperature"))
|
|
||||||
if "charge_mode" in vehicle.coordinators:
|
|
||||||
entities.append(RenaultChargeModeSensor(vehicle, "Charge Mode"))
|
|
||||||
return entities
|
|
||||||
|
|
||||||
|
|
||||||
class RenaultBatteryAutonomySensor(RenaultBatteryDataEntity, SensorEntity):
|
|
||||||
"""Battery autonomy sensor."""
|
|
||||||
|
|
||||||
_attr_icon = "mdi:ev-station"
|
|
||||||
_attr_native_unit_of_measurement = LENGTH_KILOMETERS
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def native_value(self) -> int | None:
|
def icon(self) -> str | None:
|
||||||
"""Return the state of this entity."""
|
|
||||||
return self.data.batteryAutonomy if self.data else None
|
|
||||||
|
|
||||||
|
|
||||||
class RenaultBatteryAvailableEnergySensor(RenaultBatteryDataEntity, SensorEntity):
|
|
||||||
"""Battery available energy sensor."""
|
|
||||||
|
|
||||||
_attr_device_class = DEVICE_CLASS_ENERGY
|
|
||||||
_attr_native_unit_of_measurement = ENERGY_KILO_WATT_HOUR
|
|
||||||
|
|
||||||
@property
|
|
||||||
def native_value(self) -> float | None:
|
|
||||||
"""Return the state of this entity."""
|
|
||||||
return self.data.batteryAvailableEnergy if self.data else None
|
|
||||||
|
|
||||||
|
|
||||||
class RenaultBatteryLevelSensor(RenaultBatteryDataEntity, SensorEntity):
|
|
||||||
"""Battery Level sensor."""
|
|
||||||
|
|
||||||
_attr_device_class = DEVICE_CLASS_BATTERY
|
|
||||||
_attr_native_unit_of_measurement = PERCENTAGE
|
|
||||||
|
|
||||||
@property
|
|
||||||
def native_value(self) -> int | None:
|
|
||||||
"""Return the state of this entity."""
|
|
||||||
return self.data.batteryLevel if self.data else None
|
|
||||||
|
|
||||||
|
|
||||||
class RenaultBatteryTemperatureSensor(RenaultBatteryDataEntity, SensorEntity):
|
|
||||||
"""Battery Temperature sensor."""
|
|
||||||
|
|
||||||
_attr_device_class = DEVICE_CLASS_TEMPERATURE
|
|
||||||
_attr_native_unit_of_measurement = TEMP_CELSIUS
|
|
||||||
|
|
||||||
@property
|
|
||||||
def native_value(self) -> int | None:
|
|
||||||
"""Return the state of this entity."""
|
|
||||||
return self.data.batteryTemperature if self.data else None
|
|
||||||
|
|
||||||
|
|
||||||
class RenaultChargeModeSensor(RenaultChargeModeDataEntity, SensorEntity):
|
|
||||||
"""Charge Mode sensor."""
|
|
||||||
|
|
||||||
_attr_device_class = DEVICE_CLASS_CHARGE_MODE
|
|
||||||
|
|
||||||
@property
|
|
||||||
def native_value(self) -> str | None:
|
|
||||||
"""Return the state of this entity."""
|
|
||||||
return self.data.chargeMode if self.data else None
|
|
||||||
|
|
||||||
@property
|
|
||||||
def icon(self) -> str:
|
|
||||||
"""Icon handling."""
|
"""Icon handling."""
|
||||||
if self.data and self.data.chargeMode == "schedule_mode":
|
if self.entity_description.icon_lambda is None:
|
||||||
return "mdi:calendar-clock"
|
return super().icon
|
||||||
return "mdi:calendar-remove"
|
return self.entity_description.icon_lambda(self)
|
||||||
|
|
||||||
|
|
||||||
class RenaultChargeStateSensor(RenaultBatteryDataEntity, SensorEntity):
|
|
||||||
"""Charge State sensor."""
|
|
||||||
|
|
||||||
_attr_device_class = DEVICE_CLASS_CHARGE_STATE
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def native_value(self) -> str | None:
|
def native_value(self) -> StateType:
|
||||||
"""Return the state of this entity."""
|
"""Return the state of this entity."""
|
||||||
charging_status = self.data.get_charging_status() if self.data else None
|
if self.data is None:
|
||||||
return charging_status.name.lower() if charging_status is not None else None
|
|
||||||
|
|
||||||
@property
|
|
||||||
def icon(self) -> str:
|
|
||||||
"""Icon handling."""
|
|
||||||
return "mdi:flash" if self.is_charging else "mdi:flash-off"
|
|
||||||
|
|
||||||
|
|
||||||
class RenaultChargingRemainingTimeSensor(RenaultBatteryDataEntity, SensorEntity):
|
|
||||||
"""Charging Remaining Time sensor."""
|
|
||||||
|
|
||||||
_attr_icon = "mdi:timer"
|
|
||||||
_attr_native_unit_of_measurement = TIME_MINUTES
|
|
||||||
|
|
||||||
@property
|
|
||||||
def native_value(self) -> int | None:
|
|
||||||
"""Return the state of this entity."""
|
|
||||||
return self.data.chargingRemainingTime if self.data else None
|
|
||||||
|
|
||||||
|
|
||||||
class RenaultChargingPowerSensor(RenaultBatteryDataEntity, SensorEntity):
|
|
||||||
"""Charging Power sensor."""
|
|
||||||
|
|
||||||
_attr_device_class = DEVICE_CLASS_POWER
|
|
||||||
_attr_native_unit_of_measurement = POWER_KILO_WATT
|
|
||||||
|
|
||||||
@property
|
|
||||||
def native_value(self) -> float | None:
|
|
||||||
"""Return the state of this entity."""
|
|
||||||
if not self.data or self.data.chargingInstantaneousPower is None:
|
|
||||||
return None
|
return None
|
||||||
if self.vehicle.details.reports_charging_power_in_watts():
|
if self.entity_description.value_lambda is None:
|
||||||
# Need to convert to kilowatts
|
return self.data
|
||||||
return self.data.chargingInstantaneousPower / 1000
|
return self.entity_description.value_lambda(self)
|
||||||
return self.data.chargingInstantaneousPower
|
|
||||||
|
|
||||||
|
|
||||||
class RenaultFuelAutonomySensor(RenaultCockpitDataEntity, SensorEntity):
|
def _get_formatted_charging_status(
|
||||||
"""Fuel autonomy sensor."""
|
data: KamereonVehicleBatteryStatusData,
|
||||||
|
) -> str | None:
|
||||||
_attr_icon = "mdi:gas-station"
|
"""Return the charging_status of this entity."""
|
||||||
_attr_native_unit_of_measurement = LENGTH_KILOMETERS
|
charging_status = data.get_charging_status() if data else None
|
||||||
|
return charging_status.name.lower() if charging_status else None
|
||||||
@property
|
|
||||||
def native_value(self) -> int | None:
|
|
||||||
"""Return the state of this entity."""
|
|
||||||
if not self.data or self.data.fuelAutonomy is None:
|
|
||||||
return None
|
|
||||||
return round(self.data.fuelAutonomy)
|
|
||||||
|
|
||||||
|
|
||||||
class RenaultFuelQuantitySensor(RenaultCockpitDataEntity, SensorEntity):
|
def _get_formatted_plug_status(data: KamereonVehicleBatteryStatusData) -> str | None:
|
||||||
"""Fuel quantity sensor."""
|
"""Return the plug_status of this entity."""
|
||||||
|
plug_status = data.get_plug_status() if data else None
|
||||||
_attr_icon = "mdi:fuel"
|
return plug_status.name.lower() if plug_status else None
|
||||||
_attr_native_unit_of_measurement = VOLUME_LITERS
|
|
||||||
|
|
||||||
@property
|
|
||||||
def native_value(self) -> int | None:
|
|
||||||
"""Return the state of this entity."""
|
|
||||||
if not self.data or self.data.fuelQuantity is None:
|
|
||||||
return None
|
|
||||||
return round(self.data.fuelQuantity)
|
|
||||||
|
|
||||||
|
|
||||||
class RenaultMileageSensor(RenaultCockpitDataEntity, SensorEntity):
|
SENSOR_TYPES: tuple[RenaultSensorEntityDescription, ...] = (
|
||||||
"""Mileage sensor."""
|
RenaultSensorEntityDescription(
|
||||||
|
key="battery_level",
|
||||||
_attr_icon = "mdi:sign-direction"
|
coordinator="battery",
|
||||||
_attr_native_unit_of_measurement = LENGTH_KILOMETERS
|
data_key="batteryLevel",
|
||||||
|
device_class=DEVICE_CLASS_BATTERY,
|
||||||
@property
|
entity_class=RenaultSensor[KamereonVehicleBatteryStatusData],
|
||||||
def native_value(self) -> int | None:
|
name="Battery Level",
|
||||||
"""Return the state of this entity."""
|
native_unit_of_measurement=PERCENTAGE,
|
||||||
if not self.data or self.data.totalMileage is None:
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
return None
|
),
|
||||||
return round(self.data.totalMileage)
|
RenaultSensorEntityDescription(
|
||||||
|
key="charge_state",
|
||||||
|
coordinator="battery",
|
||||||
class RenaultOutsideTemperatureSensor(RenaultHVACDataEntity, SensorEntity):
|
data_key="chargingStatus",
|
||||||
"""HVAC Outside Temperature sensor."""
|
device_class=DEVICE_CLASS_CHARGE_STATE,
|
||||||
|
entity_class=RenaultSensor[KamereonVehicleBatteryStatusData],
|
||||||
_attr_device_class = DEVICE_CLASS_TEMPERATURE
|
icon_lambda=lambda x: (
|
||||||
_attr_native_unit_of_measurement = TEMP_CELSIUS
|
"mdi:flash"
|
||||||
|
if x.data == ChargeState.CHARGE_IN_PROGRESS.value
|
||||||
@property
|
else "mdi:flash-off"
|
||||||
def native_value(self) -> float | None:
|
),
|
||||||
"""Return the state of this entity."""
|
name="Charge State",
|
||||||
return self.data.externalTemperature if self.data else None
|
value_lambda=lambda x: (
|
||||||
|
_get_formatted_charging_status(
|
||||||
|
cast(KamereonVehicleBatteryStatusData, x.coordinator.data)
|
||||||
class RenaultPlugStateSensor(RenaultBatteryDataEntity, SensorEntity):
|
)
|
||||||
"""Plug State sensor."""
|
),
|
||||||
|
),
|
||||||
_attr_device_class = DEVICE_CLASS_PLUG_STATE
|
RenaultSensorEntityDescription(
|
||||||
|
key="charging_remaining_time",
|
||||||
@property
|
coordinator="battery",
|
||||||
def native_value(self) -> str | None:
|
data_key="chargingRemainingTime",
|
||||||
"""Return the state of this entity."""
|
entity_class=RenaultSensor[KamereonVehicleBatteryStatusData],
|
||||||
plug_status = self.data.get_plug_status() if self.data else None
|
icon="mdi:timer",
|
||||||
return plug_status.name.lower() if plug_status is not None else None
|
name="Charging Remaining Time",
|
||||||
|
native_unit_of_measurement=TIME_MINUTES,
|
||||||
@property
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
def icon(self) -> str:
|
),
|
||||||
"""Icon handling."""
|
RenaultSensorEntityDescription(
|
||||||
return "mdi:power-plug" if self.is_plugged_in else "mdi:power-plug-off"
|
key="charging_power",
|
||||||
|
coordinator="battery",
|
||||||
|
data_key="chargingInstantaneousPower",
|
||||||
|
device_class=DEVICE_CLASS_POWER,
|
||||||
|
entity_class=RenaultSensor[KamereonVehicleBatteryStatusData],
|
||||||
|
name="Charging Power",
|
||||||
|
native_unit_of_measurement=POWER_KILO_WATT,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
value_lambda=lambda x: (
|
||||||
|
cast(float, x.data) / 1000
|
||||||
|
if x.vehicle.details.reports_charging_power_in_watts()
|
||||||
|
else x.data
|
||||||
|
),
|
||||||
|
),
|
||||||
|
RenaultSensorEntityDescription(
|
||||||
|
key="plug_state",
|
||||||
|
coordinator="battery",
|
||||||
|
data_key="plugStatus",
|
||||||
|
device_class=DEVICE_CLASS_PLUG_STATE,
|
||||||
|
entity_class=RenaultSensor[KamereonVehicleBatteryStatusData],
|
||||||
|
icon_lambda=lambda x: (
|
||||||
|
"mdi:power-plug"
|
||||||
|
if x.data == PlugState.PLUGGED.value
|
||||||
|
else "mdi:power-plug-off"
|
||||||
|
),
|
||||||
|
name="Plug State",
|
||||||
|
value_lambda=lambda x: (
|
||||||
|
_get_formatted_plug_status(
|
||||||
|
cast(KamereonVehicleBatteryStatusData, x.coordinator.data)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
RenaultSensorEntityDescription(
|
||||||
|
key="battery_autonomy",
|
||||||
|
coordinator="battery",
|
||||||
|
data_key="batteryAutonomy",
|
||||||
|
entity_class=RenaultSensor[KamereonVehicleBatteryStatusData],
|
||||||
|
icon="mdi:ev-station",
|
||||||
|
name="Battery Autonomy",
|
||||||
|
native_unit_of_measurement=LENGTH_KILOMETERS,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
RenaultSensorEntityDescription(
|
||||||
|
key="battery_available_energy",
|
||||||
|
coordinator="battery",
|
||||||
|
data_key="batteryAvailableEnergy",
|
||||||
|
entity_class=RenaultSensor[KamereonVehicleBatteryStatusData],
|
||||||
|
device_class=DEVICE_CLASS_ENERGY,
|
||||||
|
name="Battery Available Energy",
|
||||||
|
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
RenaultSensorEntityDescription(
|
||||||
|
key="battery_temperature",
|
||||||
|
coordinator="battery",
|
||||||
|
data_key="batteryTemperature",
|
||||||
|
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||||
|
entity_class=RenaultSensor[KamereonVehicleBatteryStatusData],
|
||||||
|
name="Battery Temperature",
|
||||||
|
native_unit_of_measurement=TEMP_CELSIUS,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
RenaultSensorEntityDescription(
|
||||||
|
key="mileage",
|
||||||
|
coordinator="cockpit",
|
||||||
|
data_key="totalMileage",
|
||||||
|
entity_class=RenaultSensor[KamereonVehicleCockpitData],
|
||||||
|
icon="mdi:sign-direction",
|
||||||
|
name="Mileage",
|
||||||
|
native_unit_of_measurement=LENGTH_KILOMETERS,
|
||||||
|
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||||
|
value_lambda=lambda x: round(cast(float, x.data)),
|
||||||
|
),
|
||||||
|
RenaultSensorEntityDescription(
|
||||||
|
key="fuel_autonomy",
|
||||||
|
coordinator="cockpit",
|
||||||
|
data_key="fuelAutonomy",
|
||||||
|
entity_class=RenaultSensor[KamereonVehicleCockpitData],
|
||||||
|
icon="mdi:gas-station",
|
||||||
|
name="Fuel Autonomy",
|
||||||
|
native_unit_of_measurement=LENGTH_KILOMETERS,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
requires_fuel=True,
|
||||||
|
value_lambda=lambda x: round(cast(float, x.data)),
|
||||||
|
),
|
||||||
|
RenaultSensorEntityDescription(
|
||||||
|
key="fuel_quantity",
|
||||||
|
coordinator="cockpit",
|
||||||
|
data_key="fuelQuantity",
|
||||||
|
entity_class=RenaultSensor[KamereonVehicleCockpitData],
|
||||||
|
icon="mdi:fuel",
|
||||||
|
name="Fuel Quantity",
|
||||||
|
native_unit_of_measurement=VOLUME_LITERS,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
requires_fuel=True,
|
||||||
|
value_lambda=lambda x: round(cast(float, x.data)),
|
||||||
|
),
|
||||||
|
RenaultSensorEntityDescription(
|
||||||
|
key="outside_temperature",
|
||||||
|
coordinator="hvac_status",
|
||||||
|
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||||
|
data_key="externalTemperature",
|
||||||
|
entity_class=RenaultSensor[KamereonVehicleHvacStatusData],
|
||||||
|
name="Outside Temperature",
|
||||||
|
native_unit_of_measurement=TEMP_CELSIUS,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
RenaultSensorEntityDescription(
|
||||||
|
key="charge_mode",
|
||||||
|
coordinator="charge_mode",
|
||||||
|
data_key="chargeMode",
|
||||||
|
device_class=DEVICE_CLASS_CHARGE_MODE,
|
||||||
|
entity_class=RenaultSensor[KamereonVehicleChargeModeData],
|
||||||
|
icon_lambda=lambda x: (
|
||||||
|
"mdi:calendar-clock" if x.data == "schedule_mode" else "mdi:calendar-remove"
|
||||||
|
),
|
||||||
|
name="Charge Mode",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
@ -12,8 +12,17 @@ from homeassistant.components.renault.const import (
|
|||||||
DEVICE_CLASS_PLUG_STATE,
|
DEVICE_CLASS_PLUG_STATE,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
)
|
)
|
||||||
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
from homeassistant.components.renault.renault_entities import ATTR_LAST_UPDATE
|
||||||
|
from homeassistant.components.sensor import (
|
||||||
|
ATTR_STATE_CLASS,
|
||||||
|
DOMAIN as SENSOR_DOMAIN,
|
||||||
|
STATE_CLASS_MEASUREMENT,
|
||||||
|
STATE_CLASS_TOTAL_INCREASING,
|
||||||
|
)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
|
ATTR_DEVICE_CLASS,
|
||||||
|
ATTR_ICON,
|
||||||
|
ATTR_UNIT_OF_MEASUREMENT,
|
||||||
CONF_PASSWORD,
|
CONF_PASSWORD,
|
||||||
CONF_USERNAME,
|
CONF_USERNAME,
|
||||||
DEVICE_CLASS_BATTERY,
|
DEVICE_CLASS_BATTERY,
|
||||||
@ -32,6 +41,14 @@ from homeassistant.const import (
|
|||||||
VOLUME_LITERS,
|
VOLUME_LITERS,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
CHECK_ATTRIBUTES = (
|
||||||
|
ATTR_DEVICE_CLASS,
|
||||||
|
ATTR_ICON,
|
||||||
|
ATTR_LAST_UPDATE,
|
||||||
|
ATTR_STATE_CLASS,
|
||||||
|
ATTR_UNIT_OF_MEASUREMENT,
|
||||||
|
)
|
||||||
|
|
||||||
# Mock config data to be used across multiple tests
|
# Mock config data to be used across multiple tests
|
||||||
MOCK_CONFIG = {
|
MOCK_CONFIG = {
|
||||||
CONF_USERNAME: "email@test.com",
|
CONF_USERNAME: "email@test.com",
|
||||||
@ -66,13 +83,15 @@ MOCK_VEHICLES = {
|
|||||||
"entity_id": "binary_sensor.plugged_in",
|
"entity_id": "binary_sensor.plugged_in",
|
||||||
"unique_id": "vf1aaaaa555777999_plugged_in",
|
"unique_id": "vf1aaaaa555777999_plugged_in",
|
||||||
"result": STATE_ON,
|
"result": STATE_ON,
|
||||||
"class": DEVICE_CLASS_PLUG,
|
ATTR_DEVICE_CLASS: DEVICE_CLASS_PLUG,
|
||||||
|
ATTR_LAST_UPDATE: "2020-01-12T21:40:16Z",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"entity_id": "binary_sensor.charging",
|
"entity_id": "binary_sensor.charging",
|
||||||
"unique_id": "vf1aaaaa555777999_charging",
|
"unique_id": "vf1aaaaa555777999_charging",
|
||||||
"result": STATE_ON,
|
"result": STATE_ON,
|
||||||
"class": DEVICE_CLASS_BATTERY_CHARGING,
|
ATTR_DEVICE_CLASS: DEVICE_CLASS_BATTERY_CHARGING,
|
||||||
|
ATTR_LAST_UPDATE: "2020-01-12T21:40:16Z",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
SENSOR_DOMAIN: [
|
SENSOR_DOMAIN: [
|
||||||
@ -80,72 +99,94 @@ MOCK_VEHICLES = {
|
|||||||
"entity_id": "sensor.battery_autonomy",
|
"entity_id": "sensor.battery_autonomy",
|
||||||
"unique_id": "vf1aaaaa555777999_battery_autonomy",
|
"unique_id": "vf1aaaaa555777999_battery_autonomy",
|
||||||
"result": "141",
|
"result": "141",
|
||||||
"unit": LENGTH_KILOMETERS,
|
ATTR_ICON: "mdi:ev-station",
|
||||||
|
ATTR_LAST_UPDATE: "2020-01-12T21:40:16Z",
|
||||||
|
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||||
|
ATTR_UNIT_OF_MEASUREMENT: LENGTH_KILOMETERS,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"entity_id": "sensor.battery_available_energy",
|
"entity_id": "sensor.battery_available_energy",
|
||||||
"unique_id": "vf1aaaaa555777999_battery_available_energy",
|
"unique_id": "vf1aaaaa555777999_battery_available_energy",
|
||||||
"result": "31",
|
"result": "31",
|
||||||
"unit": ENERGY_KILO_WATT_HOUR,
|
ATTR_DEVICE_CLASS: DEVICE_CLASS_ENERGY,
|
||||||
"class": DEVICE_CLASS_ENERGY,
|
ATTR_LAST_UPDATE: "2020-01-12T21:40:16Z",
|
||||||
|
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||||
|
ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"entity_id": "sensor.battery_level",
|
"entity_id": "sensor.battery_level",
|
||||||
"unique_id": "vf1aaaaa555777999_battery_level",
|
"unique_id": "vf1aaaaa555777999_battery_level",
|
||||||
"result": "60",
|
"result": "60",
|
||||||
"unit": PERCENTAGE,
|
ATTR_DEVICE_CLASS: DEVICE_CLASS_BATTERY,
|
||||||
"class": DEVICE_CLASS_BATTERY,
|
ATTR_LAST_UPDATE: "2020-01-12T21:40:16Z",
|
||||||
|
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||||
|
ATTR_UNIT_OF_MEASUREMENT: PERCENTAGE,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"entity_id": "sensor.battery_temperature",
|
"entity_id": "sensor.battery_temperature",
|
||||||
"unique_id": "vf1aaaaa555777999_battery_temperature",
|
"unique_id": "vf1aaaaa555777999_battery_temperature",
|
||||||
"result": "20",
|
"result": "20",
|
||||||
"unit": TEMP_CELSIUS,
|
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
|
||||||
"class": DEVICE_CLASS_TEMPERATURE,
|
ATTR_LAST_UPDATE: "2020-01-12T21:40:16Z",
|
||||||
|
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||||
|
ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"entity_id": "sensor.charge_mode",
|
"entity_id": "sensor.charge_mode",
|
||||||
"unique_id": "vf1aaaaa555777999_charge_mode",
|
"unique_id": "vf1aaaaa555777999_charge_mode",
|
||||||
"result": "always",
|
"result": "always",
|
||||||
"class": DEVICE_CLASS_CHARGE_MODE,
|
ATTR_DEVICE_CLASS: DEVICE_CLASS_CHARGE_MODE,
|
||||||
|
ATTR_ICON: "mdi:calendar-remove",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"entity_id": "sensor.charge_state",
|
"entity_id": "sensor.charge_state",
|
||||||
"unique_id": "vf1aaaaa555777999_charge_state",
|
"unique_id": "vf1aaaaa555777999_charge_state",
|
||||||
"result": "charge_in_progress",
|
"result": "charge_in_progress",
|
||||||
"class": DEVICE_CLASS_CHARGE_STATE,
|
ATTR_DEVICE_CLASS: DEVICE_CLASS_CHARGE_STATE,
|
||||||
|
ATTR_ICON: "mdi:flash",
|
||||||
|
ATTR_LAST_UPDATE: "2020-01-12T21:40:16Z",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"entity_id": "sensor.charging_power",
|
"entity_id": "sensor.charging_power",
|
||||||
"unique_id": "vf1aaaaa555777999_charging_power",
|
"unique_id": "vf1aaaaa555777999_charging_power",
|
||||||
"result": "0.027",
|
"result": "0.027",
|
||||||
"unit": POWER_KILO_WATT,
|
ATTR_DEVICE_CLASS: DEVICE_CLASS_POWER,
|
||||||
"class": DEVICE_CLASS_POWER,
|
ATTR_LAST_UPDATE: "2020-01-12T21:40:16Z",
|
||||||
|
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||||
|
ATTR_UNIT_OF_MEASUREMENT: POWER_KILO_WATT,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"entity_id": "sensor.charging_remaining_time",
|
"entity_id": "sensor.charging_remaining_time",
|
||||||
"unique_id": "vf1aaaaa555777999_charging_remaining_time",
|
"unique_id": "vf1aaaaa555777999_charging_remaining_time",
|
||||||
"result": "145",
|
"result": "145",
|
||||||
"unit": TIME_MINUTES,
|
ATTR_ICON: "mdi:timer",
|
||||||
|
ATTR_LAST_UPDATE: "2020-01-12T21:40:16Z",
|
||||||
|
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||||
|
ATTR_UNIT_OF_MEASUREMENT: TIME_MINUTES,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"entity_id": "sensor.mileage",
|
"entity_id": "sensor.mileage",
|
||||||
"unique_id": "vf1aaaaa555777999_mileage",
|
"unique_id": "vf1aaaaa555777999_mileage",
|
||||||
"result": "49114",
|
"result": "49114",
|
||||||
"unit": LENGTH_KILOMETERS,
|
ATTR_ICON: "mdi:sign-direction",
|
||||||
|
ATTR_STATE_CLASS: STATE_CLASS_TOTAL_INCREASING,
|
||||||
|
ATTR_UNIT_OF_MEASUREMENT: LENGTH_KILOMETERS,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"entity_id": "sensor.outside_temperature",
|
"entity_id": "sensor.outside_temperature",
|
||||||
"unique_id": "vf1aaaaa555777999_outside_temperature",
|
"unique_id": "vf1aaaaa555777999_outside_temperature",
|
||||||
"result": "8.0",
|
"result": "8.0",
|
||||||
"unit": TEMP_CELSIUS,
|
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
|
||||||
"class": DEVICE_CLASS_TEMPERATURE,
|
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||||
|
ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"entity_id": "sensor.plug_state",
|
"entity_id": "sensor.plug_state",
|
||||||
"unique_id": "vf1aaaaa555777999_plug_state",
|
"unique_id": "vf1aaaaa555777999_plug_state",
|
||||||
"result": "plugged",
|
"result": "plugged",
|
||||||
"class": DEVICE_CLASS_PLUG_STATE,
|
ATTR_DEVICE_CLASS: DEVICE_CLASS_PLUG_STATE,
|
||||||
|
ATTR_ICON: "mdi:power-plug",
|
||||||
|
ATTR_LAST_UPDATE: "2020-01-12T21:40:16Z",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -173,13 +214,15 @@ MOCK_VEHICLES = {
|
|||||||
"entity_id": "binary_sensor.plugged_in",
|
"entity_id": "binary_sensor.plugged_in",
|
||||||
"unique_id": "vf1aaaaa555777999_plugged_in",
|
"unique_id": "vf1aaaaa555777999_plugged_in",
|
||||||
"result": STATE_OFF,
|
"result": STATE_OFF,
|
||||||
"class": DEVICE_CLASS_PLUG,
|
ATTR_DEVICE_CLASS: DEVICE_CLASS_PLUG,
|
||||||
|
ATTR_LAST_UPDATE: "2020-11-17T09:06:48+01:00",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"entity_id": "binary_sensor.charging",
|
"entity_id": "binary_sensor.charging",
|
||||||
"unique_id": "vf1aaaaa555777999_charging",
|
"unique_id": "vf1aaaaa555777999_charging",
|
||||||
"result": STATE_OFF,
|
"result": STATE_OFF,
|
||||||
"class": DEVICE_CLASS_BATTERY_CHARGING,
|
ATTR_DEVICE_CLASS: DEVICE_CLASS_BATTERY_CHARGING,
|
||||||
|
ATTR_LAST_UPDATE: "2020-11-17T09:06:48+01:00",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
SENSOR_DOMAIN: [
|
SENSOR_DOMAIN: [
|
||||||
@ -187,65 +230,86 @@ MOCK_VEHICLES = {
|
|||||||
"entity_id": "sensor.battery_autonomy",
|
"entity_id": "sensor.battery_autonomy",
|
||||||
"unique_id": "vf1aaaaa555777999_battery_autonomy",
|
"unique_id": "vf1aaaaa555777999_battery_autonomy",
|
||||||
"result": "128",
|
"result": "128",
|
||||||
"unit": LENGTH_KILOMETERS,
|
ATTR_ICON: "mdi:ev-station",
|
||||||
|
ATTR_LAST_UPDATE: "2020-11-17T09:06:48+01:00",
|
||||||
|
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||||
|
ATTR_UNIT_OF_MEASUREMENT: LENGTH_KILOMETERS,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"entity_id": "sensor.battery_available_energy",
|
"entity_id": "sensor.battery_available_energy",
|
||||||
"unique_id": "vf1aaaaa555777999_battery_available_energy",
|
"unique_id": "vf1aaaaa555777999_battery_available_energy",
|
||||||
"result": "0",
|
"result": "0",
|
||||||
"unit": ENERGY_KILO_WATT_HOUR,
|
ATTR_DEVICE_CLASS: DEVICE_CLASS_ENERGY,
|
||||||
"class": DEVICE_CLASS_ENERGY,
|
ATTR_LAST_UPDATE: "2020-11-17T09:06:48+01:00",
|
||||||
|
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||||
|
ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"entity_id": "sensor.battery_level",
|
"entity_id": "sensor.battery_level",
|
||||||
"unique_id": "vf1aaaaa555777999_battery_level",
|
"unique_id": "vf1aaaaa555777999_battery_level",
|
||||||
"result": "50",
|
"result": "50",
|
||||||
"unit": PERCENTAGE,
|
ATTR_DEVICE_CLASS: DEVICE_CLASS_BATTERY,
|
||||||
"class": DEVICE_CLASS_BATTERY,
|
ATTR_LAST_UPDATE: "2020-11-17T09:06:48+01:00",
|
||||||
|
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||||
|
ATTR_UNIT_OF_MEASUREMENT: PERCENTAGE,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"entity_id": "sensor.battery_temperature",
|
"entity_id": "sensor.battery_temperature",
|
||||||
"unique_id": "vf1aaaaa555777999_battery_temperature",
|
"unique_id": "vf1aaaaa555777999_battery_temperature",
|
||||||
"result": STATE_UNKNOWN,
|
"result": STATE_UNKNOWN,
|
||||||
"unit": TEMP_CELSIUS,
|
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
|
||||||
"class": DEVICE_CLASS_TEMPERATURE,
|
ATTR_LAST_UPDATE: "2020-11-17T09:06:48+01:00",
|
||||||
|
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||||
|
ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"entity_id": "sensor.charge_mode",
|
"entity_id": "sensor.charge_mode",
|
||||||
"unique_id": "vf1aaaaa555777999_charge_mode",
|
"unique_id": "vf1aaaaa555777999_charge_mode",
|
||||||
"result": "schedule_mode",
|
"result": "schedule_mode",
|
||||||
"class": DEVICE_CLASS_CHARGE_MODE,
|
ATTR_DEVICE_CLASS: DEVICE_CLASS_CHARGE_MODE,
|
||||||
|
ATTR_ICON: "mdi:calendar-clock",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"entity_id": "sensor.charge_state",
|
"entity_id": "sensor.charge_state",
|
||||||
"unique_id": "vf1aaaaa555777999_charge_state",
|
"unique_id": "vf1aaaaa555777999_charge_state",
|
||||||
"result": "charge_error",
|
"result": "charge_error",
|
||||||
"class": DEVICE_CLASS_CHARGE_STATE,
|
ATTR_DEVICE_CLASS: DEVICE_CLASS_CHARGE_STATE,
|
||||||
|
ATTR_ICON: "mdi:flash-off",
|
||||||
|
ATTR_LAST_UPDATE: "2020-11-17T09:06:48+01:00",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"entity_id": "sensor.charging_power",
|
"entity_id": "sensor.charging_power",
|
||||||
"unique_id": "vf1aaaaa555777999_charging_power",
|
"unique_id": "vf1aaaaa555777999_charging_power",
|
||||||
"result": STATE_UNKNOWN,
|
"result": STATE_UNKNOWN,
|
||||||
"unit": POWER_KILO_WATT,
|
ATTR_DEVICE_CLASS: DEVICE_CLASS_POWER,
|
||||||
"class": DEVICE_CLASS_POWER,
|
ATTR_LAST_UPDATE: "2020-11-17T09:06:48+01:00",
|
||||||
|
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||||
|
ATTR_UNIT_OF_MEASUREMENT: POWER_KILO_WATT,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"entity_id": "sensor.charging_remaining_time",
|
"entity_id": "sensor.charging_remaining_time",
|
||||||
"unique_id": "vf1aaaaa555777999_charging_remaining_time",
|
"unique_id": "vf1aaaaa555777999_charging_remaining_time",
|
||||||
"result": STATE_UNKNOWN,
|
"result": STATE_UNKNOWN,
|
||||||
"unit": TIME_MINUTES,
|
ATTR_ICON: "mdi:timer",
|
||||||
|
ATTR_LAST_UPDATE: "2020-11-17T09:06:48+01:00",
|
||||||
|
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||||
|
ATTR_UNIT_OF_MEASUREMENT: TIME_MINUTES,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"entity_id": "sensor.mileage",
|
"entity_id": "sensor.mileage",
|
||||||
"unique_id": "vf1aaaaa555777999_mileage",
|
"unique_id": "vf1aaaaa555777999_mileage",
|
||||||
"result": "49114",
|
"result": "49114",
|
||||||
"unit": LENGTH_KILOMETERS,
|
ATTR_ICON: "mdi:sign-direction",
|
||||||
|
ATTR_STATE_CLASS: STATE_CLASS_TOTAL_INCREASING,
|
||||||
|
ATTR_UNIT_OF_MEASUREMENT: LENGTH_KILOMETERS,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"entity_id": "sensor.plug_state",
|
"entity_id": "sensor.plug_state",
|
||||||
"unique_id": "vf1aaaaa555777999_plug_state",
|
"unique_id": "vf1aaaaa555777999_plug_state",
|
||||||
"result": "unplugged",
|
"result": "unplugged",
|
||||||
"class": DEVICE_CLASS_PLUG_STATE,
|
ATTR_DEVICE_CLASS: DEVICE_CLASS_PLUG_STATE,
|
||||||
|
ATTR_ICON: "mdi:power-plug-off",
|
||||||
|
ATTR_LAST_UPDATE: "2020-11-17T09:06:48+01:00",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -273,13 +337,15 @@ MOCK_VEHICLES = {
|
|||||||
"entity_id": "binary_sensor.plugged_in",
|
"entity_id": "binary_sensor.plugged_in",
|
||||||
"unique_id": "vf1aaaaa555777123_plugged_in",
|
"unique_id": "vf1aaaaa555777123_plugged_in",
|
||||||
"result": STATE_ON,
|
"result": STATE_ON,
|
||||||
"class": DEVICE_CLASS_PLUG,
|
ATTR_DEVICE_CLASS: DEVICE_CLASS_PLUG,
|
||||||
|
ATTR_LAST_UPDATE: "2020-01-12T21:40:16Z",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"entity_id": "binary_sensor.charging",
|
"entity_id": "binary_sensor.charging",
|
||||||
"unique_id": "vf1aaaaa555777123_charging",
|
"unique_id": "vf1aaaaa555777123_charging",
|
||||||
"result": STATE_ON,
|
"result": STATE_ON,
|
||||||
"class": DEVICE_CLASS_BATTERY_CHARGING,
|
ATTR_DEVICE_CLASS: DEVICE_CLASS_BATTERY_CHARGING,
|
||||||
|
ATTR_LAST_UPDATE: "2020-01-12T21:40:16Z",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
SENSOR_DOMAIN: [
|
SENSOR_DOMAIN: [
|
||||||
@ -287,77 +353,102 @@ MOCK_VEHICLES = {
|
|||||||
"entity_id": "sensor.battery_autonomy",
|
"entity_id": "sensor.battery_autonomy",
|
||||||
"unique_id": "vf1aaaaa555777123_battery_autonomy",
|
"unique_id": "vf1aaaaa555777123_battery_autonomy",
|
||||||
"result": "141",
|
"result": "141",
|
||||||
"unit": LENGTH_KILOMETERS,
|
ATTR_ICON: "mdi:ev-station",
|
||||||
|
ATTR_LAST_UPDATE: "2020-01-12T21:40:16Z",
|
||||||
|
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||||
|
ATTR_UNIT_OF_MEASUREMENT: LENGTH_KILOMETERS,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"entity_id": "sensor.battery_available_energy",
|
"entity_id": "sensor.battery_available_energy",
|
||||||
"unique_id": "vf1aaaaa555777123_battery_available_energy",
|
"unique_id": "vf1aaaaa555777123_battery_available_energy",
|
||||||
"result": "31",
|
"result": "31",
|
||||||
"unit": ENERGY_KILO_WATT_HOUR,
|
ATTR_DEVICE_CLASS: DEVICE_CLASS_ENERGY,
|
||||||
"class": DEVICE_CLASS_ENERGY,
|
ATTR_LAST_UPDATE: "2020-01-12T21:40:16Z",
|
||||||
|
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||||
|
ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"entity_id": "sensor.battery_level",
|
"entity_id": "sensor.battery_level",
|
||||||
"unique_id": "vf1aaaaa555777123_battery_level",
|
"unique_id": "vf1aaaaa555777123_battery_level",
|
||||||
"result": "60",
|
"result": "60",
|
||||||
"unit": PERCENTAGE,
|
ATTR_DEVICE_CLASS: DEVICE_CLASS_BATTERY,
|
||||||
"class": DEVICE_CLASS_BATTERY,
|
ATTR_LAST_UPDATE: "2020-01-12T21:40:16Z",
|
||||||
|
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||||
|
ATTR_UNIT_OF_MEASUREMENT: PERCENTAGE,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"entity_id": "sensor.battery_temperature",
|
"entity_id": "sensor.battery_temperature",
|
||||||
"unique_id": "vf1aaaaa555777123_battery_temperature",
|
"unique_id": "vf1aaaaa555777123_battery_temperature",
|
||||||
"result": "20",
|
"result": "20",
|
||||||
"unit": TEMP_CELSIUS,
|
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
|
||||||
"class": DEVICE_CLASS_TEMPERATURE,
|
ATTR_LAST_UPDATE: "2020-01-12T21:40:16Z",
|
||||||
|
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||||
|
ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"entity_id": "sensor.charge_mode",
|
"entity_id": "sensor.charge_mode",
|
||||||
"unique_id": "vf1aaaaa555777123_charge_mode",
|
"unique_id": "vf1aaaaa555777123_charge_mode",
|
||||||
"result": "always",
|
"result": "always",
|
||||||
"class": DEVICE_CLASS_CHARGE_MODE,
|
ATTR_DEVICE_CLASS: DEVICE_CLASS_CHARGE_MODE,
|
||||||
|
ATTR_ICON: "mdi:calendar-remove",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"entity_id": "sensor.charge_state",
|
"entity_id": "sensor.charge_state",
|
||||||
"unique_id": "vf1aaaaa555777123_charge_state",
|
"unique_id": "vf1aaaaa555777123_charge_state",
|
||||||
"result": "charge_in_progress",
|
"result": "charge_in_progress",
|
||||||
"class": DEVICE_CLASS_CHARGE_STATE,
|
ATTR_DEVICE_CLASS: DEVICE_CLASS_CHARGE_STATE,
|
||||||
|
ATTR_ICON: "mdi:flash",
|
||||||
|
ATTR_LAST_UPDATE: "2020-01-12T21:40:16Z",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"entity_id": "sensor.charging_power",
|
"entity_id": "sensor.charging_power",
|
||||||
"unique_id": "vf1aaaaa555777123_charging_power",
|
"unique_id": "vf1aaaaa555777123_charging_power",
|
||||||
"result": "27.0",
|
"result": "27.0",
|
||||||
"unit": POWER_KILO_WATT,
|
ATTR_DEVICE_CLASS: DEVICE_CLASS_POWER,
|
||||||
"class": DEVICE_CLASS_POWER,
|
ATTR_LAST_UPDATE: "2020-01-12T21:40:16Z",
|
||||||
|
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||||
|
ATTR_UNIT_OF_MEASUREMENT: POWER_KILO_WATT,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"entity_id": "sensor.charging_remaining_time",
|
"entity_id": "sensor.charging_remaining_time",
|
||||||
"unique_id": "vf1aaaaa555777123_charging_remaining_time",
|
"unique_id": "vf1aaaaa555777123_charging_remaining_time",
|
||||||
"result": "145",
|
"result": "145",
|
||||||
"unit": TIME_MINUTES,
|
ATTR_ICON: "mdi:timer",
|
||||||
|
ATTR_LAST_UPDATE: "2020-01-12T21:40:16Z",
|
||||||
|
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||||
|
ATTR_UNIT_OF_MEASUREMENT: TIME_MINUTES,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"entity_id": "sensor.fuel_autonomy",
|
"entity_id": "sensor.fuel_autonomy",
|
||||||
"unique_id": "vf1aaaaa555777123_fuel_autonomy",
|
"unique_id": "vf1aaaaa555777123_fuel_autonomy",
|
||||||
"result": "35",
|
"result": "35",
|
||||||
"unit": LENGTH_KILOMETERS,
|
ATTR_ICON: "mdi:gas-station",
|
||||||
|
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||||
|
ATTR_UNIT_OF_MEASUREMENT: LENGTH_KILOMETERS,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"entity_id": "sensor.fuel_quantity",
|
"entity_id": "sensor.fuel_quantity",
|
||||||
"unique_id": "vf1aaaaa555777123_fuel_quantity",
|
"unique_id": "vf1aaaaa555777123_fuel_quantity",
|
||||||
"result": "3",
|
"result": "3",
|
||||||
"unit": VOLUME_LITERS,
|
ATTR_ICON: "mdi:fuel",
|
||||||
|
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||||
|
ATTR_UNIT_OF_MEASUREMENT: VOLUME_LITERS,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"entity_id": "sensor.mileage",
|
"entity_id": "sensor.mileage",
|
||||||
"unique_id": "vf1aaaaa555777123_mileage",
|
"unique_id": "vf1aaaaa555777123_mileage",
|
||||||
"result": "5567",
|
"result": "5567",
|
||||||
"unit": LENGTH_KILOMETERS,
|
ATTR_ICON: "mdi:sign-direction",
|
||||||
|
ATTR_STATE_CLASS: STATE_CLASS_TOTAL_INCREASING,
|
||||||
|
ATTR_UNIT_OF_MEASUREMENT: LENGTH_KILOMETERS,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"entity_id": "sensor.plug_state",
|
"entity_id": "sensor.plug_state",
|
||||||
"unique_id": "vf1aaaaa555777123_plug_state",
|
"unique_id": "vf1aaaaa555777123_plug_state",
|
||||||
"result": "plugged",
|
"result": "plugged",
|
||||||
"class": DEVICE_CLASS_PLUG_STATE,
|
ATTR_DEVICE_CLASS: DEVICE_CLASS_PLUG_STATE,
|
||||||
|
ATTR_ICON: "mdi:power-plug",
|
||||||
|
ATTR_LAST_UPDATE: "2020-01-12T21:40:16Z",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -382,19 +473,25 @@ MOCK_VEHICLES = {
|
|||||||
"entity_id": "sensor.fuel_autonomy",
|
"entity_id": "sensor.fuel_autonomy",
|
||||||
"unique_id": "vf1aaaaa555777123_fuel_autonomy",
|
"unique_id": "vf1aaaaa555777123_fuel_autonomy",
|
||||||
"result": "35",
|
"result": "35",
|
||||||
"unit": LENGTH_KILOMETERS,
|
ATTR_ICON: "mdi:gas-station",
|
||||||
|
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||||
|
ATTR_UNIT_OF_MEASUREMENT: LENGTH_KILOMETERS,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"entity_id": "sensor.fuel_quantity",
|
"entity_id": "sensor.fuel_quantity",
|
||||||
"unique_id": "vf1aaaaa555777123_fuel_quantity",
|
"unique_id": "vf1aaaaa555777123_fuel_quantity",
|
||||||
"result": "3",
|
"result": "3",
|
||||||
"unit": VOLUME_LITERS,
|
ATTR_ICON: "mdi:fuel",
|
||||||
|
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||||
|
ATTR_UNIT_OF_MEASUREMENT: VOLUME_LITERS,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"entity_id": "sensor.mileage",
|
"entity_id": "sensor.mileage",
|
||||||
"unique_id": "vf1aaaaa555777123_mileage",
|
"unique_id": "vf1aaaaa555777123_mileage",
|
||||||
"result": "5567",
|
"result": "5567",
|
||||||
"unit": LENGTH_KILOMETERS,
|
ATTR_ICON: "mdi:sign-direction",
|
||||||
|
ATTR_STATE_CLASS: STATE_CLASS_TOTAL_INCREASING,
|
||||||
|
ATTR_UNIT_OF_MEASUREMENT: LENGTH_KILOMETERS,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -5,6 +5,7 @@ import pytest
|
|||||||
from renault_api.kamereon import exceptions
|
from renault_api.kamereon import exceptions
|
||||||
|
|
||||||
from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN
|
from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN
|
||||||
|
from homeassistant.components.renault.renault_entities import ATTR_LAST_UPDATE
|
||||||
from homeassistant.const import STATE_OFF, STATE_UNAVAILABLE
|
from homeassistant.const import STATE_OFF, STATE_UNAVAILABLE
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
@ -14,7 +15,7 @@ from . import (
|
|||||||
setup_renault_integration_vehicle_with_no_data,
|
setup_renault_integration_vehicle_with_no_data,
|
||||||
setup_renault_integration_vehicle_with_side_effect,
|
setup_renault_integration_vehicle_with_side_effect,
|
||||||
)
|
)
|
||||||
from .const import MOCK_VEHICLES
|
from .const import CHECK_ATTRIBUTES, MOCK_VEHICLES
|
||||||
|
|
||||||
from tests.common import mock_device_registry, mock_registry
|
from tests.common import mock_device_registry, mock_registry
|
||||||
|
|
||||||
@ -40,10 +41,10 @@ async def test_binary_sensors(hass, vehicle_type):
|
|||||||
registry_entry = entity_registry.entities.get(entity_id)
|
registry_entry = entity_registry.entities.get(entity_id)
|
||||||
assert registry_entry is not None
|
assert registry_entry is not None
|
||||||
assert registry_entry.unique_id == expected_entity["unique_id"]
|
assert registry_entry.unique_id == expected_entity["unique_id"]
|
||||||
assert registry_entry.unit_of_measurement == expected_entity.get("unit")
|
|
||||||
assert registry_entry.device_class == expected_entity.get("class")
|
|
||||||
state = hass.states.get(entity_id)
|
state = hass.states.get(entity_id)
|
||||||
assert state.state == expected_entity["result"]
|
assert state.state == expected_entity["result"]
|
||||||
|
for attr in CHECK_ATTRIBUTES:
|
||||||
|
assert state.attributes.get(attr) == expected_entity.get(attr)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("vehicle_type", MOCK_VEHICLES.keys())
|
@pytest.mark.parametrize("vehicle_type", MOCK_VEHICLES.keys())
|
||||||
@ -67,10 +68,13 @@ async def test_binary_sensor_empty(hass, vehicle_type):
|
|||||||
registry_entry = entity_registry.entities.get(entity_id)
|
registry_entry = entity_registry.entities.get(entity_id)
|
||||||
assert registry_entry is not None
|
assert registry_entry is not None
|
||||||
assert registry_entry.unique_id == expected_entity["unique_id"]
|
assert registry_entry.unique_id == expected_entity["unique_id"]
|
||||||
assert registry_entry.unit_of_measurement == expected_entity.get("unit")
|
|
||||||
assert registry_entry.device_class == expected_entity.get("class")
|
|
||||||
state = hass.states.get(entity_id)
|
state = hass.states.get(entity_id)
|
||||||
assert state.state == STATE_OFF
|
assert state.state == STATE_OFF
|
||||||
|
for attr in CHECK_ATTRIBUTES:
|
||||||
|
if attr == ATTR_LAST_UPDATE:
|
||||||
|
assert state.attributes.get(attr) is None
|
||||||
|
else:
|
||||||
|
assert state.attributes.get(attr) == expected_entity.get(attr)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("vehicle_type", MOCK_VEHICLES.keys())
|
@pytest.mark.parametrize("vehicle_type", MOCK_VEHICLES.keys())
|
||||||
@ -101,10 +105,13 @@ async def test_binary_sensor_errors(hass, vehicle_type):
|
|||||||
registry_entry = entity_registry.entities.get(entity_id)
|
registry_entry = entity_registry.entities.get(entity_id)
|
||||||
assert registry_entry is not None
|
assert registry_entry is not None
|
||||||
assert registry_entry.unique_id == expected_entity["unique_id"]
|
assert registry_entry.unique_id == expected_entity["unique_id"]
|
||||||
assert registry_entry.unit_of_measurement == expected_entity.get("unit")
|
|
||||||
assert registry_entry.device_class == expected_entity.get("class")
|
|
||||||
state = hass.states.get(entity_id)
|
state = hass.states.get(entity_id)
|
||||||
assert state.state == STATE_UNAVAILABLE
|
assert state.state == STATE_UNAVAILABLE
|
||||||
|
for attr in CHECK_ATTRIBUTES:
|
||||||
|
if attr == ATTR_LAST_UPDATE:
|
||||||
|
assert state.attributes.get(attr) is None
|
||||||
|
else:
|
||||||
|
assert state.attributes.get(attr) == expected_entity.get(attr)
|
||||||
|
|
||||||
|
|
||||||
async def test_binary_sensor_access_denied(hass):
|
async def test_binary_sensor_access_denied(hass):
|
||||||
|
@ -4,8 +4,20 @@ from unittest.mock import patch
|
|||||||
import pytest
|
import pytest
|
||||||
from renault_api.kamereon import exceptions
|
from renault_api.kamereon import exceptions
|
||||||
|
|
||||||
|
from homeassistant.components.renault.const import (
|
||||||
|
DEVICE_CLASS_CHARGE_MODE,
|
||||||
|
DEVICE_CLASS_CHARGE_STATE,
|
||||||
|
DEVICE_CLASS_PLUG_STATE,
|
||||||
|
)
|
||||||
|
from homeassistant.components.renault.renault_entities import ATTR_LAST_UPDATE
|
||||||
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
||||||
from homeassistant.const import STATE_UNAVAILABLE, STATE_UNKNOWN
|
from homeassistant.const import (
|
||||||
|
ATTR_DEVICE_CLASS,
|
||||||
|
ATTR_ICON,
|
||||||
|
STATE_UNAVAILABLE,
|
||||||
|
STATE_UNKNOWN,
|
||||||
|
)
|
||||||
|
from homeassistant.core import State
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
from . import (
|
from . import (
|
||||||
@ -14,11 +26,28 @@ from . import (
|
|||||||
setup_renault_integration_vehicle_with_no_data,
|
setup_renault_integration_vehicle_with_no_data,
|
||||||
setup_renault_integration_vehicle_with_side_effect,
|
setup_renault_integration_vehicle_with_side_effect,
|
||||||
)
|
)
|
||||||
from .const import MOCK_VEHICLES
|
from .const import CHECK_ATTRIBUTES, MOCK_VEHICLES
|
||||||
|
|
||||||
from tests.common import mock_device_registry, mock_registry
|
from tests.common import mock_device_registry, mock_registry
|
||||||
|
|
||||||
|
|
||||||
|
def check_inactive_attribute(state: State, attr: str, expected_entity: dict):
|
||||||
|
"""Check attribute for icon for inactive sensors."""
|
||||||
|
if attr == ATTR_LAST_UPDATE:
|
||||||
|
assert state.attributes.get(attr) is None
|
||||||
|
elif attr == ATTR_ICON:
|
||||||
|
if expected_entity.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_CHARGE_MODE:
|
||||||
|
assert state.attributes.get(ATTR_ICON) == "mdi:calendar-remove"
|
||||||
|
elif expected_entity.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_CHARGE_STATE:
|
||||||
|
assert state.attributes.get(ATTR_ICON) == "mdi:flash-off"
|
||||||
|
elif expected_entity.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_PLUG_STATE:
|
||||||
|
assert state.attributes.get(ATTR_ICON) == "mdi:power-plug-off"
|
||||||
|
else:
|
||||||
|
assert state.attributes.get(ATTR_ICON) == expected_entity.get(ATTR_ICON)
|
||||||
|
else:
|
||||||
|
assert state.attributes.get(attr) == expected_entity.get(attr)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("vehicle_type", MOCK_VEHICLES.keys())
|
@pytest.mark.parametrize("vehicle_type", MOCK_VEHICLES.keys())
|
||||||
async def test_sensors(hass, vehicle_type):
|
async def test_sensors(hass, vehicle_type):
|
||||||
"""Test for Renault sensors."""
|
"""Test for Renault sensors."""
|
||||||
@ -40,10 +69,10 @@ async def test_sensors(hass, vehicle_type):
|
|||||||
registry_entry = entity_registry.entities.get(entity_id)
|
registry_entry = entity_registry.entities.get(entity_id)
|
||||||
assert registry_entry is not None
|
assert registry_entry is not None
|
||||||
assert registry_entry.unique_id == expected_entity["unique_id"]
|
assert registry_entry.unique_id == expected_entity["unique_id"]
|
||||||
assert registry_entry.unit_of_measurement == expected_entity.get("unit")
|
|
||||||
assert registry_entry.device_class == expected_entity.get("class")
|
|
||||||
state = hass.states.get(entity_id)
|
state = hass.states.get(entity_id)
|
||||||
assert state.state == expected_entity["result"]
|
assert state.state == expected_entity["result"]
|
||||||
|
for attr in CHECK_ATTRIBUTES:
|
||||||
|
assert state.attributes.get(attr) == expected_entity.get(attr)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("vehicle_type", MOCK_VEHICLES.keys())
|
@pytest.mark.parametrize("vehicle_type", MOCK_VEHICLES.keys())
|
||||||
@ -67,10 +96,10 @@ async def test_sensor_empty(hass, vehicle_type):
|
|||||||
registry_entry = entity_registry.entities.get(entity_id)
|
registry_entry = entity_registry.entities.get(entity_id)
|
||||||
assert registry_entry is not None
|
assert registry_entry is not None
|
||||||
assert registry_entry.unique_id == expected_entity["unique_id"]
|
assert registry_entry.unique_id == expected_entity["unique_id"]
|
||||||
assert registry_entry.unit_of_measurement == expected_entity.get("unit")
|
|
||||||
assert registry_entry.device_class == expected_entity.get("class")
|
|
||||||
state = hass.states.get(entity_id)
|
state = hass.states.get(entity_id)
|
||||||
assert state.state == STATE_UNKNOWN
|
assert state.state == STATE_UNKNOWN
|
||||||
|
for attr in CHECK_ATTRIBUTES:
|
||||||
|
check_inactive_attribute(state, attr, expected_entity)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("vehicle_type", MOCK_VEHICLES.keys())
|
@pytest.mark.parametrize("vehicle_type", MOCK_VEHICLES.keys())
|
||||||
@ -101,10 +130,10 @@ async def test_sensor_errors(hass, vehicle_type):
|
|||||||
registry_entry = entity_registry.entities.get(entity_id)
|
registry_entry = entity_registry.entities.get(entity_id)
|
||||||
assert registry_entry is not None
|
assert registry_entry is not None
|
||||||
assert registry_entry.unique_id == expected_entity["unique_id"]
|
assert registry_entry.unique_id == expected_entity["unique_id"]
|
||||||
assert registry_entry.unit_of_measurement == expected_entity.get("unit")
|
|
||||||
assert registry_entry.device_class == expected_entity.get("class")
|
|
||||||
state = hass.states.get(entity_id)
|
state = hass.states.get(entity_id)
|
||||||
assert state.state == STATE_UNAVAILABLE
|
assert state.state == STATE_UNAVAILABLE
|
||||||
|
for attr in CHECK_ATTRIBUTES:
|
||||||
|
check_inactive_attribute(state, attr, expected_entity)
|
||||||
|
|
||||||
|
|
||||||
async def test_sensor_access_denied(hass):
|
async def test_sensor_access_denied(hass):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user