简体中文
简体中文
English
联系我们
注册
登录
语言
简体中文
简体中文
English
联系我们
登录
注册
返回主站
layout空间导航
文档中心智慧商业三方设备接入涂鸦智慧商业三方可视门禁接入 API(Linux 版)

涂鸦智慧商业三方可视门禁接入 API(Linux 版)

更新时间:2022-01-21 09:35:59下载pdf

本文介绍了 Linux 版本的涂鸦智慧商业三方可视门禁接入 API。

关键术语

序号 术语 说明
1 cid 三方设备 ID,设备的唯一编号
2 sn 下发 DP 点的唯一编号
3 atop 接口 涂鸦对外提供的 HTTPS 接口
4 下发 云端指令通过 MQTT 消息通知给设备
5 上报 设备端消息通过 atop 接口或 MQTT 消息通知给云端

整体架构

涂鸦智慧商业三方可视门禁接入 API(Linux 版)

  本文档仅支持直连设备的接入方式,即三方门禁设备内置 Tuya IPC SDK 的方式。

业务流程

       ![云可视对讲整体流程.jpg](https://images.tuyacn.com/content-platform/hestia/1642067676c0cd5df3d8a.jpg)

功能点列表

序号 功能点 说明
1 设备配网 三方设备配网完成后,才能接收云端的指令,和云端进行交互
2 视频通话 包含发起呼叫、接听、挂断
3 人员操作 包括人员的新增、更新、移除、禁用、启用
4 人脸操作 包括人脸的新增、更新、移除
5 门禁卡操作 包括门禁卡的新增、更新、移除
6 二维码操作 包括二维码的新增、更新、移除
7 远程开门 用户通过涂鸦 App,下发开门指令给门禁设备,门禁设备收到开门指令后,打开门并上报通行记录
8 通行事件上报 门禁设备识别到有人通行,上报通行记录
9 评估人脸照片分数 服务端评估抓拍的人脸照片分数, 仅在前台登记客户时使用
10 动态刷新人员通行二维码 除支持下发的二维码通行以外, 增加了对动态可刷新二维码通行的支持
11 门禁通行密码操作 包括通行密码的新增、更新、移除

对接流程

下载集智社区 App

应用商店下载集智社区 App,通过集智社区 App 建立起和设备之间的音视频通话。

三方设备引入涂鸦 IPC SDK

       [涂鸦 IPC SDK(linux 版)demo 地址](https://github.com/tuya/tuya-iotos-embeded-sdk-multimedia)

       [涂鸦 IPC SDK 文档地址](https://developer.tuya.com/cn/docs/iot/smart-product-solution/product-solution-ipc/ipc-sdk-solution?categoryId=876019)

进行设备配网及配置

申请私钥

向涂鸦申请私钥,申请的私钥用来对配网信息进行解密。

设备扫码

您可以向涂鸦申请测试账号,并通过 二维码的生成地址 生成二维码。

设备扫描施工 App 二维码进行设备配网。二维码信息格式:

{
   "t":"AYRfQ****",
    "a":"a1-cn****",
    "key":"1168****"
}

设备扫描二维码,并获取二维码的内容,根据二维码的内容调用 HTTPS 接口。具体调用方法如下:

  1. 请求 URL。
    请求 URL 格式: https://%s/api.json?a=%s&v=%s&clientId=%s&lang=%s&t=%s&requestId=%s&sign= %s
    完整 URL 示例: https://a1-cn.wgine.com/api.json?a=tuya.device.industry.base.edge.net.qrcode.query&v =1.0&clientId=mtn9nxykycwmknutmfky&lang=zh&t=1598010014&requestId=0928d339a d654fd1874b28d245948fc3&sign=d014ed4d192f60edd2d4ffeab545919bc3527ff4b0df2 6b4176ef1c0baed10cf

  2. 生成URL 参数。

    a:apiName    默认:tuya.device.industry.base.edge.net.qrcode.query
    v:version    默认:1.0
    time         举例:1598010014 当前时间的 Long 类型
    lang         默认:zh
    clientId     默认:mtn9nxykycwmknutmfky
    requestId    通过 UUID 生成,如:0928d339ad654fd1874b28d24594****
    postData     说明:将扫描的二维码中的 key、cid 生成 JSON 字符串,格式为: {"t":1598010014,"key":"a354fffa81674d80b7d1e0696ad6****","cid":"device16411****"},再进行 md5 加密。该字符串通过 md5 加密后为:a1941a0237df3e2e2e315c8dca27c0a1
    

    sign 说明:将 a、v、time、lang、clientId、requestId、postData 先进行升序排序,排序后生成的字符串为: a=tuya.device.industry.base.edge.net.qrcode.query||clientId=mtn9nxykycwmknutmfky||l ang=zh||postData=a1941a0237df3e2e2e315c8dca27c0a1||requestId=0928d339ad654fd 1874b28d245948fc3||time=1598010014||v=1.0
    然后将排序后的数据 a 通过 secret=wjx7ss3hww7xcpc7s4t3unxwfuw37w34(密钥)进行 hmacSha256 加密,加密后生成 sign 结果为:d014ed4d192f60edd2d4ffeab545919bc3527ff4b0df26b4176ef1c0baed10cf

  3. 生成 Http Body 参数.

    1. 将 cid(设备唯一序列号)、扫描二维码结果中 key 和时间 t 生成 JSON 字符串,数据为: {“t”:1598010014,“key”:“a354fffa81674d80b7d1e0696ad6****”,“cid”:“device164114432”}
    2. 将请求 URL 和 Body 作为 Https 入参,body 格式为:postData = {“t”:1598010014,“key”:“a354fffa81674d80b7d1e0696ad6****”,“cid”:“device16411****”}
    3. 根据 URL 和 Body,发送 HTTP POST 请求。
  4. 返回数据示例。

    {
        "result": {
            "data": "0e90f53c95772614c7a9be4b41f7130d764551a315f11d3bd04c7b50ac778f23d6e 6184f4a7e47d2fe58858c5019bc1086cb622855b7d0fad3491f0ee73bab21835bc67 ecbc04511321b0de51f63b3fc30d281c1a304244d0cefc59a976dbc830b11ef43785 204c16952edfa8eb6a7572cc8d8e49927f558f4dbd1086f3af5b33c8c998b4d84119 39bc78d2334aa633c49ba4f099cbbabc8a0de3e06f44a24c63d5ae167d985e70e19a 1bfedd44ddff1248b683c41c5b4733a8a9f201b85438b",
            "key": "a354fffa81674d80b7d1e0696ad69bf3"
        },
        "t": 1598010614705,
        "success": true,
        "status": "ok"
    }
    
  5. 对 data 时间用 AES 进行解密,密钥为申请的私钥.解密后的数据示例。

    {
        "authKey": "BO3CzAHnqtbwyf4d6T2F3rOlJwugpmmu",
        "installLocation": "1 幢 1 单元",
        "vendor": "yufan",
        "pid": "toftl4za2qhgf8uc",
        "projectId": "1197119053387350016",
        "uuid": "3aaa3d3fef65f66a"
    }
    

设备配网

设备配网所需要的参数来自设备扫码,包含: t、pid、uuid、authKey 其中 t 来自二维码信息,有效期 10 分钟。

t、 pid、 uuid、authKey 在配⽹成功后,需要写⼊设备。确保设备重启、断电或恢复出厂设置后,还能重新获取上述数据,再次调用设备配网接口,从⽽连上云端;其中 t 在配网成功后不会过期。

具体配网实现参考 IPC SDK 开发手册及 Demo。

设备和社区绑定

设备配网成功以后,调用该 atop 接口。仅需要在首次配网成功时调用,调用参数如下:

请求参数

请求参数 参数类型 描述 是否必传
cid String 三方设备 ID
deviceId String 涂鸦设备 ID
uuid String 涂鸦 UUID
projectId String 项目 ID
productId String 产品 ID,3.3.2 中的 PID
installLocation String 安装地址描述
vendorCode String 供应商 Code
macAddress String 设备的 Mac 地址
extendData String 扩展属性

返回参数

类型 说明
Boolean
  • true:调用成功
  • false:调用失败

调用示例如下:

    api_name: tuya.device.industry.base.edge.device.area.bind
    api_version:1.0
    message:
        {
	"input": "{\"cid\":\"device164114432\",\"deviceId\":\"6c4363e795dcd496e6bwmx\",\"installLocation\":\"1 幢 1 单元\",\"productId\":\"toftl4za2qhgf8uc\",\"projectId\":\"1197119053387350016\",\"uuid\":\"b893fa6d54ed3fbc\",\"vendorCode\":\"yufan\",\"macAddress\":\"01:23:45:67:89:ab\"}"
}
   result:
         {
         	"result": {true},
	         "t": 1598010614705,
	         "success": true,
	         "status": "ok"
         }

API 说明

视频通话

开启视频通话、接听、挂断时,调用该 atop 接口,调用参数如下:

请求参数

请求参数 参数类型 描述 是否必传
sn String SN 号,除呼叫以外,其它模式都需要设置;设置的值为呼叫时返回的 SN
mode String 设备模式,如门铃: ac_doorbell
type String 操作类型,见详细说明
deviceId String 涂鸦设备 ID
targetAddress String 目标地址,格式:01-01-01-01-01-01,表示 01 小区 01 苑 01 幢 01 单元 01 层 01 号房间。没有传 00,比如没有苑,苑的位置传 00
extend String 通话后挂断返回 JSON 字符串:{“beginTime”:1592373736,“endTime”: 1593373736}
  • beginTime:通话接听时间
  • endTime:通话结束时间,时间类型为 long

返回参数

类型 说明
String SN 号

targetAddress 传值说明

例:xx 小区 x 幢 x 单元 101

  • 当设备作为单元门时:

    目标地址三方自行解析为 00-00-00-00-01-01 格式,其中在满足该格式情况下,前 4 组的取值范围不影响单元门口机呼叫功能,默认补 00 即可,后 2 组根据现实呼叫情况可以从设备上输入,若呼叫 101 则需要传给云端 00-00-00-00-01-01。

    前四组:00-00-00-00:不影响呼叫功能,默认补 00。

    后二组:01-01:设备上输入真实的值。

  • 当设备作为幢门时:

    如上所述,解析为 00-00-00-01-01-01。

    前三组:00-00-00:不影响呼叫功能,默认补 00。

    后三组:01-01-01:设备上输入真实的值。

  • 当设备作为小区门时:

    如上所述,解析为 00-01-01-01-01-01。

    前一组:00:不影响呼叫功能,默认补 00。

    后五组:01-01-01-01-01:设备上输入真实的值。

操作类型详细说明

操作类型 场景 操作
1 设备呼叫 App 和室内机 设备发送 type=1 事件
3 设备呼叫 App 和室内机,设备主动挂断,App 或室内机不操作 设备发送 type=3 事件,如果 App 已接听,需要传 extend
4 设备呼叫 App 和室内机,App 或室内机未接听,呼叫时间超过设备限定,设备挂断。设备限定呼叫超时时间默认 30s 设备发送 type=4 事件
5 设备呼叫 App 和室内机,App 或室内机接听后,App 或室内机挂断;sdk 会有回调事件给设备。 设备发送 type=5 事件,需要传 extend
6 设备呼叫 App 和室内机,社区 App 或室内机接听.sdk 会有回调事件给设备。 设备发送 type=6 事件
7 设备呼叫 App 和室内机,App 或室内机接听后,通话时间超过设备限定,设备挂断;设备限定通话超时时间默认 90s。 设备发送 type=7 事件,需要传 extend

调用示例如下:

   api_name: tuya.device.industry.base.edge.control.protocol.send
   api_version:1.0
   message:
       {
	"input": "{\"deviceId\":\"6c8f87e58d5ae1ccd7qbqa\",\"mode\":\"ac_doorbell\",\"targetAddress\":\"00-00-00-00-01-01\",\"type\":\"1\"}"
}
   result:
         {
         	"result":"1208283103756304384",
	         "t": 1598010614705,
	         "success": true
         }

设备同时支持 P2P 客户端数量

设备配网成功后,通过该 atop 接口查询设备同时支持 P2P 客户端的数量。

数量值作为 s_mgr_info.max_p2p_user 的值,以限制同时查看视频的客户端数量。

请求参数

请求参数 参数类型 描述 是否必传
deviceId String 涂鸦设备 ID

返回参数

参数名 类型 说明
maxClientNum Integer 最大同时支持 p2p 客户端的数量
deviceId String 涂鸦设备 ID

调用示例如下:

   api_name: tuya.device.industry.base.edge.device.p2p.client.num

   api_version:1.0
   message:
       {
	       "input": "{\"deviceId\":\"6c8f87e58d5ae1ccd7qbqa\"}"
       }
   result:
         {
	         "result": {
		           "maxClientNum": 3,
		           "deviceId": "6c8f87e58d5ae1ccd7qbqa"
	          },
	         "t": 1604494202,
	         "success": true
         }

评估人脸照片分数

对门禁抓拍的人脸照片进行评估,返回人脸照片分数。对抓拍的人脸照片进行评分,确认照片合格后,下发到其它门禁。

请求参数

请求参数 参数类型 描述 是否必传
fileId String 图片 ID,获取图片 ID 的接口

返回参数

参数名 类型 说明
value Double 人脸评分阀值范围 0~10,8 分以上为识别成功
url String 人脸照片 URL

调用示例如下:

   api_name: tuya.device.industry.base.edge.ai.face.score
   api_version:1.0
   message:
       {
	       "fileId": "45976fb2a6c06811ecc6a790ac562851"
       }
   result:
         {
	         "result": {
		           "value": 10,
		           "url": "www.tuya.com/hello.jpg"
	          },
	         "t": 1604494202,
	         "success": true
         }

查询时间规则列表

请求参数

请求参数 参数类型 描述 是否必传
input List<String> 时间规则 ID 列表

返回参数

参数名 类型 说明
ruleId Float 最大同时支持 P2P 客户端的数量
ruleName String 时间规则名称
period String 可通行时间周期,JSON 字符串,例如: [{“weekDay”:1,“weekTime”:[{“start”:“32400000”,“end”:“64800000”}]},{“weekDay”:2,“weekTime”:[{“start”:“684000000”,“end”:“75600000”}]}]
allowedDate String 可通行时间段,JSON 字符串,例如:[{“start”:“1571587200000”,“end”:“1864569600000”},{“start”:“1573022801885”,“end”:“1864569600000”}]
deniedDate String 不可通行时间段, JSON 字符串,例如: [{“start”:“1571587200000”,“end”:“1864569600000”},{“start”:“1573022801885”,“end”:“1864569600000”}]

调用示例如下:

   api_name: tuya.device.industry.base.edge.device.time.rules
   api_version:1.0
   message:
       {
	       "input": ["242021****00000001","242021****00000002"]
       }
   result:
         {
	         "result":[ {
              "ruleId": "242021****00000001",
               "ruleName": "时间规则 1",
               "period": " [{\"weekDay\":1,\"weekTime\":    [{\"start\":\"1571587200000\",\"end\":\"1864569600000\"},{\"start\":\"1864569600000\",\"end\":\"1864569700000\"}]}]",
               "allowedDate": "[{\"start\":\"1571587200000\",\"end\":\"1864569600000\"}]",
               "deniedDate": "[{\"start\":\"1571587200000\",\"end\":\"1864569600000\"}]",
            }],
	         "t": 1604494202,
	         "success": true
         }

时间段规则说明

  • period:可通行时间周期,可以指定在一周中哪些时间允许进行。

  • weekDay:用 1 到 7 分别表示周一到周日,开始时间、结束时间用距离 0 点相隔的毫秒数表示,比如 32400000 表明是 9 点
    如 [{“weekDay”:1,“weekTime”:[{“start”:“32400000”,“end”:“64800000”}]},{“weekDay”:2,“weekTime”:[{“start”:“684000000”,“end”:“75600000”}]}] 表明的含义是 周一 9:00~18:00,周二 19:00~21:00 可通行,

  • allowedDate:可通行时间段,13 位时间戳表示

  • deniedDate:不可通行时间段,13 位时间戳表示

  • 时间优先级:不可通行时间段 > 可通行时间段 > 可通行时间周期
    先判断访问时间是否在“不可通行时间段”中,若在则不允许通行。否则看访问时间是否在“可通行时间段”中,若在则允许通行,若不在继续判断是否在“可通行时间周期”中,若在则允许通行,若都不在,则不能通行。

上报通行记录

门禁识别到有用户通行时上报的记录,调用 MQTT 接口进行上报,上报参数如下:

请求参数

请求参数 参数类型 描述 是否必传
uid String 用户 ID,陌生人 UID 为空
t long 事件发生时间,时间的 long 类型
way Integer 开门方式:
  • 1:卡
  • 2:密码
  • 3:二维码
  • 4:人脸
  • 5:指纹
  • 8:蓝牙
  • 9:远程
  • 10:其它
imageId String 图片文件 ID,获取图片 ID 的接口
passPwd String 通行密码(密码通行方式下必传)
cardNo String 卡号
temp String 体温
success Integer 是否成功
  • true:成功
  • false:失败
message String 消息提示(错误原因)

返回参数

类型 说明
boolean
  • true:调用成功
  • false:调用失败

参考 IPC SDK 开发手册的 DP 点数据保存接口,上报 DP 点:126, DP 点数据格式示例:

{\"uid\":\"uid9527\",\"imageId\":\"imageId\",\"t\":1598521531166,\"success\":1,\"way\":4}

上报门禁告警

门禁调用 MQTT 接口主动上报告警事件,上报参数如下:

请求参数 参数类型 描述 是否必传
alarmId String 告警 ID
alarmType Integer 告警类型
alarmName String 告警类型名称
alarmContent String 告警值
alarmTime Long 告警发送时间
confirmEnable Boolean 是否已处理 true:已处理
extendData String 扩展信息

参考 IPC SDK 开发手册的 DP 点数据保存接口,上报 DP 点:185,DP 点数据格式示例:

{
    \"deviceId\":\"123123123\",
    \"alarmName\": \"设备异常\",
    \"alarmTime\": 1609435288373,
    \"alarmType\": 1,
    \"alarmContent\": \"异常\",
    \"dealTime\": 1609435348373,
    \"alarmId\": \"123123123123\",
    \"extendData\": \"\"
}

上传图片

  上传图片来自于门禁设备识别到有用户通行的时候拍的照片。

获取上传图片的 url 及 token

  调用 atop 接口获取,调用示例如下:
    api_name: tuya.device.industry.base.edge.file.upload.token
    api_version: 1.0
    message: {"subjectType":"edgegateway_faceImg"}
    result:
         {
         	"result": {
	              "uploadUrl": "https://cetus-cn.wgine.com/v1.0/file/upload",
	              "token": "0f2bf5e20cf2cc99c73986c196b0d2c0"
              },
	         "t": 1598010614705,
	         "success": true,
	         "status": "ok"
         }

上传图片

发送 Https post 请求上传图片,https url 为 6.6.1 接口返回的 uploadUrl,6.6.1 接口返回的 token 放在 header 里面,header 的 key 为 file_upload_token,request body 里面放的是图片的字节流,上传的图片类型为 image/jpg。

上传示例:

涂鸦智慧商业三方可视门禁接入 API(Linux 版)
涂鸦智慧商业三方可视门禁接入 API(Linux 版)

response body 返回的结果:

{
    "result": {"tmp_file_id": "45976fb2a6c06811ecc6a790ac562851"},
    "success": true,
}

其中 tmp_file_id 的值即为照片文件 ID。

上报执行结果

每一个下发的 DP 点在设备端执行成功后,调用该 atop 接口,上报下发 DP 点的执行结果。

请求参数

参数名 类型 说明 是否必传
sn String sn 号
success Integer 设备状态:
  • 0:失败
  • 1:成功
message String 执行结果描述

返回参数

类型 说明
Boolean
  • true:调用成功
  • false:调用失败

调用示例如下:

	api_name: tuya.device.industry.base.edge.Issue.data.cmd.result.upload
	api_version: 1.0
	message: {"message": "执⾏成功","success": 1,"sn": 12082831037563****}
	result: {
		"result": {true},
		"t": 1598010614705,
		"success": true,
		"status": "ok"
	}

人员通行权限校验

人员通行权限校验步骤:

  1. 人员需要先通过门禁卡、人脸或二维码验证。
  2. 验证失败则不允许通行。
  3. 验证成功则调用人员通行权限校验接口进行校验,即进行健康码认证,获取人员健康码状态。
  4. 若健康码为黄码或红码则不允许通行,若健康码为绿码或没有健康码则允许通行。

请求参数

参数名 类型 说明 是否必传
uid String 用户 ID
deviceId String 设备 ID
verifyTypeList List<Integer> 验证类型列表
  • 1:健康码认证

返回参数

参数名 类型 说明
uid String 用户 ID
extend String JSON 字符串,如{“healthCode”:“unknown”}

extend 说明

验证类型 extend 描述
1 返回 healthCode 字段
  • unknown(未知,内部异常或没有健康码)
  • red(红码)
  • green(绿码)
  • yellow(黄码)

调用示例如下:

  api_name: tuya.industry.base.edge.door.verify.pass
   api_version:1.0
   message:
       {
	       "uid": "ay16110581781387****",
	       "deviceId": "6c2421a42631dcda68****",
	       "verifyTypeList": "[1]"
       }
   result:
         {
	         "result": {
		           "uid": "ay16110581781387****",
		           "extend": "{\"healthCode\":\"unknown\"}"
	          },
	         "t": 1614167169000,
	         "success": true
         }

门禁功能说明

新增人员

下发参数如下:

请求参数 参数类型 描述 是否必传
uid String 涂鸦用户 ID
name String 涂鸦用户名
idCard String 证件 ID
phone String 手机号
beginTime Long 有效期开始时间,13 位时间戳
endTime Long 有效期结束时间,13 位时间戳
ruleIdList List 时间规则 ID 列表
userFloor Integer 人员所在楼层,边缘侧呼梯时使用
secretKey String 用户秘钥,长度 32 位,建议 UUID 生成。App 端和设备端基于同一个秘钥生成动态二维码
refreshTime Long 二维码刷新时间,默认 5 分钟,单位毫秒
extend String 扩展信息,JSON 字符串

参考 IPC SDK 开发手册的 DP 点数据读取。下发的 DP 点:128。DP 点数据格式示例:

{
  "t": 1573557869,
  "data": {
  	"cid": "12345678",
  	"dps": {
  		"128": {
  			"data": {
  				"beginTime": 1576846010886,
  				"endTime": 1576846010886,
  				"idCard": "idcard",
  				"name": "username",
  				"phone": "1778888****",
  				"uid": "123123",
        "userFloor": 1,
        "ruleIdList":["242021030300000001","242021030300000002"],
        "secretKey":"fa26477e064d884c2fc13120e9da****",
        "refreshTime": 300000
  			},
  			"mode": "dc_userInfo",
  			"sn": 120800586238851****,
  			"type": "add"
  		}
  	}
  }
}

该接口需要支持幂等,即同一个 UID 人员新增成功后,还支持再次新增,再次新增时返回成功。

如果 beginTime 和 endTime 不为空,则人员有效期取 beginTime 和 endTime,否则以 timeRuleList 为准,timeRuleList 最大长度支持 3 条。获取到 ruleIdList 列表后,需要根据列表查询具体的时间规则列表,在人员通行的时候,需要根据时间规则校验用户是否可以通行。

更新人员

下发参数如下:

请求参数 参数类型 描述 是否必传
uid String 涂鸦用户 ID
name String 用户名
idCard String 证件 ID
phone String 手机号
beginTime Long 有效期开始时间,13 位时间戳
endTime Long 有效期结束时间,13 位时间戳
ruleIdList List 时间规则 ID 列表
userFloor String 人员所在楼层,边缘侧呼梯时使用
secretKey String 用户秘钥,长度 32 位,建议 UUID 生成;App 端和设备端基于同一个秘钥生成动态二维码
refreshTime Long 二维码刷新时间,默认 5 分钟,单位毫秒
extend String 扩展信息,JSON 字符串

参考 IPC SDK 开发手册的 DP 点数据读取,下发的 DP 点:128 ,DP 点数据格式示例:

{
	"t": 1573557869,
	"data": {
		"cid": "12345678",
		"dps": {
			"128": {
				"data": {
					"beginTime": 1576846387411,
					"endTime": 1576846387411,
					"idCard": "idcard",
					"name": "username",
					"phone": "1778888****",
					"uid": "uid",
          "userFloor": 1,
          "secretKey":"fa26477e064d884c2fc13120e9da****",
          "refreshTime": 300000
				},
				"mode": "dc_userInfo",
				"sn": 120800744162785****,
				"type": "update"
			}
		}
	}
}

如果 beginTime 和 endTime 不为空,则人员有效期取 beginTime 和 endTime,否则以 timeRuleList 为准,timeRuleList 最大长度支持 3 条。获取到 ruleIdList 列表后,需要根据列表 查询具体的时间规则列表

在人员通行的时候,需要根据时间规则校验用户是否可以通行。

删除人员

下发参数如下:

请求参数 参数类型 描述 是否必传
uid String 涂鸦用户 ID

参考 IPC SDK 开发手册的 DP 点数据读取,下发的 DP 点:128,DP 点数据格式示例:

{
	"t": 1573557869,
	"data": {
		"cid": "12345678",
		"dps": {
			"128": {
				"data": {
					"uid": "123123"
				},
				"mode": "dc_userInfo",
				"sn": 120801049576484****,
				"type": "del"
			}
		}
	}
}

人员通行授权

下发参数如下:

请求参数 参数类型 描述 是否必传
uid String 涂鸦用户 ID
enable Integer 启用/禁用
  • 0:禁用
  • 1:启用
默认添加的人员通行授权状态是启用,人员通行状态一旦改为禁用,则用户通过人脸、二维码、门禁卡均不能通行

参考 IPC SDK 开发手册的 DP 点数据读取,下发的 DP 点:128。DP 点数据格式示例:

{
	"t": 1573557869,
	"data": {
		"cid": "12345678",
		"dps": {
			"128": {
				"data": {
					"enabled": 1,
					"uid": "uid"
				},
				"mode": "dc_userInfo",
				"sn": 12080078984253****,
				"type": "enable"
			}
		}
	}
}

新增人脸

下发参数如下:

请求参数 参数类型 描述 是否必传
uid String 涂鸦用户 ID
faceId String 人脸 ID
url String 人脸照片地址

参考 IPC SDK 开发手册的 DP 点数据读取,下发的 DP 点:128;DP 点数据格式示例:

{
	"t": 1573557869,
	"data": {
		"cid": "12345678",
		"dps": {
			"128": {
				"data": {
					"faceId": "faceid",
					"uid": "uid",
					"url": "url"
				},
				"mode": "dc_faceInfo",
				"sn": 120801153336434****,
				"type": "add"
			}
		}
	}
}

该接口需要支持幂等,即同一个 faceId 人脸照片新增成功后,还支持再次新增,再次新增时返回成功。

更新人脸

下发参数如下:

请求参数 参数类型 描述 是否必传
uid String 涂鸦用户 ID
faceId String 人脸 ID
url String 人脸照片地址

参考 IPC SDK 开发手册的 DP 点数据读取,下发的 DP 点:128。DP 点数据格式示例:

{
	"t": 1573557869,
	"data": {
		"cid": "12345678",
		"dps": {
			"128": {
				"data": {
					"faceId": "faceid",
					"uid": "uid",
					"url": "url"
				},
				"mode": "dc_faceInfo",
				"sn": 120801477101051****,
				"type": "update"
			}
		}
	}
}

移除人脸

下发参数如下:

请求参数 参数类型 描述 是否必传
uid String 涂鸦用户 ID
faceId String 人脸 ID

参考 IPC SDK 开发手册的 DP 点数据读取,下发的 DP 点:128。DP 点数据格式示例:

{
	"t": 1573557869,
	"data": {
		"cid": "12345678",
		"dps": {
			"128": {
				"data": {
					"faceId": "faceid",
					"uid": "uid"
				},
				"mode": "dc_faceInfo",
				"sn": 120801517856684****,
				"type": "del"
			}
		}
	}
}

新增门禁卡

下发参数如下:

请求参数 参数类型 描述 是否必传
uid String 涂鸦用户 ID
cardNo String 门禁卡号

参考 IPC SDK 开发手册的 DP 点数据读取,下发的 DP 点:128。DP 点数据格式示例:

{
	"t": 1573557869,
	"data": {
		"cid": "12345678",
		"dps": {
			"128": {
				"data": {
					"cardNo": "cardNo",
					"uid": "uid"
				},
				"mode": "dn_cardInfo",
				"sn": 120801655522969****,
				"type": "add"
			}
		}
	}
}

该接口需要支持幂等,即同一个 cardNo 卡新增成功后,还支持再次新增,再次新增时返回成功的结果。门禁须限制用户单个门禁卡,同个用户新增第二个门禁卡时,结果应返回失败。

更新门禁卡

下发参数如下:

请求参数 参数类型 描述 是否必传
uid String 涂鸦用户 ID
cardNo String 门禁卡号
oldCardNo String 被修改门禁卡号

参考 IPC SDK 开发手册的 DP 点数据读取,下发的 DP 点:128。DP 点数据格式示例:

{
	"t": 1573557869,
	"data": {
		"cid": "12345678",
		"dps": {
			"128": {
				"data": {
					"cardNo": "cardNo",
					"oldCardNo": "oldCardNo",
					"uid": "uid9****"
				},
				"mode": "dn_cardInfo",
				"sn": 120801783846441****,
				"type": "update"
			}
		}
	}
}

用户限制单个门禁卡,更新门禁卡先清空该用户原先门禁卡,再新增。

移除门禁卡

下发参数如下:

请求参数 参数类型 描述 是否必传
uid String 涂鸦用户 ID
cardNo String 被删除门禁卡号

参考 IPC SDK 开发手册的 DP 点数据读取,下发的 DP 点:128。DP 点数据格式示例:

{
	"t": 1573557869,
	"data": {
		"cid": "12345678",
		"dps": {
			"128": {
				"data": {
					"cardNo": "uid9****",
					"uid": "uid9****"
				},
				"mode": "dn_cardInfo",
				"sn": 120801875624104****,
				"type": "del"
			}
		}
	}
}

门禁卡启/禁用

下发参数如下:

请求参数 参数类型 描述 是否必传
uid String 涂鸦用户 ID
enable Boolean 启用/禁用
  • 0:禁用
  • 1:启用

参考 IPC SDK 开发手册的 DP 点数据读取,下发的 DP 点:128。DP 点数据格式示例:

{
	"t": 1573557869,
	"data": {
		"cid": "12345678",
		"dps": {
			"128": {
				"data": {
					"uid": "uid9****",
          "enable":true
				},
				"mode": "dn_cardInfo",
				"sn": 120801875624104****,
				"type": "enable"
			}
		}
	}
}

新增二维码

下发参数如下:

请求参数 参数类型 描述 是否必传
uid String 涂鸦用户 ID
qrCode String 二维码 Code

参考 IPC SDK 开发手册的 DP 点数据读取,下发的 DP 点:128。DP 点数据格式示例:

{
	"t": 1573557869,
	"data": {
		"cid": "12345678",
		"dps": {
			"128": {
				"data": {
					"qrcode": "qrCode",
					"uid": "uid9****"
				},
				"mode": "dc_qrCodeInfo",
				"sn": 120802035040372****,
				"type": "add"
			}
		}
	}
}

该接口需要支持幂等,即同一个 qrCode 新增成功后,还支持再次新增,再次新增时返回成功。门禁须限制用户单个二维码,同个用户新增第二个二维码时,结果应返回失败。

更新二维码

下发参数如下:

请求参数 参数类型 描述 是否必传
uid String 涂鸦用户 ID
qrCode String 二维码 Code

参考 IPC SDK 开发手册的 DP 点数据读取,下发的 DP 点:128。DP 点数据格式示例:

{
	"t": 1573557869,
	"data": {
		"cid": "12345678",
		"dps": {
			"128": {
				"data": {
					"qrcode": "qrCode",
					"uid": "uid9****"
				},
				"mode": "dc_qrCodeInfo",
				"sn": 120802035040372****,
				"type": "update"
			}
		}
	}
}

用户限制单个二维码,更新二维码先清空该用户原先二维码,再新增。

移除二维码

下发参数如下:

请求参数 参数类型 描述 是否必传
uid String 涂鸦用户 ID

参考 IPC SDK 开发手册的 DP 点数据读取,下发的 DP 点:128。DP 点数据格式示例:

{
	"t": 1573557869,
	"data": {
		"cid": "12345678",
		"dps": {
			"128": {
				"data": {
					"uid": "uid9****"
				},
				"mode": "dc_qrCodeInfo",
				"sn": 120802035040372****,
				"type": "del"
			}
		}
	}
}

远程开门

下发参数如下:

请求参数 参数类型 描述 是否必传
uid String 涂鸦用户 ID

参考 IPC SDK 开发手册的 DP 点数据读取,下发的 DP 点:127。DP 点数据格式示例:

{
    "t": 1573557869,
    "data": {
        "cid": "12345678",
        "dps": {
            "127": {
                "sn": 8839438****,
                "uid": "uid9****"
            }
        }
    }
}

远程开⻔不需要校验用户权限,即设备只要接收到 DP 点,就会进行开门操作,而不会校验人员信息。

门禁启用/禁用

下发参数如下:

请求参数 参数类型 描述 是否必传
sn String 请求唯一标识
bizType String 业务类型,默认值:maintain
operateType String 操作类型,默认值:enable_disable_device
extendData String JSON 字符串,如: {“value” :1},value,1: 启用,0: 禁⽤

参考 IPC SDK 开发手册的 DP 点数据读取,下发的 DP 点:186;DP 点数据格式示例:

{
    "t": 1573557869,
    "data": {
        "cid": "12345678",
        "dps": {
            "186": {
                "sn": 140550692870396****,
                "bizType": "maintain",
                "operateType": "enable_disable_device",
                "extendData": "{\"value\":1}"
            }
        }
    }
}
  • 若禁用设备功能,则刷卡、密码、二维码、人脸、指纹、远程开门等功能不可用。

  • 若启用设备功能,则刷卡、密码、二维码、人脸、指纹、远程开门等功能可用。

新增门禁通行密码

下发参数如下:

请求参数 参数类型 描述 是否必传
uid String 涂鸦用户 ID
passPwd String 门禁通行密码

参考 IPC SDK 开发手册的 DP 点数据读取,下发的 DP 点:128。DP 点数据格式示例:

{
	"t": 1573557869,
	"data": {
		"cid": "12345678",
		"dps": {
			"128": {
				"data": {
					"passPwd": "passPwd",
					"uid": "uid9****"
				},
				"mode": "dc_passPwd",
				"sn": 120802035040372****,
				"type": "add"
			}
		}
	}
}

用户支持多个通行密码,不同用户间通行密码允许相同。
该接口需要支持幂等,当同一个用户重复新增同一个通行密码时返回成功。

更新门禁通行密码

下发参数如下:

请求参数 参数类型 描述 是否必传
uid String 涂鸦用户 ID
passPwd String 门禁通行密码
oldPassPwd String 被修改的原门禁通行密码

参考 IPC SDK 开发手册的 DP 点数据读取,下发的 DP 点:128。DP 点数据格式示例:

{
	"t": 1573557869,
	"data": {
		"cid": "12345678",
		"dps": {
			"128": {
				"data": {
					"passPwd": "passPwd",
          "oldPassPwd": "oldPassPwd",
					"uid": "uid9****"
				},
				"mode": "dc_passPwd",
				"sn": 120802035040372****,
				"type": "update"
			}
		}
	}
}

修改用户通行密码,如果用户下不存在被修改的通行密码(oldPassPwd),需返回失败。

移除门禁通行密码

下发参数如下:

请求参数 参数类型 描述 是否必传
uid String 涂鸦用户 ID
passPwd String 被删除的门禁通行密码

参考 IPC SDK 开发手册的 DP 点数据读取,下发的 DP 点:128。DP 点数据格式示例:

{
	"t": 1573557869,
	"data": {
		"cid": "12345678",
		"dps": {
			"128": {
				"data": {
          "passPwd": "passPwd",
					"uid": "uid9****"
				},
				"mode": "dc_passPwd",
				"sn": 120802035040372****,
				"type": "del"
			}
		}
	}
}

移除用户通行密码,如果用户下不存在被删除的通行密码(passPwd),需返回失败。

其它

md5 算法

 标准 md5 算法参见: https://github.com/JieweiWei/md5

我们的 md5 算法在标准基础上做了封装,参见 md5Hex 方法。
#include <iostream>
#include "./md5.h"

using std::cout;
using std::endl;

string md5Hex(string data, bool isMd5){
	string hex = "";
	if (data != "") { //data != null
		if (!isMd5) {
			hex = MD5(data).toStr();
		}

		string midden = hex.substr(8, 16);
		string temp = midden.substr(0, 8);
		temp = temp + hex.substr(0, 8);
		temp = temp + hex.substr(24, 8);
		temp = temp + midden.substr(8, 8);
		hex = temp;
	}
	return hex;
}

int main()
{
	string data = "{\"t\":1598010014,\"key\":\"a354fffa81674d80b7d1e0696ad69bf3\",\"cid\":\"4rfvd80basdue7d1e0\"}";
	cout<< md5Hex(data, false) <<endl;
	return 0;
}

二维码识别功能

设备端需要支持两种二维码格式:

1.0 版本的二维码格式:

7421371370865597349888943036

2.0 版本的二维码格式:

{
  "qrcode": "YXkxNjExMDU4MTc4MTM4N240Mjd8ZTRhZjIzZTY=" //value:Base64 编码后字符串
}

设备扫描二维码后的校验步骤:

  1. 根据二维码的内容判断二维码是 1.0 版本还是 2.0 版本;

  2. 如果是 1.0 版本,直接和人员的二维码进行比较,比较成功则允许通行;如果是 2.0 版本,则先通过 Base64 方法对内容进行解码,解析出二维码信息;

  3. 首先进行静态二维码对比校验,二维码信息和人员的二维码进行比较,比较成功则允许通行;

  4. 如果比对失败则通过动态二维码验证接口进行验证,验证成功则允许通行,验证失败则不允许通行。

动态二维码验证接口:

totp_util.c verifyTOTPFlexibility 方法校验动态二维码。

totp_util.h

#ifndef __TOTP_UTIL_H__
#define __TOTP_UTIL_H__

#ifdef __cplusplus
	extern "C" {
#endif

//返回的 char*需要手动释放
char* generateTOTP(char* uid, char* secretKey,LONG_T refreshTime) ;

BOOL_T  verifyTOTPFlexibility(char* uid, char* secretKey, char* willVerifyTotp, LONG_T refreshTime);


#ifdef __cplusplus
} // extern "C"
#endif

#endif

totp_util.c

#include "tuya_sha256.h"
#include "uni_time.h"
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <ctype.h>
#include "totp_util.h"
static  long INITIAL_TIME = 0;
#ifndef MAX
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
#endif
static void insert_zero(char* input);
static void hex_to_string(IN BYTE_T *input, IN INT_T input_len, OUT BYTE_T *output, INOUT INT_T* p_out_len);
static UINT64_T timeFactor(UINT64_T targetTime, UINT64_T refreshTime) ;
static char* doGenerateTOTP(char* key, char* time);
static unsigned char* hexStr2Bytes(char* src);
static void toUpperCase(char* input);

static void toUpperCase(char* input)
{
    for(int i = 0 ;i<strlen(input);i++)
    {   
        input[i]=toupper(input[i]);
    }
}
//首部填充 0
static void insert_zero(char* input)
{
    int size = strlen(input);
    char* tmp = (char*)malloc(size+1) ;
    strcpy(tmp,input);
    int i=0;
    for(;i <size;i++){
        input[i+1]=tmp[i];
    }
    input[0]='0';
    free(tmp);
}


static void hex_to_string(IN BYTE_T *input, IN INT_T input_len, OUT BYTE_T *output, INOUT INT_T* p_out_len)
{
    INT_T i;
    INT_T offset = 0;
    
    if(NULL == input || NULL == output || NULL == p_out_len)
    {
        printf("invalid argument\r\n");
        return;
    }

    if(*p_out_len < input_len * 2 + 1)
    {
        printf("OUTPUT LEN IS SHORT THAN INPUT LEN * 2\r\n");
        *p_out_len = 0;
        return;
    }

    for(i = 0;i < input_len;i++) 
    {
        sprintf(&output[offset],"%02x",input[i]);
        offset += 2;
    }
    output[offset] = 0;

    *p_out_len = offset;
}

static char* longtoHexString(unsigned long input)
{
    printf("long input=%lu \n",input);
    char* str = (char*)malloc(10);
    int i = 0; 
    unsigned long temp = 0; 
    while(input > 0 || i==0){
            temp = input % 16;// 取余数 
            str[i++] = temp >= 10 ? ((temp - 10) + 'a') : (temp + '0');
            input = input >> 4; 
    }
    str[i] = '\0';
    // 翻转 , 将高位与低位进行翻转 
    int t = 0; 
    for(int j = 0; j < i / 2; j++){
            t = str[j];
            str[j] = str[i - j - 1];
            str[i - j - 1] = t;
    } 
    printf("longtoHexString ouput=%lu \n",str);
    return str; 
}


static unsigned char* hexStr2Bytes(char* src)
{
    unsigned char* tmp = (char*)malloc(strlen(src)+1);
    memset(tmp,0,sizeof(tmp));
    strcpy(tmp,src);
    //保证 tmp 为偶数
    if(strlen(tmp)%2 != 0){
       insert_zero(tmp);
    }
    
    int m=0;
    int len = strlen(tmp)/2;
    char* end ;
    unsigned char* dst = (char*)malloc(len+1);
    memset(dst,0,len+1);
    for(int i =0;i<len;i++)
    {
    	m = i*2;
        char subs[3]={0};
        memcpy(subs,tmp+m,2);
        dst[i]=strtol(subs,NULL,16);
    }
    free(tmp);
}

/**
 * 获取动态因子
 *
 * @param targetTime  指定时间
 * @param refreshTime 二维码刷新时间
 * @return long
 */
static UINT64_T timeFactor(UINT64_T targetTime, UINT64_T refreshTime) 
{
    return (targetTime - INITIAL_TIME) / refreshTime;
}

/**
 * @param key  用户 ID+种子密钥
 * @param time 时间因子
 * @return
 */
static char* doGenerateTOTP(char* key, char* time)
{
    // 1、如果 time 小于 16 个字符,则用零补齐,例如:30B649C -> 00000000030B649C
    int time_len = MAX(17,strlen(time));
    char* fill_time=(char*)malloc(time_len);
    memset(fill_time,0,time_len);
    strcpy(fill_time,time);
    while (strlen(fill_time) < 16) {
        insert_zero(fill_time);
    }
    
    unsigned char* input = hexStr2Bytes(fill_time);
    // 哈希加密
    BYTE_T hash[32+1] = {0};
    sha2_hmac(key,strlen(key),input,strlen(input),hash,0);
    
    // 返回指定位数的动态口令
    INT_T result_len = 65;
    char* result = (char*)malloc(result_len);
    memset(result,0,result_len);
	hex_to_string(hash, strlen(hash), result, &result_len);

    //如果返回值小于指定的位数,则用 0 补齐
    while (strlen(result) < 8) {
        insert_zero(result);
    }
    printf("sha2_hmac result:%s\n", result);
    free(fill_time);
    fill_time=NULL;
    free(input);
    input=NULL;
    return result;
}

/**
 * 刚性口令验证, 3 个周期内都能验证通过
 *
 * @param uid            用户 ID
 * @param secretKey      秘钥,32 位字符串,建议 UUID 生成
 * @param willVerifyTotp 待验证的二维码信息
 * @param refreshTime    二维码刷新时间
 * @return boolean
 */
BOOL_T  verifyTOTPFlexibility(char* uid, char* secretKey, char* willVerifyTotp, LONG_T refreshTime)
{
    if (!uid || !secretKey|| !willVerifyTotp ) {
        printf("param err \r\n");
        return false;
    }
    
//获取当前时间 ms 级别
    UINT64_T now = uni_time_get_posix_ms();

//uuid 和 secretKey 构造一个变量
    char* uid_secretKey = (char*)malloc(strlen(uid)+strlen(secretKey)+1);
    memset(uid_secretKey,0,sizeof(uid_secretKey));
    strcpy(uid_secretKey,uid);
    strcat(uid_secretKey,secretKey);
    char* time1 = longtoHexString(timeFactor(now, refreshTime));
    toUpperCase(time1);

    //TOTP 加密 周期一
    char* tempTotp1 = doGenerateTOTP(uid_secretKey, time1);
    //相对返回 TRUE
    if (strcmp(tempTotp1,willVerifyTotp)==0) {
        free(tempTotp1);
        free(uid_secretKey);
        free(time1);
        return TRUE;
    }
    free(tempTotp1);
    

//TOTP 加密 周期二

    char* time2 = longtoHexString(timeFactor(now - refreshTime, refreshTime));
    toUpperCase(time2);
    //uuid 和 secretKey 构造一个变量
    memset(uid_secretKey,0,sizeof(uid_secretKey));
    strcpy(uid_secretKey,uid);
    strcat(uid_secretKey,secretKey);

    char* tempTotp2 = doGenerateTOTP(uid_secretKey, time2);
    if (strcmp(tempTotp2,willVerifyTotp)==0) {
        free(tempTotp2);
        free(uid_secretKey);
        free(time2);
        return true;
    }
    free(tempTotp2);

 //TOTP 加密 周期三
    char* time3 = longtoHexString(timeFactor(now + refreshTime, refreshTime));
    toUpperCase(time3);
    memset(uid_secretKey,0,sizeof(uid_secretKey));
    strcpy(uid_secretKey,uid);
    strcat(uid_secretKey,secretKey);
    
    char* tempTotp3 = doGenerateTOTP(uid_secretKey, time3);
    if (strcmp(tempTotp3,willVerifyTotp)==0) {
        free(tempTotp3);
        free(uid_secretKey);
        free(time3);
        return true;
    }
    free(tempTotp3);
    free(uid_secretKey);
    free(time3);
    return false;
}

/**
  * 生成动态码
  *
  * @param uid         用户 ID
  * @param secretKey   动态种子秘钥,32 位字符串,建议 UUID 生成
  * @param refreshTime 二维码刷新时间
  * @return
  */
 char* generateTOTP(char* uid, char* secretKey,LONG_T refreshTime) 
 {
      if (!uid || !secretKey || refreshTime==0) {
        printf("param err \r\n");
        return false;
    }
     // 获取当前时间的时间戳
     UINT64_T now = uni_time_get_posix_ms();
     // 将动态因子转为 16 进制
     printf("cur time =%llu,",now);
     printf("timeFactor%lu,",timeFactor(now, refreshTime));
     char* time = longtoHexString(timeFactor(now, refreshTime));
     toUpperCase(time);
     char* uid_secretKey = (char*)malloc(strlen(uid)+strlen(secretKey)+1);
     strcpy(uid_secretKey,uid);
     strcat(uid_secretKey,secretKey);
     char* totp = doGenerateTOTP(uid_secretKey, time);
     free(uid_secretKey);
     free(time);
     return totp;
 }

2.0 版二维码示例

二维码内容
{
  "qrcode": "YXkxNjExMDU4MTc4MTM4N240Mjd8ZTRhZjIzZTY=" //value:Base64 编码后字符串
}

YXkxNjExMDU4MTc4MTM4N240Mjd8ZTRhZjIzZTY= Base64 解码后获得字符串
"ay16110581781387****|e4af23e6"

用户 ID: ay16110581781387****
二维码信息: e4af23e6

首先根据用户 ID 获取静态二维码进行对比。
如果对比失败,则进行动态二维码校验。
二维码刷新时间: 5 * 60 * 1000
根据用户 ID 查询到用户的秘钥 secretKey
然后调用动态二维码验证接口 TOTPUtils.verifyTOTPFlexibility
返回 true,校验通过。

门禁卡识别功能

门禁设备需支持读取门禁卡功能;人员卡号数据在门禁卡第 7 扇区,门禁从门禁卡第 7 扇区读取门禁卡后,与设备内人员门禁卡对比,若对比成功,则门禁刷卡成功。