1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
|
"""Define a SimpliSafe lock."""
from __future__ import annotations
from collections.abc import Awaitable, Callable
from enum import Enum
from typing import TYPE_CHECKING, Any, cast
from simplipy.const import LOGGER
from simplipy.device import DeviceTypes, DeviceV3
if TYPE_CHECKING:
from simplipy.system import System
class LockStates(Enum):
"""States that a lock can be in."""
UNLOCKED = 0
LOCKED = 1
JAMMED = 2
UNKNOWN = 99
class Lock(DeviceV3):
"""A lock that works with V3 systems.
Note that this class shouldn't be instantiated directly; it will be
instantiated as appropriate via :meth:`simplipy.API.async_get_systems`.
Args:
request: The request method from the :meth:`simplipy.API` object.
system: A :meth:`simplipy.system.System` object (or one of its subclasses).
device_type: The type of device represented.
serial: The serial number of the device.
"""
class _InternalStates(Enum):
"""Define an enum to map internal lock states to values we understand."""
LOCKED = 1
UNLOCKED = 2
def __init__(
self,
request: Callable[..., Awaitable[dict[str, Any]]],
system: System,
device_type: DeviceTypes,
serial: str,
) -> None:
"""Initialize.
Args:
request: The request method from the :meth:`simplipy.API` object.
system: A :meth:`simplipy.system.System` object (or one of its subclasses).
device_type: The type of device represented.
serial: The serial number of the device.
"""
super().__init__(system, device_type, serial)
self._request = request
@property
def disabled(self) -> bool:
"""Return whether the lock is disabled.
Returns:
The lock's disable status.
"""
return cast(
bool, self._system.sensor_data[self._serial]["status"]["lockDisabled"]
)
@property
def lock_low_battery(self) -> bool:
"""Return whether the lock's battery is low.
Returns:
The lock's low battery status.
"""
return cast(
bool, self._system.sensor_data[self._serial]["status"]["lockLowBattery"]
)
@property
def pin_pad_low_battery(self) -> bool:
"""Return whether the pin pad's battery is low.
Returns:
The pinpad's low battery status.
"""
return cast(
bool, self._system.sensor_data[self._serial]["status"]["pinPadLowBattery"]
)
@property
def pin_pad_offline(self) -> bool:
"""Return whether the pin pad is offline.
Returns:
The pinpad's offline status.
"""
return cast(
bool, self._system.sensor_data[self._serial]["status"]["pinPadOffline"]
)
@property
def state(self) -> LockStates:
"""Return the current state of the lock.
Returns:
The lock's state.
"""
if bool(self._system.sensor_data[self._serial]["status"]["lockJamState"]):
return LockStates.JAMMED
raw_state = self._system.sensor_data[self._serial]["status"]["lockState"]
try:
internal_state = self._InternalStates(raw_state)
except ValueError:
LOGGER.error("Unknown raw lock state: %s", raw_state)
return LockStates.UNKNOWN
if internal_state == self._InternalStates.LOCKED:
return LockStates.LOCKED
return LockStates.UNLOCKED
def as_dict(self) -> dict[str, Any]:
"""Return dictionary version of this device.
Returns:
A dict representation of this device.
"""
return {
**super().as_dict(),
"disabled": self.disabled,
"lock_low_battery": self.lock_low_battery,
"pin_pad_low_battery": self.pin_pad_low_battery,
"pin_pad_offline": self.pin_pad_offline,
"state": self.state.value,
}
async def async_lock(self) -> None:
"""Lock the lock."""
await self._request(
"post",
f"doorlock/{self._system.system_id}/{self.serial}/state",
json={"state": "lock"},
)
# Update the internal state representation:
self._system.sensor_data[self._serial]["status"][
"lockState"
] = self._InternalStates.LOCKED.value
async def async_unlock(self) -> None:
"""Unlock the lock."""
await self._request(
"post",
f"doorlock/{self._system.system_id}/{self.serial}/state",
json={"state": "unlock"},
)
# Update the internal state representation:
self._system.sensor_data[self._serial]["status"][
"lockState"
] = self._InternalStates.UNLOCKED.value
|