File: lock.py

package info (click to toggle)
simplisafe-python 2024.1.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,268 kB
  • sloc: python: 5,252; sh: 50; makefile: 19
file content (167 lines) | stat: -rw-r--r-- 4,899 bytes parent folder | download
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