RESTful API
API の紹介
Sesame APP は、RESTful API と Iot 物連技術を利用して、マルチプラットフォームとマルチデバイス間に相互の連携とコミュニケーションを実現します。
API インターフェースのドキュメンテーション
1、デバイスのシャドウの取得【CHIoTManager】
- メソッド名:getCHDeviceShadow
- パラメータ:deviceId
- ネットワークリクエスト get, /device/v1/sesame2
c
func getCHDeviceShadow(_ sesameLock: CHSesameLock, onResponse: (CHResult<CHDeviceShadow>)? = nil) {
func getShadow() {
CHAccountManager.shared.API(request: .init(.get, "/device/v1/sesame2/\(sesameLock.deviceId.uuidString)")) { apiResult in
switch apiResult {
case .success(let data):
L.d("⌚️ API getShadow ok",data)
if let shadow = CHDeviceShadow.fromRESTFulData(data!) {
onResponse?(.success(.init(input: shadow)))
}
case .failure(let error):
L.d("⌚️ API error",error )
onResponse?(.failure(error))
}
}
}
getShadow()
}2、Wifi Moudle2 にメッセージを送信する
メソッド名:sendCommandToWM2 パラメータ:cmd history sign ネットワークリクエスト post, /device/v1/iot/sesame2/deviceId
c
let parameter = [
"cmd": command.rawValue,
"history": keyData.historyTag!.base64EncodedString(),
"sign": keyCheck[0...3].toHexString()
] as [String : Any]
func sendCommandToWM2(_ command: SesameItemCode, _ device: CHDevice, onResponse: @escaping (CHResult<CHEmpty>)) {
guard let keyData = device.getKey() else {
return
}
if keyData.historyTag == nil {
keyData.historyTag = Data()
}
var data = command.rawValue.data
data += device.deviceId.uuidString.data(using: .utf8)!
data += Data.createOS2Histag(keyData.historyTag)
var timestamp: UInt32 = UInt32(Date().timeIntervalSince1970)
let timestampData = Data(bytes: ×tamp,
count: MemoryLayout.size(ofValue: timestamp))
let randomTag = Data(timestampData.arrayOfBytes()[1...3])
let keyCheck = CC.CMAC.AESCMAC(randomTag,
key: keyData.secretKey.hexStringtoData())
data = keyCheck[0...3] + data
let parameter = [
"cmd": command.rawValue,
"history": keyData.historyTag!.base64EncodedString(),
"sign": keyCheck[0...3].toHexString()
] as [String : Any]
CHAccountManager.shared.API(request: .init(.post, "/device/v1/iot/sesame2/\(device.deviceId.uuidString)",
parameter)) { apiResult in
if case let .failure(error) = apiResult {
onResponse(.failure(error))
} else {
onResponse(.success(.init(input: .init())))
}
}
}3、自転車ロックの登録【CHSesameBikeDevice+Register】
- メソッド名:onRegisterStage1
- パラメータ:[ak:publicKey,sessionToken,e:er,t:bikelock.rawValue]
- ネットワークリクエスト post, /device/v1/sesame2/deviceId
c
[
"s1": [
"ak": (Data(appKeyPair.publicKey).base64EncodedString()),
"n": bikeLockSessionToken!.base64EncodedString(),
"e": er,
"t": CHProductModel.bikeLock.rawValue
]
]c
func onRegisterStage1(er: String, result: @escaping CHResult<CHEmpty>) {
L.d("[bike][register]onRegisterStage1")
let request = CHAPICallObject(.post, "/device/v1/sesame2/\(self.deviceId.uuidString)", [
"s1": [
"ak": (Data(appKeyPair.publicKey).base64EncodedString()),
"n": bikeLockSessionToken!.base64EncodedString(),
"e": er,
"t": CHProductModel.bikeLock.rawValue
]
]
)
CHAccountManager
.shared
.API(request: request) { response in
switch response {
case .success(let data):
L.d("[bike][request]success")
guard let data = data else {
result(.failure(NSError.noContent))
return
}
// todo kill this parcer with json decoder
if let dict = try? data.decodeJsonDictionary() as? [String: String],
// let b64PayloadTime = dict["r"],
// let timePayload = Data(base64Encoded: b64PayloadTime) ,
let b64Sig1 = dict["sig1"],
let b64ServerToken = dict["st"],
let b64SesamePublicKey = dict["pubkey"],
let sig1 = Data(base64Encoded: b64Sig1),
let serverToken = Data(base64Encoded: b64ServerToken),
let sesamePublicKey = Data(base64Encoded: b64SesamePublicKey) {
self.deviceStatus = .registering()
self.onRegisterStage2(
appKey: self.appKeyPair,
sig1: sig1,
serverToken: serverToken,
sesame2PublicKey: sesamePublicKey,
result: result
)
} else {
result(.failure(NSError.parseError))
self.deviceStatus = .readyToRegister()
}
case .failure(let error):
result(.failure(error))
}
}
}4、履歴のアップロード【CHSesameDevice+History】
- メソッド名:postProcessHistory
- パラメータ:[ s:deviceId, v:historyData ]
- ネットワークリクエスト post, /device/v1/sesame2/historys
c
func postProcessHistory(_ historyData: Data) {
let request: CHAPICallObject = .init(.post, "/device/v1/sesame2/historys", [
"s": self.deviceId.uuidString,
"v": historyData.toHexString()
])
CHAccountManager
.shared
.API(request: request) { result in
switch result {
case .success(_): break
case .failure(let error):
L.d("履歴のアップロードに失敗 : \(error)")
}
}
}5、SesameBot の登録【CHSesameBotDevice+Register】
- メソッド名:onRegisterStage1
- パラメータ:[ak:publicKey,sessionToken,e:er,t:bikelock.rawValue]
- ネットワークリクエスト post, /device/v1/sesame2/deviceId
c
func onRegisterStage1(er: String, result: @escaping CHResult<CHEmpty>) {
let request = CHAPICallObject(.post, "/device/v1/sesame2/\(self.deviceId.uuidString)", [
"s1": [
"ak": (Data(appKeyPair.publicKey).base64EncodedString()),
"n": self.sesameBotSessionToken!.base64EncodedString(),
"e": er,
"t": CHProductModel.sesameBot.rawValue
]
]
)
CHAccountManager
.shared
.API(request: request) { response in
switch response {
case .success(let data):
guard let data = data else {
result(.failure(NSError.noContent))
return
}
// todo kill this parcer with json decoder
if let dict = try? data.decodeJsonDictionary() as? [String: String],
let b64Sig1 = dict["sig1"],
let b64ServerToken = dict["st"],
let b64SesamePublicKey = dict["pubkey"],
let sig1 = Data(base64Encoded: b64Sig1),
let serverToken = Data(base64Encoded: b64ServerToken),
let sesamePublicKey = Data(base64Encoded: b64SesamePublicKey) {
self.deviceStatus = .registering()
self.onRegisterStage2(
appKey: self.appKeyPair,
sig1: sig1,
serverToken: serverToken,
sesame2PublicKey: sesamePublicKey,
result: result
)
} else {
result(.failure(NSError.parseError))
self.deviceStatus = .readyToRegister()
}
case .failure(let error):
result(.failure(error))
}
}
}6、Sesame2Device を登録する【CHSesame2Device+Register】
- メソッド名:onRegisterStage1
- パラメタ:[ak:publicKey,sessionToken,e:er,t:bikelock.rawValue]
- ネットワークリクエスト post, /device/v1/sesame2/deviceId
c
let request = CHAPICallObject(.post, "/device/v1/sesame2/\(self.deviceId.uuidString)", [
"s1": [
"ak": (Data(appKeyPair.publicKey).base64EncodedString()),
"n": self.sesameBotSessionToken!.base64EncodedString(),
"e": er,
"t": CHProductModel.sesameBot.rawValue
]
]
)7、非同期時間の取得【CHSesame2Device+Login】
- メソッド名:requestSyncTime
- パラメタ:[st:sessionToken]
- ネットワークリクエスト post, device/v1/sesame2/deviceId/time
c
let reqBody: NSDictionary = [
"st": sessionToken.base64EncodedString()
]8、履歴のアップロード【CHSesame2Device+History】
- メソッド名:postProcessHistory
- パラメタ:[s:deviceId, v:historyData]
- ネットワークリクエスト post, /device/v1/sesame2/historys
c
let request: CHAPICallObject = .init(.post, "/device/v1/sesame2/historys", [
"s": self.deviceId.uuidString,
"v": historyData.toHexString()
])9、Sesame Touch Pro の登録【CHSesameTouchPro+Register】
- メソッド名:register
- パラメタ:[t:productType, pk:sesameToken]
- ネットワークリクエスト post, /device/v1/sesame5/deviceId
c
let request = CHAPICallObject(.post, "/device/v1/sesame5/\(self.deviceId.uuidString)", [
"t":advertisement!.productType!.rawValue,
"pk":self.mSesameToken!.toHexString()
] as [String : Any])10、Bike 2 の登録【CHSesameBike2Device+Register】
- メソッド名:register
- パラメタ:[t:productType, pk:sesameToken]
- ネットワークリクエスト post, /device/v1/sesame5/deviceId
c
let request = CHAPICallObject(.post, "/device/v1/sesame5/\(self.deviceId.uuidString)", [
"t":advertisement!.productType!.rawValue,
"pk":self.mSesameToken!.toHexString()
])11、Sesame 5 の登録【CHSesame5Device+Register】
- メソッド名:register
- パラメタ:[t:productType, pk:sesameToken]
- ネットワークリクエスト post, /device/v1/sesame5/deviceId
c
let request = CHAPICallObject(.post, "/device/v1/sesame5/\(self.deviceId.uuidString)", [
"t":advertisement!.productType!.rawValue,
"pk":self.mSesameToken!.toHexString()
])12、履歴のアップロード【CHSesame5Device+History】
- メソッド名:postProcessHistory
- パラメタ:[s:deviceId, v:historyData,t:5]
- ネットワークリクエスト post, /device/v1/sesame2/historys
c
let request: CHAPICallObject = .init(.post, "/device/v1/sesame2/historys", [
"s": self.deviceId.uuidString,
"v": historyData.toHexString(),
"t":"5",
])
CHAccountManager
.shared
.API(request: request) { result in
switch result {
case .success(_):
L.d("[ss5][history]Bluetooth", "履歴のアップロードに成功しました")
break
case .failure(let error):
L.d("[ss5][history]Bluetooth", "履歴のアップロードに失敗しました。サーバーが落ちました: \(error)")
break
}
}
}13、ゲストキーの作成 createGuestKey【CHDevice】
- メソッド名:createGuestKey
- パラメータ:[keyName:name]
- ネットワークリクエスト post, /device/v1/sesame2/deviceId/guestkey
c
func createGuestKey(result: @escaping CHResult<String>) {
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let deviceKey = getKey() //CHDeviceKeyを返します
let jsonData = try! encoder.encode(deviceKey)
var data = try! JSONSerialization.jsonObject(with: jsonData, options: []) as! [String: Any]
let date = Date()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM/dd HH:mm"
dateFormatter.locale = Locale(identifier: "ja_JP")
data["keyName"] = dateFormatter.string(from: date)
CHAccountManager.shared.API(request: .init(.post, "/device/v1/sesame2/\(deviceId.uuidString)/guestkey", data)) { postResult in
switch postResult {
case .success(let data):
let decoder = JSONDecoder()
let guestKey = try! decoder.decode(String.self, from: data!)
result(.success(.init(input: guestKey)))
case .failure(let error):
result(.failure(error))
}
}
}14、IoT のトピックを購読する前に認証が必要【CHDevice】
- メソッド名:iotCustomVerification
- パラメータ:[a:getTimeSignature]
- ネットワークリクエスト get, /device/v1/iot/sesame2/deviceId
c
func iotCustomVerification(result: @escaping CHResult<CHEmpty>) {
CHAccountManager.shared.API(request: .init(.get, "/device/v1/iot/sesame2/\(deviceId.uuidString)", queryParameters: ["a": getTimeSignature()])) { verifyResult in
switch verifyResult {
case .success(_):
result(.success(.init(input: .init())))
case .failure(let error):
result(.failure(error))
}
}
}15、ゲストのキーを取得する【CHDevice】
- メソッド名:getGuestKeys
- パラメータ:[a:getTimeSignature]
- ネットワークリクエスト get, /device/v1/sesame2/deviceId/guestkeys
c
func getGuestKeys(result: @escaping CHResult<[CHGuestKey]>) {
CHAccountManager.shared.API(request: .init(.get, "/device/v1/sesame2/\(deviceId.uuidString)/guestkeys")) { getResult in
switch getResult {
case .success(let data):
let guestKeys = try! JSONDecoder().decode([CHGuestKey].self, from: data!)
result(.success(.init(input: guestKeys)))
case .failure(let error):
result(.failure(error))
}
}
}16、ゲストのキーを更新する【CHDevice】
- メソッド名:updateGuestKey
- パラメータ:["guestKeyId": guestKeyId, "keyName": name]
- ネットワークリクエスト get, /device/v1/sesame2/deviceId/guestkeys
17、ゲストのキーを削除する【CHDevice】
- メソッド名:removeGuestKey
- パラメータ:[ "guestKeyId": guestKeyId, "randomTag": keyCheck[0...3]]
- ネットワークリクエスト delete, /device/v1/sesame2/deviceId/guestkeys
c
CHAccountManager.shared.API(request: .init(.delete, "/device/v1/sesame2/\(deviceId.uuidString)/guestkey", [ "guestKeyId": guestKeyId, "randomTag": keyCheck[0...3].toHexString()])) { deleteResult in
switch deleteResult {
case .success(_):
result(.success(.init(input: .init())))
case .failure(let error):
result(.failure(error))
}
}18、通知を有効にする【CHDevice】
- メソッド名:enableNotification
- パラメータ:["token": token,"deviceId":deviceId,"name": name]
- ネットワークリクエスト get, /device/v1/token
c
func enableNotification(token: String, name: String, result: @escaping CHResult<CHEmpty>) {
CHAccountManager.shared.API(request: .init(.post, "/device/v1/token",
["token": token,
"deviceId":deviceId.uuidString,
"name": name])) { createResult in
if case let .failure(error) = createResult {
result(.failure(error))
} else {
result(.success(.init(input: .init())))
}
}
}19、通知が有効かどうかを確認する【CHDevice】
- メソッド名:isNotificationEnabled
- パラメータ:["deviceId": deviceId, "deviceToken": token, "name": name]
- ネットワークリクエスト get, /device/v1/token
20、ゲストキーは signedToken を呼び出す【CHDevice】
// session token を取得し、server にアップロードする。その後、secretKey で署名を行い、login token を得る
- メソッド名:signedToken
- パラメータ:["deviceId": deviceId., "token": token, "secretKey": keyData.secretKey]
- ネットワークリクエスト post, /device/v1/sesame2/sign/
c
func sign(token: String, result: @escaping CHResult<String>) {
guard let keyData = getKey() else {
return
}
L.d("API:/device/v1/sesame2/sign",token, deviceId.uuidString,keyData.secretKey)
CHAccountManager.shared.API(request: .init(.post, "/device/v1/sesame2/sign", ["deviceId": deviceId.uuidString, "token": token, "secretKey": keyData.secretKey])) { serverResult in
switch serverResult {
case .success(let data):
let signedToken = String(data: data!, encoding: .utf8)!
// L.d("sign ok:",signedToken)
result(.success(.init(input: signedToken)))
case .failure(let error):
L.d("sign error!")
result(.failure((error)))
}
}
}21、通知を無効にする【CHDeviceManager】
- メソッド名:disableNotification
- パラメータ:["token": token, "deviceId": deviceId, "name": name]
- ネットワークリクエスト delete, /device/v1/token/
c
public func disableNotification(deviceId: String, token: String, name: String, result: @escaping CHResult<CHEmpty>) {
CHAccountManager.shared.API(request: .init(.delete, "/device/v1/token", ["token": token, "deviceId": deviceId, "name": name])) { deleteResult in
switch deleteResult {
case .success(_):
result(.success(.init(input: .init())))
case .failure(let error):
result(.failure(error))
}
}
}