Overview
Seam provides a universal API to connect and control many brands of smart locks. This guide provides a rapid introduction to connecting and controlling your SmartThings Hub and smart lock using the Seam API. SmartThings Hub is connected to your local network using either Wi-Fi or Ethernet. The smart lock is paired to SmartThings via Zigbee or Z-Wave. These locks can then be remotely locked, unlocked, and have access codes programmed to allow keyless entry.
To learn more about other smart lock brands supported by Seam such as August, Yale, and Salto, head over to our integration page .
1 — Install Seam SDK
Seam provides client libraries for many languages, such as JavaScript, Python, Ruby, PHP, and others, as well as a Postman collection and OpenAPI spec.
JavaScript Python Ruby PHP Java C# Go
Copy pip install seam
# For some development environments, use pip3 in this command instead of pip.
Copy composer require seamapi/seam
Gradle:
Copy // build.gradle
dependencies {
implementation 'io.github.seamapi:java:0.x.x'
}
Maven:
Copy <!-- pom.xml -->
< dependency >
< groupId >io.github.seamapi</ groupId >
< artifactId >java</ artifactId >
< version >0.x.x</ version >
</ dependency >
Copy go get github.com/seamapi/go
Once installed, sign-up for Seam to get your API key, and export it as an environment variable:
Copy $ export SEAM_API_KEY=seam_test2ZTo_0mEYQW2TvNDCxG5Atpj85Ffw
This guide uses a sandbox workspace. You can only connect virtual devices in a sandbox workspace. If you need to connect a real device, use a non-sandbox workspace and API key.
2 — Link Your SmartThings Account with Seam
To pair a SmartThings Hub and smart lock with our API, you must sign in to a SmartThings account. To do so, create a Seam Connect Webview.
Python Javascript Ruby
Copy from seam import Seam
seam = Seam ()
webview = seam . connect_webviews . create (accepted_providers = [ "smartthings" ])
assert webview . login_successful is False
# Send the webview URL to your user
print (webview.url)
Copy import { Seam } from "seam"
const seam = Seam ()
const webview = await seam . connect_webviews .create ({
accepted_providers : [ "smartthings" ]
})
console .log (webview)
/*
{
"connect_webview_id": "811b80e7-7c87-4ce1-a938-2f6936851aa9",
"url": "https://connect.getseam.com/v1/connect_webviews/view?connect_webview_id=811b80e7-7c87-4ce1-a938-2f6936851aa9&auth_token=GXtMoAaperbuNhSCByyQMroT9HMRquXK6",
"device_selection_mode": "none",
"accepted_providers": [
"smartthings"
],
"created_at": "2022-01-03T21:11:27.002Z",
"login_successful": false,
"status": "pending"
}
*/
Copy require "seam"
seam = Seam . new (api_key: "MY_API_KEY" )
webview = seam . connect_webviews . create(
accepted_providers: [ "smartthings" ]
)
puts webview
# <Seam::ConnectWebview:0x006a950
# url="https://connect.getseam.com/connect_webviews/view?connect_webview_id=123e4567-e89b-12d3-a456-426614174000&auth_token=q123DASDASKd23DADdad29"
# status="pending"
# created_at="2022-07-06T23:20:09.785729+00:00"
# workspace_id="123e4567-e89b-12d3-a456-426614174000"
# accepted_devices=[]
# login_successful=false
# accepted_providers=["smartthings"]
# any_device_allowed=nil
# connect_webview_id="123e4567-e89b-12d3-a456-426614174000"
# custom_redirect_url=nil
# any_provider_allowed=false
# device_selection_mode="none">
Now, send the Connect Webview URL to the user. When the user signs in, you see a "completed" status on the Connect Webview or login_successful
set to true.
Python Javascript Ruby
Copy updated_webview = seam . connect_webviews . get (connect_webview_id: webview.connect_webview_id)
assert updated_webview . login_successful
Copy const updated_webview = await seam . connect_webviews .get (
webview .connect_webview_id
)
console .log ( updated_webview .login_successful)
// true
Copy updated_webview = seam . connect_webviews . get(
webview . connect_webview_id
)
puts updated_webview . login_successful
# true
You can now find all the devices that are associated with the connected account. From the returned payload, see whether the door lock is locked or unlocked.
Python Javascript Ruby
Copy from pprint import pprint
all_devices = seam . devices . list ()
some_device = all_devices [ 0 ]
assert device . properties [ "online" ] is True
assert device . properties [ "locked" ] is True
pprint (device)
# Device(device_id='a8669e4c-76e3-4db6-a827-11a65eb360ba',
# device_type='smartthings_lock',
# location=None,
# properties={'smartthings_metadata': {...}},
# 'locked': True,
# 'online': True})
Copy const all_locks = await seam . locks .list ()
const some_lock = all_locks[ 0 ]
console .log (some_lock)
/*
{
device_id: 'a8669e4c-76e3-4db6-a827-11a65eb360ba',
device_type: 'smartthings_lock',
location: null,
properties: { smartthings_metadata: {...} },
locked: true,
online: true
}
*/
Copy all_locks = seam . locks . list()
some_lock = all_locks . first
puts some_lock
# <Seam::Device:0x001e870
# device_id="123e4567-e89b-12d3-a456-426614174000"
# device_type="smartthings_lock"
# properties={
# "locked"=>true,
# "online"=>true,
# "battery_level"=>1,
# "smartthings_metadata"=>{
# "device_id"=>"123e4567-e89b-12d3-a456-426614174000",
# "device_name"=>"Yale Door Lock"},
# "name"=>"Yale Door Lock"}>
Locking a Door
Lock a door
POST
https://connect.getseam.com/locks/lock_door
Request Body
ID of the Device to be locked
200: OK Returns a pending action attempt
Copy {
"action_attempt" : {
"action_attempt_id" : "8f17e75c-dd1f-42c6-8c5c-35fbfa3e6809" ,
"status" : "pending" ,
"action_type" : "LOCK_DOOR"
} ,
"ok" : true
}
Python Javascript Ruby
Copy seam . locks . lock_door (some_device)
updated_device = seam . devices . get (some_device.device_id)
assert updated_device . properties [ "locked" ] is True
Copy await seam . locks .lock_door ({ device : some_device })
const updated_device = await seam . devices .get (some_device)
console .log ( updated_device . properties .locked)
// true
Copy action_attempt = seam . locks . lock_door(device_id: some_lock . device_id)
updated_lock = seam . devices . get(device_id: some_lock . device_id)
puts updated_lock . properties . locked
# true
Unlocking a Door
Unlock a door
POST
https://connect.getseam.com/locks/unlock_door
Request Body
ID of the Device to be unlocked
200: OK Returns a pending action attempt
Copy {
"action_attempt" : {
"action_attempt_id" : "8f17e75c-dd1f-42c6-8c5c-35fbfa3e6809" ,
"status" : "pending" ,
"action_type" : "UNLOCK_DOOR"
} ,
"ok" : true
}
Python Javascript Ruby
Copy seam . locks . unlock_door (some_device)
updated_device = seam . devices . get (some_device.device_id)
assert updated_device . properties [ "locked" ] is False
Copy await seam . locks .unlock_door ({ lock : some_lock })
const updated_lock = await seam . locks .get ( some_lock .device_id)
console .log ( updated_lock . properties .locked)
// false
Copy action_attempt = seam . locks . unlock_door(device_id: some_lock . device_id)
updated_lock = seam . devices . get(device_id: some_lock . device_id)
puts updated_lock . properties . locked
# false
Create an Access Code
POST
https://connect.getseam.com/access_codes/create
Request Body
ID of the Device to be programmed
digits to set on the device
iso8601 timestamp for desired start time of code. null implies that the code will be immediately active.
iso8601 timestamp for desired end time of code. null implies that the code will be permanent.
200: OK Returns an Action Attempt
Copy {
"action_attempt" : {
"action_attempt_id" : "123e4567-e89b-12d3-a456-426614174000" ,
"action_type" : "CREATE_ACCESS_CODE" ,
"status" : "pending" ,
"result" : {}
} ,
"ok" : true
}
For Scheduled Access Codes, both starts_at
and ends_at
are required.
For Ongoing Access Codes, don't set starts_at
or ends_at
.
Python Javascript Ruby
Copy access_code = seam . access_codes . create (
device = some_device,
name = "new code" ,
code = "876543" ,
starts_at = "2022-01-13T21:17:56.138Z" ,
ends_at = "2022-01-13T21:17:58.138Z"
)
pprint (access_code)
# AccessCode(
# access_code_id='a8669e4c-76e3-4db6-a827-11a65eb360ba',
# type='ongoing',
# name='new code',
# code='876543',
# starts_at='2022-01-13T21:17:56.138Z'
# ends_at='2022-01-13T21:17:58.138Z',
# location=None,
# )
Copy const access_code = await seam . access_codes .create ({
lock : some_lock ,
code : "876543" ,
})
console .log (access_code)
/*
{
"access_code_id": "a8669e4c-76e3-4db6-a827-11a65eb360ba",
"code": "876543",
"type": "ongoing",
"starts_at": "2022-01-13T21:17:56.138Z",
"ends_at": null
}
*/
Copy access_code = seam . access_codes . create(
device_id: some_lock . device_id ,
name: "new code" ,
code: "876543" ,
starts_at: "2022-01-13T21:17:56.138Z" ,
ends_at: "2022-01-13T21:17:58.138Z"
)
puts access_code
# <Seam::AccessCode:0x002cf60
# code="876543"
# name="new code"
# type="time_bound"
# ends_at="2022-01-13T21:18:00.000Z"
# starts_at="2022-01-13T21:18:00.000Z"
# created_at="2022-07-06T22:57:56.650Z"
# access_code_id="a8669e4c-76e3-4db6-a827-11a65eb360ba">
Delete an Access Code
DELETE
https://connect.getseam.com/access_codes/remove
Request Body
200: OK Returns an Action Attempt
Copy {
"action_attempt" : {
"action_attempt_id" : "123e4567-e89b-12d3-a456-426614174000" ,
"action_type" : "DELETE_ACCESS_CODE" ,
"status" : "pending" ,
"result" : {}
} ,
"ok" : true
}
Python Javascript Ruby
Copy attempt = seam . access_codes . delete (access_code = access_code)
pprint (attempt)
# ActionAttempt(
# action_attempt_id='af0155aa-51fe-4e63-9acb-2fbd33675cac',
# action_type='DELETE_ACCESS_CODE',
# status='success',
# result={},
# error=None
# )
Copy await seam . access_codes .remove ({
code : access_code
})
Copy attempt = seam . access_codes . delete(access_code_id: access_code . access_code_id)
puts attempt
# <Seam::ActionAttempt:0x0000000107026c98
# status="success"
# action_type="DELETE_ACCESS_CODE",
# action_attempt_id="af0155aa-51fe-4e63-9acb-2fbd33675cac",
# result={}>
List Access Codes
GET
https://connect.getseam.com/access_codes
Query Parameters
200: OK Returns a list of Access Codes
Copy {
"access_codes" : [
{
"access_code_id" : "123e4567-e89b-12d3-a456-426614174000" ,
"code" : "876543" ,
"name" : "ongoing code" ,
"type" : "ongoing" ,
"status" : "setting" ,
"created_at" : "2022-02-11T22:53:40.988Z"
} ,
{
"access_code_id" : "123e4568-e89b-12d3-a456-426614174000" ,
"code" : "141498" ,
"name" : "scheduled code" ,
"type" : "time_bound" ,
"status" : "set" ,
"ends_at" : "2022-02-11T22:55:00.000Z" ,
"starts_at" : "2022-02-11T22:54:00.000Z" ,
"created_at" : "2022-02-11T22:53:40.988Z"
}
]
}
Python Javascript Ruby
Copy # you can use a device or a device_id as the "device" parameter
seam . access_codes . list (device = some_device)
# [
# AccessCode(
# access_code_id='af5272b1-2a49-4eb5-9388-2447fc7b5bd1',
# type='ongoing',
# code='123459',
# starts_at=None,
# ends_at=None,
# name='ongoing 1'
# ),
# AccessCode(
# access_code_id='57f8216a-ebc4-46e7-8f89-9c9448f70733',
# type='ongoing',
# code='189644',
# starts_at=None,
# ends_at=None,
# name='ongoing 2'
# )
# ]
Copy // you can use a device or a device_id as the "device" parameter
const access_codes = await seam . access_codes .list ({
device : {
"device_id" : "af5272b1-2a49-4eb5-9388-2447fc7b5bd1"
}
})
console .log (access_codes)
/*
[
{
"access_code_id": "a8669e4c-76e3-4db6-a827-11a65eb360ba",
"code": "876543",
"type": "ongoing",
"starts_at": "2022-01-13T21:17:56.138Z",
"ends_at": null
}
]
*/
Copy access_codes = seam . access_codes . list(device_id: some_lock . device_id)
puts access_codes
# [<Seam::AccessCode:0x002cf60
# code="876543"
# name="new code"
# type="time_bound"
# ends_at="2022-01-13T21:18:00.000Z"
# starts_at="2022-01-13T21:18:00.000Z"
# created_at="2022-07-06T22:57:56.650Z"
# access_code_id="09c1ba5a-623a-4de4-9826-91377e468c14">]