AppleHomeKitでデバイスを動作させるために、homebridge用のデバイスをtypescriptで書く

# AppleHomeKitとは
[ここに](https://www.apple.com/ios/home/)紹介されているように、Appleが提供するスマートホームを提供するためのプラットフォーム。他社製品(google, alexa)と異なり、ローカルネットワークで動くためインターネットに接続できない環境でも動作する。そのカラクリはローカルネットワークにiPadかAppleTVなどのデータハブが存在し、ハブ経由で通信が実行されることだ。つまり、インターネットに接続できていたとしても、ハブとして動いているデバイスが動作していなければ、AppleHomeKitを使用したスマートホームは生命線を絶たれた状態になるので、全くもってスマートホームは動作しなくなる。  
# HomeBridgeとは
AppleHomeKitに対応しているデバイスは非常に少ない。大きな原因はAppleの認定制度にある。Appleには[MFi](https://mfi.apple.com)というAppleのエコシステムに乗っかるための認定制度があり、ここに適応できないと大々的にAppleHomeKit適用と製品に書けない。こちらにはAppleの[厳しい審査](https://blog.earley.jp/entry/blog/advent-calendar/2017/apple-mfi-program-summary/)があり、費用がかかり、高頻度のアップデートがあり、筆者のような趣味でスマートホームを楽しむものには大きな壁となる。  
そこで出てくるのが[HomeBridge](https://homebridge.io)と呼ばれるMFiを経由しないでも、適当なIDを用いるだけで、AppleHomeKit傘下にデバイスを登録できるツールだ。詳細はインターネットで調べれば沢山情報を得られるはずだ。なぜかhomebridge用のデバイスを立ち上げる記事は多かったが、デバイスの動きをコーディングする記事が見当たらなかったので、本記事では簡単な実装を示そうと思う。
# HomeBridge API
基本は[ここに](https://developers.homebridge.io/#/)あるAPIを利用していく。  

# 具体例：「部屋の温度と湿度を返すデバイスをAppleHomeKitで立てる」

AppleHomeKitで温度と湿度を返すデバイスを作成する。raspberry pi zeroにI2Cで接続した[BME280](https://www.bosch-sensortec.com/products/environmental-sensors/humidity-sensors-bme280/)の温度と湿度の値を返すWebサービスを立ち上げ、Appleのhomeアプリからそのwebサービスを読みに行きiPhone上に表示させる。以下が実際のコードです。

* [AppleHomuKitのデバイス例](https://gitlab.com/hiroo_sawada/homebridge-bme280-accessory)
* [BME280の値を返すwebサービス](https://gitlab.com/hiroo_sawada/pi_bme280_reader)


## 全体像
* デバイスの種類(テレビ、モーションセンサーなどを）選ぶ。サポートされているものは[ここ](https://developers.homebridge.io/#/categories)に書かれている。
* Platform pluginかAccessory pluginかを選ぶ。
* [テンプレート](https://github.com/homebridge/homebridge-plugin-template)を参照しながら、コーディングする。


## デバイスの種類を選ぶ
今回は温度と湿度の値を表示させる。したがって、[温度センサー](https://developers.homebridge.io/#/service/TemperatureSensor)と[湿度センサー](https://developers.homebridge.io/#/service/HumiditySensor)を選択する。

## Platform pluginかAccessory pluginかを選ぶ
Accessory pluginとはデバイス一つと考えてもらって差し支えないです。Platform pluginはAccessory pluginを複数抱えるプラグインです。具体例を出すと、Phillips HueのようにGatewayの下に照明や人感センサーのような複数のデバイスを一つのpluginで管理したい場合はPlatform pluginを選択します。今回は複数のデバイスではなくて、BME280を繋いだraspberry pi zeroからセンサー値を取り出すだけなので、Accessoryとする。

## テンプレートを参照しながら、コーディングする

### configファイル

configファイルは以下の様になっています。動作に関係するところは、温度はtemperatureにあるhostを見に行き、湿度はhumidityにあるhostを見に行くようにします。他は適宜必要なメタデータになります。

```
     "accessories":[
            {
                "accessory": "Bme280SensorAccessory",
                "name": "Bme280SensorAccessory",
                "temperature":{
                    "name":"temperature",
                    "host":"http://192.168.1.140:8088/bme280/temp"
                },
                "humidity":{
                    "name":"humidity", 
                    "host":"http://192.168.1.140:8088/bme280/humid"
                },
                "manufacturer": "Bosch",
                "model": "BME280",
                "serialnumber": "00000001"
            }
     ]
```

### メタデータ

新しいaccessoryの製造元、シリアル番号、モデルを与えます。ホームアプリでいう以下に相当する箇所です。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2294491/d47a7c73-013b-dc3b-74d0-fd7ae097f74f.jpeg" width="300">

ここではconfigファイルから取得した値をAccessoryInformationのsetCharacteristicを利用して渡しています。


```
this.informationService = new hap.Service.AccessoryInformation()
    .setCharacteristic(hap.Characteristic.Manufacturer, config.manufacturer)
    .setCharacteristic(hap.Characteristic.SerialNumber, config.serialnumber)
    .setCharacteristic(hap.Characteristic.Model, config.model);
```

### accessoryが提供するサービスの記述
accessoryが提供するサービスを定義し、それらに対してgetCharctersticされたときのhandlerを渡します。ここでは温度と湿度というサービスを定義し、それぞれの温度や湿度を返すメソッドを渡しています。


```
// create a new Temperature Sensor service
this.temperatureService = new hap.Service.TemperatureSensor(this.tempname);
this.HumidityService = new hap.Service.HumiditySensor(this.humidname);

// create handlers for required characteristics
this.temperatureService.getCharacteristic(api.hap.Characteristic.CurrentTemperature)
  .on('get', this.handleCurrentTemperatureGet.bind(this));
this.HumidityService.getCharacteristic(api.hap.Characteristic.CurrentRelativeHumidity)
  .on('get', this.handleCurrentHumidityGet.bind(this));
```

温度のhandlerは以下の様になっています。callback関数を渡しそのcallbackに温度を詰め込みます。callback関数にはnullを渡していますが、これはnullを渡せば成功、Errorを渡すと失敗となります。(この例ではcatchしてもログ出すだけで、適切にエラー処理していないですが。。。)　　
ここでのcallbackの処理はconfigから与えたtempHostというURLをaxiosでGETして、そのresponseのJSONの中からvalueというキーの値を取得しています。

```
    handleCurrentTemperatureGet(callback: CharacteristicGetCallback) {
      this.log.debug('Triggered GET CurrentTemperature');
      let currentValue: number = 0;
      // set this to a valid value for CurrentTemperature
      axiosGet(this.temphost)
      .then(data => {
        this.log.debug(data);
        currentValue = data.value;
        callback(null, currentValue);
      })
      .catch(err => {
        this.log.error(err);
      });

    }
```

あとは、typescriptをコンパイルして、homebridgeの適当な箇所に配置すれば動作します。その辺りはサーチエンジンを検索すると色々情報が出てくるので探してみてください。  

本記事をきっかけに、Homebridgeデバイスを自分で作成し、家の色々なものを自動化して楽しんでみていただければ幸いです！！