2021-07-28 19:41:11 +00:00
|
|
|
"""Proxy to handle account communication with Renault servers."""
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
import asyncio
|
|
|
|
from datetime import timedelta
|
|
|
|
import logging
|
|
|
|
from typing import cast
|
|
|
|
|
|
|
|
from renault_api.kamereon import models
|
|
|
|
from renault_api.renault_vehicle import RenaultVehicle
|
|
|
|
|
2021-07-30 12:06:55 +00:00
|
|
|
from homeassistant.const import (
|
|
|
|
ATTR_IDENTIFIERS,
|
|
|
|
ATTR_MANUFACTURER,
|
|
|
|
ATTR_MODEL,
|
|
|
|
ATTR_NAME,
|
|
|
|
ATTR_SW_VERSION,
|
|
|
|
)
|
2021-07-28 19:41:11 +00:00
|
|
|
from homeassistant.core import HomeAssistant
|
|
|
|
from homeassistant.helpers.entity import DeviceInfo
|
|
|
|
|
|
|
|
from .const import DOMAIN
|
|
|
|
from .renault_coordinator import RenaultDataUpdateCoordinator
|
|
|
|
|
|
|
|
LOGGER = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
class RenaultVehicleProxy:
|
|
|
|
"""Handle vehicle communication with Renault servers."""
|
|
|
|
|
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
hass: HomeAssistant,
|
|
|
|
vehicle: RenaultVehicle,
|
|
|
|
details: models.KamereonVehicleDetails,
|
|
|
|
scan_interval: timedelta,
|
|
|
|
) -> None:
|
|
|
|
"""Initialise vehicle proxy."""
|
|
|
|
self.hass = hass
|
|
|
|
self._vehicle = vehicle
|
|
|
|
self._details = details
|
|
|
|
self._device_info: DeviceInfo = {
|
2021-07-30 12:06:55 +00:00
|
|
|
ATTR_IDENTIFIERS: {(DOMAIN, cast(str, details.vin))},
|
|
|
|
ATTR_MANUFACTURER: (details.get_brand_label() or "").capitalize(),
|
|
|
|
ATTR_MODEL: (details.get_model_label() or "").capitalize(),
|
|
|
|
ATTR_NAME: details.registrationNumber or "",
|
|
|
|
ATTR_SW_VERSION: details.get_model_code() or "",
|
2021-07-28 19:41:11 +00:00
|
|
|
}
|
|
|
|
self.coordinators: dict[str, RenaultDataUpdateCoordinator] = {}
|
|
|
|
self.hvac_target_temperature = 21
|
|
|
|
self._scan_interval = scan_interval
|
|
|
|
|
|
|
|
@property
|
|
|
|
def details(self) -> models.KamereonVehicleDetails:
|
|
|
|
"""Return the specs of the vehicle."""
|
|
|
|
return self._details
|
|
|
|
|
|
|
|
@property
|
|
|
|
def device_info(self) -> DeviceInfo:
|
|
|
|
"""Return a device description for device registry."""
|
|
|
|
return self._device_info
|
|
|
|
|
|
|
|
async def async_initialise(self) -> None:
|
|
|
|
"""Load available sensors."""
|
|
|
|
if await self.endpoint_available("cockpit"):
|
|
|
|
self.coordinators["cockpit"] = RenaultDataUpdateCoordinator(
|
|
|
|
self.hass,
|
|
|
|
LOGGER,
|
|
|
|
# Name of the data. For logging purposes.
|
|
|
|
name=f"{self.details.vin} cockpit",
|
|
|
|
update_method=self.get_cockpit,
|
|
|
|
# Polling interval. Will only be polled if there are subscribers.
|
|
|
|
update_interval=self._scan_interval,
|
|
|
|
)
|
|
|
|
if await self.endpoint_available("hvac-status"):
|
|
|
|
self.coordinators["hvac_status"] = RenaultDataUpdateCoordinator(
|
|
|
|
self.hass,
|
|
|
|
LOGGER,
|
|
|
|
# Name of the data. For logging purposes.
|
|
|
|
name=f"{self.details.vin} hvac_status",
|
|
|
|
update_method=self.get_hvac_status,
|
|
|
|
# Polling interval. Will only be polled if there are subscribers.
|
|
|
|
update_interval=self._scan_interval,
|
|
|
|
)
|
|
|
|
if self.details.uses_electricity():
|
|
|
|
if await self.endpoint_available("battery-status"):
|
|
|
|
self.coordinators["battery"] = RenaultDataUpdateCoordinator(
|
|
|
|
self.hass,
|
|
|
|
LOGGER,
|
|
|
|
# Name of the data. For logging purposes.
|
|
|
|
name=f"{self.details.vin} battery",
|
|
|
|
update_method=self.get_battery_status,
|
|
|
|
# Polling interval. Will only be polled if there are subscribers.
|
|
|
|
update_interval=self._scan_interval,
|
|
|
|
)
|
|
|
|
if await self.endpoint_available("charge-mode"):
|
|
|
|
self.coordinators["charge_mode"] = RenaultDataUpdateCoordinator(
|
|
|
|
self.hass,
|
|
|
|
LOGGER,
|
|
|
|
# Name of the data. For logging purposes.
|
|
|
|
name=f"{self.details.vin} charge_mode",
|
|
|
|
update_method=self.get_charge_mode,
|
|
|
|
# Polling interval. Will only be polled if there are subscribers.
|
|
|
|
update_interval=self._scan_interval,
|
|
|
|
)
|
|
|
|
# Check all coordinators
|
|
|
|
await asyncio.gather(
|
|
|
|
*(
|
|
|
|
coordinator.async_config_entry_first_refresh()
|
|
|
|
for coordinator in self.coordinators.values()
|
|
|
|
)
|
|
|
|
)
|
|
|
|
for key in list(self.coordinators):
|
|
|
|
# list() to avoid Runtime iteration error
|
|
|
|
coordinator = self.coordinators[key]
|
|
|
|
if coordinator.not_supported:
|
|
|
|
# Remove endpoint as it is not supported for this vehicle.
|
|
|
|
LOGGER.error(
|
|
|
|
"Ignoring endpoint %s as it is not supported for this vehicle: %s",
|
|
|
|
coordinator.name,
|
|
|
|
coordinator.last_exception,
|
|
|
|
)
|
|
|
|
del self.coordinators[key]
|
|
|
|
elif coordinator.access_denied:
|
|
|
|
# Remove endpoint as it is denied for this vehicle.
|
|
|
|
LOGGER.error(
|
|
|
|
"Ignoring endpoint %s as it is denied for this vehicle: %s",
|
|
|
|
coordinator.name,
|
|
|
|
coordinator.last_exception,
|
|
|
|
)
|
|
|
|
del self.coordinators[key]
|
|
|
|
|
|
|
|
async def endpoint_available(self, endpoint: str) -> bool:
|
|
|
|
"""Ensure the endpoint is available to avoid unnecessary queries."""
|
|
|
|
return await self._vehicle.supports_endpoint(
|
|
|
|
endpoint
|
|
|
|
) and await self._vehicle.has_contract_for_endpoint(endpoint)
|
|
|
|
|
|
|
|
async def get_battery_status(self) -> models.KamereonVehicleBatteryStatusData:
|
|
|
|
"""Get battery status information from vehicle."""
|
|
|
|
return await self._vehicle.get_battery_status()
|
|
|
|
|
|
|
|
async def get_charge_mode(self) -> models.KamereonVehicleChargeModeData:
|
|
|
|
"""Get charge mode information from vehicle."""
|
|
|
|
return await self._vehicle.get_charge_mode()
|
|
|
|
|
|
|
|
async def get_cockpit(self) -> models.KamereonVehicleCockpitData:
|
|
|
|
"""Get cockpit information from vehicle."""
|
|
|
|
return await self._vehicle.get_cockpit()
|
|
|
|
|
|
|
|
async def get_hvac_status(self) -> models.KamereonVehicleHvacStatusData:
|
|
|
|
"""Get hvac status information from vehicle."""
|
|
|
|
return await self._vehicle.get_hvac_status()
|