BLEタグとiPhoneでつくるオレオレAmazon Dash Button

Amazon Dash Buttonをハックして自分専用に作り変えるネタが流行ってますが、もう少し簡単にできないものかと、思いまして作ってみました。

# シナリオ
1. 会議室のホワイトボードマーカーが書けなくなったことに気がつく
2. ホワイトボードの近くの小さなBLEタグのボタンを押す
3. Kintoneへレコードが登録される
4. 総務部の人がそのレコードを見てホワイトボードマーカーを補充
5. 総務部の人はKintoneのレコードを削除する

# 材料
1. ボタン付きBLEタグ：iTag 833円（https://www.amazon.co.jp/dp/B017SC9U76?th=1）
2. お手持ちのiPhone

# 原理
BLE通信でボタン付きBLEタグのボタンクリックイベントを拾うだけ

# BLEのServiceやCharacteristicはどうやって調べたの？
下記のアプリを使うとあっという間に分かります。

BLExplr
https://itunes.apple.com/jp/app/blexplr/id524018027?mt=8

# コード
コンセプト通り機能するかを確認するレベルの内容ですが、こんな感じです。

```swift:ViewController.swift
import UIKit
import CoreBluetooth
import Alamofire

class ViewController: UIViewController, CBCentralManagerDelegate, CBPeripheralDelegate  {

    @IBOutlet weak var label: UILabel!
    @IBOutlet weak var activityIndicator: UIActivityIndicatorView!
    var centralManager: CBCentralManager!
    var tag: CBPeripheral!
    let kintoneBaseURL   = "https://<Kintoneのドメイン>/k/v1"
    let kintoneAppID     = /* <KintoneアプリのID> */
    let kintoneToken     = /* <KIntoneアプリのアクセストークン> */
    let blueTagUUID      = /* <iTagのUUID> */
    let blueTagServiceID = /* <iTagのService ID> */
    let blueTagCharID    = /* <iTagのCharacteristic ID> */
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.centralManager = CBCentralManager(delegate: self, queue: nil, options: nil)
        label.isHidden = true
        activityIndicator.startAnimating()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }


    // CentralManager状態の受信
    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        switch (central.state) {
        case .poweredOff:
            NSLog("BLE PoweredOff")
        case .poweredOn:
            NSLog("BLE PoweredOn")
            central.scanForPeripherals(withServices: nil, options: nil)
        case .resetting:
            NSLog("BLE Resetting")
        case .unauthorized:
            NSLog("BLE Unauthorized")
        case .unknown:
            NSLog("BLE Unknown")
        case .unsupported:
            NSLog("BLE Unsupported")
        }
    }
    
    // Peripheral探索結果の受信と接続
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber){
        NSLog("name: \(peripheral.name), UUID: \(peripheral.identifier.uuidString), adData: \(advertisementData), RSSI: \(RSSI)")
        
        if(peripheral.identifier.uuidString == blueTagUUID){
            self.centralManager.stopScan()
            self.tag = peripheral
            self.centralManager.connect(self.tag, options: nil)
        }
    }
    
    // Peripheralへの接続結果の受信(成功時)
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
        NSLog("connection success: \(peripheral.identifier.uuidString)")
        peripheral.delegate = self
        
        // サービスを探索
        let UUID = CBUUID(string: blueTagServiceID)
        peripheral.discoverServices([UUID])
    }
    
    // Peripheralへの接続結果の受信(失敗時)
    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
        NSLog("connection failed: \(peripheral.identifier.uuidString)")
    }
    
    // Service探索結果の受信
    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
        if (error != nil) {
            NSLog("error: \(error)")
            return
        }
        
        for service in peripheral.services!
        {
            NSLog("found service: \(service)")
            
            //Characterristicを探索
            let UUID = CBUUID(string: blueTagCharID)
            peripheral.discoverCharacteristics([UUID], for:service as CBService)
        }
    }
    
    // Characterristic探索結果の受信
    func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?){
        if (error != nil) {
            NSLog("error: \(error)")
            return
        }
        
        for characteristic in service.characteristics! {
            NSLog("found characteristic: \(characteristic.uuid)")
            if(characteristic.uuid.uuidString == blueTagCharID){
                peripheral.setNotifyValue(true, for: characteristic)
            }
        }
    }
    
    // データ更新通知受け取り開始／停止結果を取得する
    func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
        if let error = error {
            NSLog("error: \(error)")
        } else {
            NSLog("isNotifying: \(characteristic.isNotifying)")
            activityIndicator.hidesWhenStopped = true
            activityIndicator.stopAnimating()
            label.isHidden = false
        }
    }
    
    // データ更新通知を受け取る = ボタンが押された際に発生するイベント
    func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
        if let error = error {
            NSLog("error: \(error)")
            return
        }
        
        NSLog("value updated. UUID: \(characteristic.uuid.uuidString), value: \(characteristic.value)")
        
        if(label.text == "Push me!"){
            label.text = "Hello!"
        }else{
            label.text = "Push me!"
        }
        sendKintone()
    }
    
    func sendKintone(){
        let headers: HTTPHeaders = [
            "X-Cybozu-API-Token":kintoneToken
        ]

        let params: Parameters = [
            "app": kintoneAppID,
            "record": [
                "アイテム" : ["value" : "会議室A：ホワイトボードマーカー（黒）"],
                "数量"    : ["value" : "2"],
            ]
        ]
        
        _ = Alamofire.request("\(kintoneBaseURL)/record.json", method: .post, parameters: params, encoding: JSONEncoding.default, headers: headers).responseJSON{ resp in
                        
            switch resp.result {
            case .success:
                NSLog("Sent")
            case .failure(let error):
                NSLog(error as! String)
            }
        }
    }
}
```

# 参考にした記事

[[Swift][初心者]BLE接続するための情報まとめ](http://qiita.com/MashMorgan/items/32500f158cb08d565786)
[Core Bluetooth with Swift （ObjCのおまけ付き）](http://qiita.com/shu223/items/78614325ce25bf7f4379)