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/lock.py
"""Lock for Uconnect integration."""

from __future__ import annotations
from typing import Callable, Final

from homeassistant.core import HomeAssistant
from homeassistant.components.lock import LockEntity, LockEntityDescription
from homeassistant.config_entries import ConfigEntry
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from py_uconnect.client import Vehicle
from py_uconnect.command import *

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


@dataclass
class UconnectLockEntityDescription(LockEntityDescription):
    """A class that describes custom lock entities."""

    icon_locked: str = None
    icon_unlocked: str = None
    command_on: Command = None
    command_off: Command = None
    is_locked: Callable[[Vehicle], bool] | None = None


LOCK_DESCRIPTIONS: Final[tuple[UconnectLockEntityDescription, ...]] = (
    UconnectLockEntityDescription(
        key="lock_doors",
        name="Doors Lock",
        icon_locked="mdi:car-door-lock",
        icon_unlocked="mdi:car-door-lock-open",
        command_on=COMMAND_DOORS_LOCK,
        command_off=COMMAND_DOORS_UNLOCK,
        is_locked=lambda x: getattr(x, "door_driver_locked", None),
    ),
)


async def async_setup_entry(
    hass: HomeAssistant,
    config_entry: ConfigEntry,
    async_add_entities: AddEntitiesCallback,
) -> None:
    # Do not add entities if not configured
    if not config_entry.options.get(CONF_ADD_COMMAND_ENTITIES):
        return

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

    entities = []
    for vehicle in coordinator.client.vehicles.values():
        for description in LOCK_DESCRIPTIONS:
            if (
                description.command_on.name in vehicle.supported_commands
                or description.command_off.name in vehicle.supported_commands
            ):
                entities.append(UconnectLock(coordinator, description, vehicle))

    async_add_entities(entities)
    return True


class UconnectLock(LockEntity, UconnectEntity):
    def __init__(
        self,
        coordinator: UconnectDataUpdateCoordinator,
        description: UconnectLockEntityDescription,
        vehicle: Vehicle,
    ):
        UconnectEntity.__init__(self, coordinator, vehicle)

        self.entity_description: UconnectLockEntityDescription = 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 icon(self):
        return (
            self.entity_description.icon_locked
            if self.is_locked
            else self.entity_description.icon_unlocked
        )

    @property
    def is_locked(self):
        return self.entity_description.is_locked(self.vehicle)

    async def async_lock(self, **kwargs):
        await self.coordinator.async_command(
            self.vehicle.vin, self.entity_description.command_on
        )

    async def async_unlock(self, **kwargs):
        await self.coordinator.async_command(
            self.vehicle.vin, self.entity_description.command_off
        )