homebridgeプラグイン作成入門

おうちハックAdvent Calendar 1日目にしてギリギリの投稿になってしまいました🙇🏻
この記事ではおうちハックでもよく見かけるhomebridgeのプラグイン開発の方法についてまとめました。

既存のプラグインを使って遊んでいたが、物足りなくなって自分が欲しいプラグインを
書くことになると思いますので、その際の参考になればと思います。

# プラグインのテンプレート
以下のファイルがhomebridgeのプロジェクトにありますのでまずはこれをベースに開発してみると良いかと思います。
https://github.com/nfarina/homebridge/blob/master/example-plugins/homebridge-samplePlatform/index.js


# どんなServiceがあるかを調べる
`HAP-NodeJS/lib/gen/HomeKitTypes.js`(https://github.com/KhaosT/HAP-NodeJS/blob/master/lib/gen/HomeKitTypes.js) にサービスが定義してあるので確認してみましょう。
`cat HomeKitTypes.js | grep ' \* Service'`の実行結果が以下の通り

```
 * Service "Accessory Information"
 * Service "Air Quality Sensor"
 * Service "Battery Service"
 * Service "Bridge Configuration"
 * Service "Bridging State"
 * Service "Camera Control"
 * Service "Camera RTP Stream Management"
 * Service "Carbon Dioxide Sensor"
 * Service "Carbon Monoxide Sensor"
 * Service "Contact Sensor"
 * Service "Door"
 * Service "Doorbell"
 * Service "Fan"
 * Service "Garage Door Opener"
 * Service "Humidity Sensor"
 * Service "Leak Sensor"
 * Service "Light Sensor"
 * Service "Lightbulb"
 * Service "Lock Management"
 * Service "Lock Mechanism"
 * Service "Microphone"
 * Service "Motion Sensor"
 * Service "Occupancy Sensor"
 * Service "Outlet"
 * Service "Pairing"
 * Service "Protocol Information"
 * Service "Relay"
 * Service "Security System"
 * Service "Smoke Sensor"
 * Service "Speaker"
 * Service "Stateful Programmable Switch"
 * Service "Stateless Programmable Switch"
 * Service "Switch"
 * Service "Temperature Sensor"
 * Service "Thermostat"
 * Service "Time Information"
 * Service "Tunneled BTLE Accessory Service"
 * Service "Window"
 * Service "Window Covering"
```

# どんなCharacteristicがあるかを調べる
次にそのサービスにどのようなCharacteristicが使えるのかを調べます。
以下はFanの定義を抜粋したものです。
必須のCharacteristicはOn(つまりOn/Offが可能)
そのほかにもRotationDirection(回転の向き)、RotationSpeed(回転速度)、Name(名前)がオプショナルで設定する事が出来る事がわかります。

```
/**
 * Service "Fan"
 */

Service.Fan = function(displayName, subtype) {
  Service.call(this, displayName, '00000040-0000-1000-8000-0026BB765291', subtype);

  // Required Characteristics
  this.addCharacteristic(Characteristic.On);

  // Optional Characteristics
  this.addOptionalCharacteristic(Characteristic.RotationDirection);
  this.addOptionalCharacteristic(Characteristic.RotationSpeed);
  this.addOptionalCharacteristic(Characteristic.Name);
};

```

これで使用したいServiceとそのCharacteristicがわかりましたね？
では次にCharacteristicのイベントに対してアクションを作っていきましょう。

# 実行するWebAPIを設定する
今回の例ではイベントで実行されるアクションは全てWeb APIをコールする想定で書いてみます。
`on_url`や`off_url`に対して叩きたいAPIをパラメタと渡します。

```
{
    "bridge": {
        "name": "Homebridge",
        "username": "CD:22:3D:E3:CE:30",
        "port": 51826,
        "pin": "031-45-156"
    },
    
    "description": "sample config",

    "platforms": [],

    "accessories": [
        {
            "accessory": "TLFan",
            "name": "ファン",
            "on_url": "", // Your  Motor controll API
            "off_url": "", // Your Motor control API
            "speed_url": "", // Your Motor control API
            "method": "POST",
            "timeout": 10000,
            "polling schedule": "*/1 * * * *"
        }
    ]
}
```

#パラメタに設定したWebAPIをコールする
次にCharacteristicのイベントがキックされたところで、
上記パラメタで与えられたWebAPIを叩くよう記述します。 

```
fanAccessory.prototype = {
  identify: function (callback) {
    this.log('Identify requested!')
    callback() // success
  },

  httpRequest: function (url, method, timeout, callback) {
    request({
      url: url,
      method: method,
      timeout: timeout
    },
    (error, response, body) => {
      callback(error, response, body)
    })
  },

  setState: function (value, callback) {
    this.log('value is: ' + value)
    // ファン ON/OFF操作
    var url = value ? this.on_url : this.off_url
    this.httpRequest(url, this.method, this.timeout, (error, response, body) => {
      let bodyJson = JSON.parse(body)
      if (error || bodyJson['error']) {
        this.log('error :' + error)
        this.log('error :' + body)
        callback(new Error('ファンのコントロールに失敗しました'))
        return
      }
      // this.log('response :' + response)
      this.log('body :' + body)
      this.fanStatus = value
      callback(null)
    })
  },

  getState: function (callback) {
    this.log('get fan...')
    callback(null,this.fanStatus)
  },

  getServices: function () {
    this.log('getServices')
    this.fanService
      .getCharacteristic(Characteristic.On)
      .on('set', this.setState.bind(this))
      .on('get', this.getState.bind(this))
    
    return [this.fanService]
  }
}
```

# Web API側について
Web APIさえ作れていればあとはRaspberry Piを使うなりESPrなどで
作るなりは自由に組めるところですが、お手軽に作りたい方は以下の方法がオススメです。

## Node-REDを使う
https://nodered.jp
Raspbianなどにも標準で入っているNode-REDを使えば、WebAPIを作ったり、Raspberry PiのGPIOを操作をGUIベースの開発環境で簡単に作成する事ができます。

## Wio Nodeを使う
https://liginc.co.jp/290031
http://qiita.com/tamaki/items/6935d55dc6a7ea642f00
Wio NodeはiOS/Androidでファームウェアの書き込みが出来るプロトタイピングに非常に相性の良いツールです。
値段も1,000円ちょっとくらいで買えてしまうので非常に手を出しやすいという点、Groveのセンサ類を使うため、
半田付けなどの電子工作がほぼいらない点などが優れています。

# サンプルコード
- ファン https://github.com/tamaki-shingo/homebridge-tlfan
- 温度計 https://github.com/tamaki-shingo/homebridge-tlhumiditysensor/blob/master/index.js



# 参考URL
- https://github.com/KhaosT/HAP-NodeJS
- https://github.com/nfarina/homebridge/
- https://github.com/nfarina/homebridge/blob/master/example-plugins/homebridge-samplePlatform/
- https://github.com/senyoltw/homebridge-cmd