Skip to content

Commit

Permalink
feat: add support for the lock platform
Browse files Browse the repository at this point in the history
  • Loading branch information
vanstinator committed Feb 26, 2022
1 parent bf95adc commit 9e3c484
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 24 deletions.
38 changes: 14 additions & 24 deletions custom_components/toyota_na/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,17 @@
from homeassistant.helpers import device_registry as dr, service
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed

from .const import DOMAIN
from .const import (
COMMAND_MAP,
DOMAIN,
DOOR_LOCK,
DOOR_UNLOCK,
ENGINE_START,
ENGINE_STOP,
)

_LOGGER = logging.getLogger(__name__)
PLATFORMS = ["binary_sensor", "device_tracker", "sensor"]

SERVICE_DOOR_LOCK = "door_lock"
SERVICE_DOOR_UNLOCK = "door_unlock"
SERVICE_ENGINE_START = "engine_start"
SERVICE_ENGINE_STOP = "engine_stop"

commands = {
SERVICE_DOOR_LOCK: RemoteRequestCommand.DoorLock,
SERVICE_DOOR_UNLOCK: RemoteRequestCommand.DoorUnlock,
SERVICE_ENGINE_START: RemoteRequestCommand.EngineStart,
SERVICE_ENGINE_STOP: RemoteRequestCommand.EngineStop,
}

SERVICE_DOOR_LOCK = "door_lock"
SERVICE_DOOR_UNLOCK = "door_unlock"
SERVICE_ENGINE_START = "engine_start"
SERVICE_ENGINE_STOP = "engine_stop"
PLATFORMS = ["binary_sensor", "device_tracker", "lock", "sensor"]


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
Expand Down Expand Up @@ -90,17 +80,17 @@ async def async_service_handle(service_call: ServiceCall) -> None:
vin = identifier[1]
for vehicle in coordinator.data:
if vehicle.vin == vin:
await vehicle.send_command(commands[remote_action])
await vehicle.send_command(COMMAND_MAP[remote_action])
break

_LOGGER.info("Handling service call %s for %s ", remote_action, vin)

return

hass.services.async_register(DOMAIN, SERVICE_DOOR_LOCK, async_service_handle)
hass.services.async_register(DOMAIN, SERVICE_DOOR_UNLOCK, async_service_handle)
hass.services.async_register(DOMAIN, SERVICE_ENGINE_START, async_service_handle)
hass.services.async_register(DOMAIN, SERVICE_ENGINE_STOP, async_service_handle)
hass.services.async_register(DOMAIN, DOOR_LOCK, async_service_handle)
hass.services.async_register(DOMAIN, DOOR_UNLOCK, async_service_handle)
hass.services.async_register(DOMAIN, ENGINE_START, async_service_handle)
hass.services.async_register(DOMAIN, ENGINE_STOP, async_service_handle)

return True

Expand Down
15 changes: 15 additions & 0 deletions custom_components/toyota_na/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,23 @@
from homeassistant.components.sensor import SensorStateClass
from homeassistant.const import LENGTH_MILES, PERCENTAGE, PRESSURE_PSI

from toyota_na.vehicle.base_vehicle import RemoteRequestCommand


DOMAIN = "toyota_na"

DOOR_LOCK = "door_lock"
DOOR_UNLOCK = "door_unlock"
ENGINE_START = "engine_start"
ENGINE_STOP = "engine_stop"

COMMAND_MAP = {
DOOR_LOCK: RemoteRequestCommand.DoorLock,
DOOR_UNLOCK: RemoteRequestCommand.DoorUnlock,
ENGINE_START: RemoteRequestCommand.EngineStart,
ENGINE_STOP: RemoteRequestCommand.EngineStop,
}

BINARY_SENSORS = [
{
"device_class": BinarySensorDeviceClass.DOOR,
Expand Down
114 changes: 114 additions & 0 deletions custom_components/toyota_na/lock.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import asyncio
from typing import Any

from toyota_na.vehicle.base_vehicle import ToyotaVehicle, VehicleFeatures
from toyota_na.vehicle.entity_types.ToyotaLockableOpening import ToyotaLockableOpening
from toyota_na.vehicle.entity_types.ToyotaOpening import ToyotaOpening
from toyota_na.vehicle.entity_types.ToyotaRemoteStart import ToyotaRemoteStart


from homeassistant.components.lock import (
LockEntity,
)

from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator

from .base_entity import ToyotaNABaseEntity
from .const import COMMAND_MAP, DOMAIN, DOOR_LOCK, DOOR_UNLOCK


async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_devices: AddEntitiesCallback,
):
"""Set up the binary_sensor platform."""
locks = []

coordinator: DataUpdateCoordinator[list[ToyotaVehicle]] = hass.data[DOMAIN][
config_entry.entry_id
]["coordinator"]

for vehicle in coordinator.data:
locks.append(
ToyotaLock(
coordinator,
"",
vehicle.vin,
)
)

async_add_devices(locks, True)


class ToyotaLock(ToyotaNABaseEntity, LockEntity):

_state_changing = False

def __init__(
self,
vin,
*args: Any,
):
super().__init__(vin, *args)

@property
def icon(self):
return "mdi:car-key"

@property
def is_locked(self):
_is_locked = True

if self.vehicle is not None:

all_locks = [
feature
for feature in self.vehicle.features.values()
if isinstance(feature, ToyotaLockableOpening)
]

for lock in all_locks:
if lock.locked is False:
_is_locked = False

else:
_is_locked = False

return _is_locked

@property
def is_locking(self):
return self._state_changing is True and self.is_locked is False

@property
def is_unlocking(self):
return self._state_changing is True and self.is_locked is True

async def async_lock(self, **kwargs):
"""Lock all or specified locks. A code to lock the lock with may optionally be specified."""
await self.toggle_lock(DOOR_LOCK)

async def async_unlock(self, **kwargs):
"""Unlock all or specified locks. A code to unlock the lock with may optionally be specified."""
await self.toggle_lock(DOOR_UNLOCK)

async def toggle_lock(self, command: str):
"""Set the lock state via the provided command string."""
if self.vehicle is not None:
self._state_changing = True
await self.vehicle.send_command(COMMAND_MAP[command])

# TODO: This works great and prevents us from unnecessarily hitting Toyota. But we can and should
# probably do stuff like this in the library where we can better control which APIs we hit to refresh our in-memory data.
self.coordinator.async_set_updated_data(self.coordinator.data)
await asyncio.sleep(10)
self._state_changing = False
await self.coordinator.async_request_refresh()

@property
def available(self):
return self.vehicle is not None

0 comments on commit 9e3c484

Please sign in to comment.