关于 seam-access-grants
指导开发者在现有代码库中集成 Seam Access Grants,包括围绕预订创建、更新和删除访问授权。重点是按房间映射设备,并保存授权 ID 以供后续操作。
主要功能
- 01在现有处理程序中加入 Seam 调用
- 02按预订时间段创建授权
- 03将每个房间映射到对应设备 ID
- 04使用已存 ID 更新或删除授权
- 05添加访问事件 Webhook 端点
适用场景
- 01在创建预订时授予门锁访问权限
- 02预订变更后调整访问时间
- 03取消预订时撤销访问权限
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ seamapi/ seam-plugin --skill seam-access-grants安装后在 Claude Code 中输入/seam-access-grants确认已加载。
附带文件references/production-checklist.mdreferences/troubleshooting.md
详细介绍
Seam 访问授权
你是一名经验丰富的 Seam 集成工程师。直接在开发者现有的代码库中编写集成代码。
实施方法
- 快速推进。 搜索关键文件(预订/预约处理程序、路由、模型),阅读后立即开始编写代码。
- 在现有文件中编写代码。 直接在现有服务/处理程序函数中添加 Seam 调用。不要创建包装服务。
- 尽量减少改动。 仅修改需要调用 Seam 的文件和 webhook 路由。安装 SDK,添加导入,并添加调用。
Access Grants 的工作原理
创建访问授权时,需要指定用户身份、目标设备、请求的访问方式(PIN、手机钥匙)和时间范围。Seam 会在门锁上配置凭证。你必须存储 access_grant_id,以便之后更新或删除该授权。
1. 安装 SDK 并初始化
不要固定到特定版本。
npm install seam # Node.js
pip install seam # Python
bundle add seam # Ruby
Next.js 的重要注意事项: 在模块作用域中使用 new Seam() 会导致 next build 失败。请使用惰性 getter:
import { Seam } from "seam";
let _seam: Seam;
function getSeam() {
if (!_seam) _seam = new Seam({ apiKey: process.env.SEAM_API_KEY! });
return _seam;
}
对于 Express / 标准 Node.js:
import { Seam } from "seam";
const seam = new Seam({ apiKey: process.env.SEAM_API_KEY });
from seam import Seam
seam = Seam(api_key=os.environ["SEAM_API_KEY"])
2. 获取设备 ID
Access Grants 的目标是由 device_id 指定的特定设备。每个房间/门都必须映射到自己的设备 ID,不要对所有房间使用同一个全局设备。
查找设备 ID 的位置:
- 每个房间对应的环境变量:
SEAM_DEVICE_ROOM_101、SEAM_DEVICE_ID_ROOM_A1等。 - 应用的数据模型(例如
room.seamDeviceId、unit.deviceId)
如果缺少映射,则操作失败,不要回退到全局设备。将访问权限授予错误的门,比操作失败更糟。
3. 创建预订时创建访问授权
直接在创建函数中添加。在预订对象上存储 access_grant_id。
// Inside createBooking(), after saving the booking:
const deviceId = getDeviceIdForRoom(room); // Must resolve per-room
if (!deviceId) {
throw new Error(`No Seam device configured for room ${room.id}`);
}
try {
const accessGrant = await seam.accessGrants.create({
user_identity: {
full_name: guest.name,
email_address: guest.email
},
device_ids: [deviceId],
requested_access_methods: [
{ mode: "code" } // PIN code
// { mode: "mobile_key" } // Add for mobile key + Instant Key
],
starts_at: booking.checkIn,
ends_at: booking.checkOut
});
booking.seamAccessGrantId = accessGrant.access_grant_id;
} catch (err) {
console.error("Seam access grant failed:", err);
// Consider: should this fail the booking? If access is required, throw.
}
# Inside create_booking(), after saving:
device_id = get_device_id_for_room(room) # Must resolve per-room
if not device_id:
raise ValueError(f"No Seam device configured for room {room.id}")
try:
access_grant = seam.access_grants.create(
user_identity={"full_name": guest.name, "email_address": guest.email},
device_ids=[device_id],
requested_access_methods=[{"mode": "code"}],
starts_at=booking.check_in,
ends_at=booking.check_out
)
booking.seam_access_grant_id = access_grant.access_grant_id
except Exception as e:
print(f"Seam access grant failed: {e}")
# Consider: should this fail the booking? If access is required, raise.
注意事项
- 存储
access_grant_id——更新和删除时需要用到它。如果它是null/undefined,则授权失败,需要重试。若预订模型中没有该字段,请添加。 - 绝不要回退到全局设备——每个房间都必须映射到自己的特定设备。授权进入错误的房间,比无法进入更糟。
user_identity接受full_name和email_address,而不是name和email。device_ids是一个数组——一次调用即可授予多个门的访问权限。requested_access_methods——PIN 使用"code",手机钥匙和 Instant Key 使用"mobile_key"。- 确定失败处理方式:如果预订必须具备访问权限(例如酒店房间),则 Seam 调用失败时抛出错误,避免在没有可用密码的情况下确认预订。如果访问权限可选(例如健身房),则记录日志并继续。
4. 预订变更时更新访问授权
if (booking.seamAccessGrantId) {
await seam.accessGrants.update({
access_grant_id: booking.seamAccessGrantId,
starts_at: booking.checkIn,
ends_at: booking.checkOut
});
}
5. 取消预订时删除访问授权
if (booking.seamAccessGrantId) {
await seam.accessGrants.delete({
access_grant_id: booking.seamAccessGrantId
});
}
if booking.seam_access_grant_id:
seam.access_grants.delete(
access_grant_id=booking.seam_access_grant_id
)
6. 添加 webhook 端点
遵循代码库中现有的 webhook 模式:
router.post("/seam", (req, res) => {
const { event_type, ...data } = req.body;
switch (event_type) {
case "access_code.set_on_device":
console.log("PIN set on lock:", data.access_code_id);
break;
case "access_code.failed_to_set_on_device":
console.log("PIN failed:", data.access_code_id);
break;
case "device.disconnected":
console.log("Lock offline:", data.device_id);
break;
}
res.json({ received: true });
});
7. 将函数改为异步
将服务函数改为异步,并更新调用方以使用 await。
如果出现问题,请阅读 references/troubleshooting.md。如需做好生产环境准备,请阅读 references/production-checklist.md。