HEX
Server: Apache/2.4.63 (Unix)
System: Linux Synopilou92 4.4.302+ #72806 SMP Mon Jul 21 23:16:00 CST 2025 x86_64
User: pilou92 (1026)
PHP: 8.0.30
Disabled: NONE
Upload Files
File: /volume1/docker/homeassistant2025/custom_components/uconnect/binary_sensor.py
"""Sensor for Uconnect integration."""

from __future__ import annotations

from collections.abc import Callable
from dataclasses import dataclass
from typing import Final

from homeassistant.components.binary_sensor import (
    BinarySensorDeviceClass,
    BinarySensorEntity,
    BinarySensorEntityDescription,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from py_uconnect.client import Vehicle

from .const import DOMAIN
from .coordinator import UconnectDataUpdateCoordinator
from .entity import UconnectEntity


@dataclass
class UconnectBinarySensorEntityDescription(BinarySensorEntityDescription):
    """A class that describes custom binary sensor entities."""

    is_on: Callable[[Vehicle], bool] | None = None
    postprocess: Callable[[bool], bool] | None = None
    on_icon: str | None = None
    off_icon: str | None = None


SENSOR_DESCRIPTIONS: Final[tuple[UconnectBinarySensorEntityDescription, ...]] = (
    UconnectBinarySensorEntityDescription(
        key="ignition_on",
        name="Ignition",
        on_icon="mdi:engine",
        off_icon="mdi:engine-off",
        device_class=BinarySensorDeviceClass.POWER,
    ),
    UconnectBinarySensorEntityDescription(
        key="ev_running",
        name="EV Running",
        on_icon="mdi:engine",
        off_icon="mdi:engine-off",
        device_class=BinarySensorDeviceClass.POWER,
    ),
    UconnectBinarySensorEntityDescription(
        key="door_driver_locked",
        name="Door Driver",
        postprocess=lambda x: not x,
        on_icon="mdi:car-door-lock",
        off_icon="mdi:car-door-lock-open",
        device_class=BinarySensorDeviceClass.LOCK,
    ),
    UconnectBinarySensorEntityDescription(
        key="door_passenger_locked",
        name="Door Passenger",
        postprocess=lambda x: not x,
        on_icon="mdi:car-door-lock",
        off_icon="mdi:car-door-lock-open",
        device_class=BinarySensorDeviceClass.LOCK,
    ),
    UconnectBinarySensorEntityDescription(
        key="door_rear_left_locked",
        name="Door Rear Left",
        postprocess=lambda x: not x,
        on_icon="mdi:car-door-lock",
        off_icon="mdi:car-door-lock-open",
        device_class=BinarySensorDeviceClass.LOCK,
    ),
    UconnectBinarySensorEntityDescription(
        key="door_rear_right_locked",
        name="Door Rear Right",
        postprocess=lambda x: not x,
        on_icon="mdi:car-door-lock",
        off_icon="mdi:car-door-lock-open",
        device_class=BinarySensorDeviceClass.LOCK,
    ),
    UconnectBinarySensorEntityDescription(
        key="trunk_locked",
        name="Trunk",
        postprocess=lambda x: not x,
        on_icon="mdi:car-back",
        off_icon="mdi:car-back",
        device_class=BinarySensorDeviceClass.LOCK,
    ),
    UconnectBinarySensorEntityDescription(
        key="window_driver_closed",
        name="Window Driver",
        postprocess=lambda x: not x,
        on_icon="mdi:car-door",
        off_icon="mdi:car-door",
        device_class=BinarySensorDeviceClass.WINDOW,
    ),
    UconnectBinarySensorEntityDescription(
        key="window_passenger_closed",
        name="Window Passenger",
        postprocess=lambda x: not x,
        on_icon="mdi:car-door",
        off_icon="mdi:car-door",
        device_class=BinarySensorDeviceClass.WINDOW,
    ),
    UconnectBinarySensorEntityDescription(
        key="plugged_in",
        name="EV Charger",
        on_icon="mdi:power-plug",
        off_icon="mdi:power-plug-off",
        device_class=BinarySensorDeviceClass.PLUG,
    ),
    UconnectBinarySensorEntityDescription(
        key="charging",
        name="Charging",
        on_icon="mdi:battery-charging",
        off_icon="mdi:battery-alert",
        device_class=BinarySensorDeviceClass.BATTERY_CHARGING,
    ),
    UconnectBinarySensorEntityDescription(
        key="wheel_front_left_pressure_warning",
        name="Tire Pressure Front Left Warning",
        on_icon="mdi:car-tire-alert",
        off_icon="mdi:car-tire-alert",
        device_class=BinarySensorDeviceClass.PROBLEM,
    ),
    UconnectBinarySensorEntityDescription(
        key="wheel_front_right_pressure_warning",
        name="Tire Pressure Front Right Warning",
        on_icon="mdi:car-tire-alert",
        off_icon="mdi:car-tire-alert",
        device_class=BinarySensorDeviceClass.PROBLEM,
    ),
    UconnectBinarySensorEntityDescription(
        key="wheel_rear_right_pressure_warning",
        name="Tire Pressure Rear Right Warning",
        on_icon="mdi:car-tire-alert",
        off_icon="mdi:car-tire-alert",
        device_class=BinarySensorDeviceClass.PROBLEM,
    ),
    UconnectBinarySensorEntityDescription(
        key="wheel_rear_left_pressure_warning",
        name="Tire Pressure Rear Left Warning",
        on_icon="mdi:car-tire-alert",
        off_icon="mdi:car-tire-alert",
        device_class=BinarySensorDeviceClass.PROBLEM,
    ),
    UconnectBinarySensorEntityDescription(
        key="fuel_low",
        name="Low Fuel",
        on_icon="mdi:gas-station",
        off_icon="mdi:fuel",
        device_class=BinarySensorDeviceClass.PROBLEM,
    ),
)


async def async_setup_entry(
    hass: HomeAssistant,
    config_entry: ConfigEntry,
    async_add_entities: AddEntitiesCallback,
) -> None:
    """Set up binary_sensor platform."""

    coordinator: UconnectDataUpdateCoordinator = hass.data[DOMAIN][
        config_entry.unique_id
    ]
    entities: list[UconnectBinarySensor] = []

    for vehicle in coordinator.client.vehicles.values():
        for description in SENSOR_DESCRIPTIONS:
            if getattr(vehicle, description.key) is not None:
                entities.append(UconnectBinarySensor(coordinator, description, vehicle))

    async_add_entities(entities)
    return True


class UconnectBinarySensor(BinarySensorEntity, UconnectEntity):
    """Uconnect binary sensor class."""

    def __init__(
        self,
        coordinator: UconnectDataUpdateCoordinator,
        description: UconnectBinarySensorEntityDescription,
        vehicle: Vehicle,
    ) -> None:
        """Initialize the sensor."""
        super().__init__(coordinator, vehicle)
        self.key = description.key
        self.postprocess = description.postprocess
        self.entity_description: UconnectBinarySensorEntityDescription = description
        self._attr_unique_id = f"{DOMAIN}_{vehicle.vin}_{description.key}"
        self._attr_name = f"{vehicle.make} {
            vehicle.nickname or vehicle.model} {description.name}"

    @property
    def is_on(self) -> bool | None:
        """Return true if the binary sensor is on."""

        if self.entity_description.is_on is not None:
            v = self.entity_description.is_on(self.vehicle)
        else:
            v = getattr(self.vehicle, self.key)

        if self.postprocess is not None:
            v = self.postprocess(v)

        return v

    @property
    def icon(self):
        """Return the icon to use in the frontend, if any."""

        if (
            self.entity_description.on_icon == self.entity_description.off_icon
        ) is None:
            return BinarySensorEntity.icon

        return (
            self.entity_description.on_icon
            if self.is_on
            else self.entity_description.off_icon
        )