Skip to content
This repository was archived by the owner on Jun 28, 2024. It is now read-only.

Commit d0e856f

Browse files
authored
feat: Create Access Codes against many devices with same code (#60)
1 parent 30517b0 commit d0e856f

File tree

4 files changed

+105
-4
lines changed

4 files changed

+105
-4
lines changed

.github/CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* @codetheweb @seveibar @andrii-balitskyi

seamapi/access_codes.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ def create(
125125
code: Optional[str] = None,
126126
starts_at: Optional[str] = None,
127127
ends_at: Optional[str] = None,
128+
common_code_key: Optional[str] = None,
128129
) -> AccessCode:
129130
"""Creates an access code on a device.
130131
@@ -161,6 +162,8 @@ def create(
161162
create_payload["starts_at"] = starts_at
162163
if ends_at is not None:
163164
create_payload["ends_at"] = ends_at
165+
if common_code_key is not None:
166+
create_payload["common_code_key"] = common_code_key
164167

165168
res = self.seam.make_request(
166169
"POST",
@@ -175,6 +178,67 @@ def create(
175178

176179
return AccessCode.from_dict(success_res["access_code"])
177180

181+
@report_error
182+
def create_multiple(
183+
self,
184+
devices: Union[List[DeviceId], List[Device]],
185+
name: Optional[str] = None,
186+
code: Optional[str] = None,
187+
starts_at: Optional[str] = None,
188+
ends_at: Optional[str] = None,
189+
) -> List[AccessCode]:
190+
"""Creates multiple access codes across multiple devices. All access
191+
codes will have the same code (if possible).
192+
193+
Parameters
194+
----------
195+
devices : List of DeviceIds or Devices
196+
Device ids or Devices to create an access code for
197+
name : str, optional
198+
Access code name
199+
code : str, optional
200+
Access code value
201+
starts_at : str, optional
202+
Time when access code becomes effective
203+
ends_at : str, optional
204+
Time when access code ceases to be effective
205+
206+
Raises
207+
------
208+
Exception
209+
If the API request wasn't successful.
210+
211+
Returns
212+
------
213+
AccessCode
214+
"""
215+
216+
device_ids: List[str] = []
217+
for device in devices:
218+
device_ids.append(to_device_id(device))
219+
220+
create_payload: dict[str, Any] = {"device_ids": device_ids}
221+
if name is not None:
222+
create_payload["name"] = name
223+
if code is not None:
224+
create_payload["code"] = code
225+
if starts_at is not None:
226+
create_payload["starts_at"] = starts_at
227+
if ends_at is not None:
228+
create_payload["ends_at"] = ends_at
229+
230+
res = self.seam.make_request(
231+
"POST",
232+
"/access_codes/create_multiple",
233+
json=create_payload,
234+
)
235+
236+
access_codes: List[AccessCode] = []
237+
for access_code in res["access_codes"]:
238+
access_codes.append(AccessCode.from_dict(access_code))
239+
240+
return access_codes
241+
178242
@report_error
179243
def update(
180244
self,

seamapi/types.py

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,19 @@
1616
DeviceType = str # e.g. august_lock
1717
WorkspaceId = str
1818

19+
1920
class SeamAPIException(Exception):
20-
def __init__(self, status_code: int, request_id: str, metadata: Optional[Dict[str, any]]):
21+
def __init__(
22+
self, status_code: int, request_id: str, metadata: Optional[Dict[str, any]]
23+
):
2124
self.status_code = status_code
2225
self.request_id = request_id
2326
self.metadata = metadata
2427

25-
super().__init__(f"SeamAPIException: status={status_code}, request_id={request_id}, metadata={metadata}")
28+
super().__init__(
29+
f"SeamAPIException: status={status_code}, request_id={request_id}, metadata={metadata}"
30+
)
31+
2632

2733
class ActionAttemptFailedException(Exception):
2834
def __init__(
@@ -61,6 +67,7 @@ def from_dict(d: Dict[str, Any]):
6167
errors=d["errors"],
6268
)
6369

70+
6471
@dataclass
6572
class Event:
6673
event_id: str
@@ -75,6 +82,7 @@ class Event:
7582
end_time: Union[str, None]
7683
created_at: Union[str, None]
7784

85+
7886
@dataclass_json
7987
@dataclass
8088
class ActionAttemptError:
@@ -140,6 +148,7 @@ class AccessCode:
140148
ends_at: Optional[str] = None
141149
name: Optional[str] = ""
142150
status: Optional[str] = None
151+
common_code_key: Optional[str] = None
143152

144153

145154
class AbstractActionAttempts(abc.ABC):
@@ -188,9 +197,27 @@ def get(
188197
) -> AccessCode:
189198
raise NotImplementedError
190199

200+
@abc.abstractmethod
201+
def create_multiple(
202+
self,
203+
devices: Union[List[DeviceId], List[Device]],
204+
name: Optional[str] = None,
205+
code: Optional[str] = None,
206+
starts_at: Optional[str] = None,
207+
ends_at: Optional[str] = None,
208+
common_code_key: Optional[str] = None,
209+
) -> List[AccessCode]:
210+
raise NotImplementedError
211+
191212
@abc.abstractmethod
192213
def create(
193-
self, device: Union[DeviceId, Device], name: str, code: str
214+
self,
215+
device: Union[DeviceId, Device],
216+
name: Optional[str] = None,
217+
code: Optional[str] = None,
218+
starts_at: Optional[str] = None,
219+
ends_at: Optional[str] = None,
220+
common_code_key: Optional[str] = None,
194221
) -> AccessCode:
195222
raise NotImplementedError
196223

@@ -228,11 +255,13 @@ def get(
228255
) -> Device:
229256
raise NotImplementedError
230257

258+
231259
class AbstractEvents(abc.ABC):
232260
@abc.abstractmethod
233261
def list(self) -> List[Event]:
234262
raise NotImplementedError
235263

264+
236265
class AbstractWorkspaces(abc.ABC):
237266
@abc.abstractmethod
238267
def list(self) -> List[Workspace]:
@@ -274,6 +303,7 @@ def get(
274303
) -> ConnectedAccount:
275304
raise NotImplementedError
276305

306+
277307
@dataclass
278308
class AbstractRoutes(abc.ABC):
279309
workspaces: AbstractWorkspaces
@@ -297,6 +327,7 @@ class AbstractSeam(AbstractRoutes):
297327
def __init__(self, api_key: Optional[str] = None):
298328
raise NotImplementedError
299329

330+
300331
@dataclass_json
301332
@dataclass
302333
class ResetSandBoxResponse:

tests/access_codes/test_access_codes.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
def test_access_codes(seam: Seam):
88
run_august_factory(seam)
99

10-
some_device = seam.devices.list()[0]
10+
all_devices = seam.devices.list()
11+
some_device = all_devices[0]
1112

1213
created_access_code = seam.access_codes.create(
1314
some_device.device_id, "Test code", "4444"
@@ -29,3 +30,7 @@ def test_access_codes(seam: Seam):
2930

3031
delete_action_attempt = seam.access_codes.delete(created_access_code)
3132
assert delete_action_attempt.status == "success"
33+
34+
access_codes = seam.access_codes.create_multiple(devices=all_devices)
35+
assert len(access_codes) == len(all_devices)
36+
assert len(set([ac.common_code_key for ac in access_codes])) == 1

0 commit comments

Comments
 (0)