2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

AndroidとSesameとNFCで開けゴマ WEBAPI版

Last updated at Posted at 2020-05-10

修正(2020/05/12):CradIDの型を指定、カードIDの等価式を修正

#目的
家の鍵を「かざ」すだけでして開けたい!

・Sesame
・Sesame Wifiアクセスポイント
https://jp.candyhouse.co

・NFCカード + 電波遮断シート
 玄関のドアは金属性なので、台紙にしないと反応しないので
 (100均に電波遮断シートが売ってることにびっくりした、、、
・Androidスマホ

##方法
1.AndroidでNFCを読み込み→ネット→公開APIで解錠(本記事)
WEBAPI.png
2.AndroidでNFCを読み込み→BLEで解錠(別記事)
BLE.png

##成果物
事前準備として、
NXP TagWriter
https://play.google.com/store/apps/details?id=com.nxp.nfc.tagwriter&hl=ja
にて、書込み可能なタグに対して
 「Plain Text」で適当な文字列
を書き込んで置いてください。

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapplication">

    <!-- APP起動に必要な権限 -->
    <uses-permission android:name="android.permission.NFC" />
    <uses-permission android:name="android.permission.INTERNET" />
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity-alias
            android:name=".MainActivityAlias"
            android:enabled="true"
            android:targetActivity=".MainActivity">

            <!-- NFC card からの起動 -->
            <intent-filter>
                <action android:name="android.nfc.action.NDEF_DISCOVERED" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="text/plain" />
            </intent-filter>
        </activity-alias>
    </application>
</manifest>

kotlinx.coroutinesとimport com.github.kittinunf.fuelを使うので、
buid.gradle(app)に以下を追加

dependencies {
・・・・
   implementation 'com.github.kittinunf.fuel:fuel:X.X.X'
   implementation "com.github.kittinunf.fuel:fuel-json:X.X.X"
   implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:X.X.X'
}

MainActivity.kt

package com.example.myapplication

//AndroidでUIを使うためのおまじない
import android.content.pm.PackageManager
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
//NFCを使うためのライブラリ
import android.nfc.NfcAdapter
//HTTP(S)をメインスレッドで呼び出さないためのライブラリ
import kotlinx.coroutines.*
//WebAPIを実行するためのライブラリ
import com.github.kittinunf.fuel.httpGet
import com.github.kittinunf.fuel.httpPost
import com.github.kittinunf.fuel.json.responseJson
import com.github.kittinunf.result.Result

class MainActivity : AppCompatActivity() {
    //鍵にしたいカードのIDを情報
    val CardID : String = -鍵にしたいCardID-
    //操作したいSesameのAPI情報
    val AuthCode = -APIの鍵-
    val DeviceID = -APIに使うDEVICEID-

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        //このアプリを開いた[起因]のNFC情報を読み取る
        //[起因]は、AndroidManifest.xmlに規定した<intent-filter>に起因する
        if(NfcAdapter.ACTION_NDEF_DISCOVERED == intent.action) {
            //カードのID情報を取得
            val tagId : ByteArray = intent.getByteArrayExtra(NfcAdapter.EXTRA_ID)
            val list = ArrayList<String>()
            for(byte in tagId) {
                list.add(String.format("%02X", byte.toInt() and 0xFF))
            }
            //読み込んだカードIDが一致すれば
            if(list.joinToString(separator = ":") == CardID)){

                //Coroutineで別スレッドにてHTTP(S)処理
                GlobalScope.launch{
                    Change_Status()
                }
            }
        }
    }

    /**
     * Sesame Web API関係
     */
    fun Change_Status(){
        //SesameのAPIに従って、HTTP(S)のheaderにApiキーを設定
        val header1: HashMap<String, String> = hashMapOf("Authorization" to AuthCode)
        //一回目の通信で、操作したSesameの状態を取得し記録
        var Locked : Boolean = false
        var Responsive : Boolean = false

        //一回目の通信:Sesameの状態を取得(同期通信)
        val (_, _, result1) = ("https://api.candyhouse.co/public/sesame/"+DeviceID).httpGet().header(header1).responseJson()
        when (result1){
            //通信失敗時:
            is Result.Failure -> {
            }
            //通信成功時:結果としてJsonが返ってくるので
            is Result.Success -> {
                //施錠状態を取得
                if (result1.get().obj().get("locked").toString() == "true"){
                    Locked = true
                }
                //現在操作可能かを取得
                if (result1.get().obj().get("responsive").toString() == "true"){
                    Responsive = true
                }
            }
        }

        //操作可能であれば、2回目の通信
        if(Responsive){
            //SesameのAPIに従って、HTTP(S)のheaderにApiキーと、POSTで送るデータ型を設定
            val header2: HashMap<String, String> = hashMapOf("Authorization" to AuthCode, "Content-Type" to "application/json")
            //Seameに対する操作を設定 状態を逆する様に設定
            var command: String = """{"command":"lock"}"""
            if(Locked){command = """{"command":"unlock"}"""}

            //二回目の通信:Sesameに対して指令(同期通信)
            val (_, _, result2) = ("https://api.candyhouse.co/public/sesame/"+DeviceID).httpPost().header(header2).body(command).responseString()
            //三回目の通信をして、コマンドが処理されたかを確認するのは、
            //目の前で鍵が閉まった、開いたかを確認するのでこのアプリではチェックしない
            when (result2){
                is Result.Failure -> {
                }
                is Result.Success -> {
                }
            }
        }
    }
}

##動作フロー
・スマホにNFCタグをかざす
A.Androidのタグディスパッチシステムにより、アプリが起動
A.onCreate内でそのままIDを読み込み
・NFCからWEBPIへ操作が移り変わる
B.状態の取得
B.施錠・解錠の変更

##参考文献
####WebAPIに関した部分
Sesame API - CANDY HOUSE, Inc.
https://docs.candyhouse.co

APIキー取得方法とセサミIDの確認方法
https://jp.candyhouse.co/blogs/how-to/apiキー取得方法とセサミidの確認方法

言わずもがな、公式サイトのAPI情報を参照

#####AndroidでHTTP通信する
KotlinでHTTP通信(FuelとHttpURLConnection)
https://qiita.com/naoi/items/8df1409ad48ad8f3c632

Kotlin/FuelでHTTPアクセスしました。
https://qiita.com/naokiur/items/d6337bfc976cb1249575

Kotlin+fuel-coroutinesを使ってHTTP通信
https://qiita.com/chouxcreams/items/abb032d8127098dea7d1

Kotlin の Coroutine を概観する
https://qiita.com/kawmra/items/ee4acb7db61f70dec9a8

メインスレッドにてHTTP(S)通信が出来ないようになっていることで、マルチスレッドをどうしたらいいか四苦八苦。
状態確認→施錠・解錠処理は連動させたかったため、連続する動作は「同期処理」にして、全体の処理を「非同期」にすることにしました。
HTTP(S)通信については色んなライブラリ、色んな書き方があったので、むしろその差を理解する方が大変だったなっと思っています。

##編集後記
BLE版を見てから思うと、アッサリとしたソースだなぁとしみじみ思います。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?