Chapter 02

HomeKit入門

tokorom
tokorom
2020.10.12に更新

HomeKitでできること

HomeKitでできることは次のとおりです。

  • ホームやルームなどの構成の管理
  • HomeKit対応アクセサリの管理と操作
  • 多数のアクセサリを一度に操作するシーンの管理
  • オートメーションのためのトリガの管理
  • ホームを利用するユーザの管理
  • ホームやアクセサリの変更監視

HomeKitの構成

HomeKit構成全体像
図. HomeKit構成全体像

本節では、HomeKitを構成する要素について解説します。

ホームとアクセサリ

ホームとアクセサリ
図. ホームとアクセサリ

HomeKitの最小構成は自宅などのホーム(HMHome)に、HomeKitに対応したライトやセンサーなどのアクセサリ(HMAccessory)が保持された状態です。
たとえば、自宅のリビングにライトを1つ設置して利用しているだけなら、以降の項で紹介するルームやゾーンなどは一切気にしなくても利用できます。

ホームにアクセサリを簡単に追加するためのメソッドがHMHomeに用意されています(ソースコード. アクセサリの追加)。

アクセサリの追加
home.addAndSetupAccessories { error in
}

この1つのメソッドを呼び出すだけでHomeKitに用意されたアクセサリ追加のUIが表示され、細かな設定まで全ての面倒を見てくれます(図. アクセサリ追加のUI)。

アクセサリ追加のUI
図. アクセサリ追加のUI

ホームマネージャとホーム

ホームマネージャとホーム
図. ホームマネージャとホーム

前項のとおり最小構成はホームとアクセサリだけですが、ホームを管理するホームマネージャ(HMHomeManager)が存在します。利用するホームが自宅1つであっても、ホームマネージャを経由してホームにアクセスする必要があります。この場合、そのホームがホームマネージャのプライマリホームとして設定されています(ソースコード. プライマリホームのアクセサリを参照)。

プライマリホームのアクセサリを参照
// HMHomeManagerDelegateのhomeManagerDidUpdateHomes(_ manager: HMHomeManager)
// が呼ばれるのを待つ必要がある
// これが呼ばれるまでprimaryHomeはnilのまま

let home = homeManager.primaryHome
let accessories = home?.accessories

ホームマネージャと複数のホーム
図. ホームマネージャと複数のホーム

自宅とオフィスなど複数のホームを管理したい場合は、ホームマネージャへホームの追加や削除ができます(ソースコード. ホームの追加と削除)。
ホームを削除すると、そのホームに所属するアクセサリなどもまとめて削除されてしまうため注意が必要です。

ホームの追加と削除
// 名前を指定してホームを追加
homeManager.addHome(withName: "オフィス") { home, error in
    if let home = home {
        // homeが実際に追加されたホーム
    }
}

// 任意のホームを削除
homeManager.removeHome(office) { error in
}

プライマリホームの管理

ホームマネージャにはプライマリホームが1つ存在します。ホームマネージャに1つ目のホームを追加したとき、そのホームは自動的にプライマリホームとなります。また、プライマリホームを削除すると、残ったホームのいずれかが自動的にプライマリホームとなります。

もちろん、任意のホームをプライマリホームに指定することもできます(ソースコード. プライマリホームを指定)。

プライマリホームを指定
homeManager.updatePrimaryHome(myHome) { error in
}

ホームとルーム

ホームとルーム
図. ホームとルーム

ホームにはリビング、寝室など複数のルーム(HMRoom)を持たせることができます。ホームとアクセサリのとおりアクセサリを保持するのはホーム(HMHome)ですが、アクセサリをいずれかのルームに割り当てることができます(ソースコード. アクセサリをルームに割り当てる)。

アクセサリをルームに割り当てる
// ルームに任意のアクセサリを割り当て
home.assignAccessory(accessory, to: room) { error in
}

ホームにルームを追加するときは必ずルームの名前を指定して追加する必要があります。既存のルームを別のホームに移動することはできません。また、ルームをホームから削除すると、そのルームの存在自体が削除されてしまいます(ソースコード. ホームにルームを追加・削除する)。

ホームにルームを追加・削除する
// 名前を指定してホームにルームを追加
home.addRoom(withName: name) { room, error in
    if let room = room {
        // roomが実際に追加されたルーム
    }
}

// ホームから任意のルームを削除
home.removeRoom(room) { error in
}

デフォルトの部屋

「アクセサリをいずれかのルームに割り当てることができる」と書きましたが、正確にはアクセサリは常にいずれか1つのルームに割り当てられた状態になっています。
ホームにはデフォルトの部屋(HMHomeroomForEntireHome())というものがあり、明示的にルームに割り当てられていないアクセサリは全てデフォルトの部屋に割り当てられます。そのアクセサリを他のルームに割り当てると、デフォルトの部屋からは自動的に外されます。また、アクセサリが割り当てられているルームが削除された場合、そのアクセサリは再度デフォルトの部屋に戻ります。

なお、いずれかのルームに割り当てられたアクセサリを割り当てから解除したい場合は、デフォルトの部屋にそのアクセサリを割り当てます[1]ソースコード. アクセサリの割り当てを解除する)。デフォルトの部屋にも残さず完全に削除したい場合はHMHomeremoveAccessory(_:completionHandler:)を使います。

アクセサリの割り当てを解除する
// アクセサリの割り当てを解除
home.assignAccessory(accessory, to: home.roomForEntireHome()) { error in
}

// アクセサリをホームから削除
home.removeAccessory(accessory) { error in
}

ルームとゾーン

ルームとゾーン
図. ルームとゾーン

必要であればルーム(HMRoom)をグルーピングするためのゾーン(HMZone)が利用できます。
ゾーンもホームに追加・削除して管理します(ソースコード. ゾーンの追加・削除)。

ゾーンの追加・削除
// 名前を指定してゾーンを追加
home.addZone(withName: name) { zone, error in
    if let zone = zone {
        // zoneが実際に追加されたゾーン
    }
}

// 任意のゾーンを削除
home.removeZone(zone) { error in
}

ゾーンには複数のルームを追加することができ、ルームは複数のゾーンに所属することができます(ソースコード. ゾーンにルームを追加・削除)。

ゾーンにルームを追加・削除
// ゾーンに任意のルームを追加
zone.addRoom(room) { error in
}

// ゾーンから任意のルームを削除
zone.removeRoom(room) { error in
}

アクセサリへのアクセス

本節では、アクセサリの機能や特性にアクセスし、操作する具体的な方法を解説します。

アクセサリとサービス

アクセサリとサービス
図. アクセサリとサービス

1つのアクセサリ(HMAccessory)は複数の機能を持っています。これらアクセサリの機能のことをHomeKitではサービス(HMService)と呼びます。
サービスには様々な種類があり、iOS 11に定義されている全サービスをHomeKitお役立ちリファレンスHMService.serviceType一覧で紹介しています。

各アクセサリのサービスはservicesプロパティで取得できます(ソースコード. アクセサリのサービスを参照)。

アクセサリのサービスを参照
let services = accessory.services

たとえばPhilips Hueのライトには、表. Philips Hue ライトのサービスに示す2種類のサービスがあります。

表. Philips Hue ライトのサービス

HMServiceType 説明
Lightbulb 電球
AccessoryInformation アクセサリ情報サービス

AccessoryInformationは単にファームウェアバージョンやシリアル番号などのメタ情報を保持するだけのサービスですので、電球の明るさなどを取得・変更するにはLightbulbを利用します。

サービスとキャラクタ

サービスとキャラクタ
図. サービスとキャラクタ

1つのサービス(HMService)は複数の特性を持ちます。これらサービスの特性のことをHomeKitではキャラクタ(HMCharacteristic)と呼びます。
キャラクタにも様々な種類があり、iOS 11に定義されている全キャラクタをHomeKitお役立ちリファレンスHMCharacteristic.characteristicType一覧で紹介しています。

各サービスのキャラクタはcharacteristicsプロパティで取得できます(ソースコード. サービスのキャラクタを参照)。

サービスのキャラクタを参照
let characteristics = service.characteristics

一例としてLightbulbサービスには、表. Lightbulbのキャラクタの5種のキャラクタがあります[2]

表. Lightbulbのキャラクタ

HMCharacteristicType 説明
Name 名前
Hue 色相
Saturation 彩度
Brightness 明るさ
PowerState 電源の状態

たとえばライトが点灯しているかどうかを知るためにはPowerStateのキャラクタを参照します(ソースコード. PowerStateの参照)。
キャラクタの種類の判別にはcharacteristicTypeプロパティを利用し、キャラクタの種類に応じてvalueプロパティを変換して参照する必要があります。
valueプロパティが取り得る値の詳細についてはmetadataプロパティで知ることができます。metadataプロパティについては次項のキャラクタのメタデータで紹介します。

PowerStateの参照
// characteristicsの中からPowerStateを抽出
let service = home.servicesWithTypes([HMServiceTypeLightbulb])?.first
let candidates = service?.characteristics
    .filter { $0.characteristicType == HMCharacteristicTypePowerState }

guard let powerState = candidates?.first else {
    return
}

// powerState.value に取得済みのvalueが入っているが
// readValueでデバイスから最新のvalueを再読み込み可能
powerState.readValue { error in
    // PowerStateはBool(NSNumber)でvalueが返ってくる
    guard let value = powerState.value as? Bool else {
        return
    }
    // ライトが点灯中ならtrue
    // ライトが消灯中ならfalse
    print("# powerState: \(value)")
}

もちろんライトの点灯・消灯も切り替えられます(ソースコード. PowerStateの上書き)。

PowerStateの上書き
// 点灯したい場合はtrue
// 消灯したい場合はfalse
let value = true
powerState.writeValue(value) { error in
}

キャラクタのメタデータ

キャラクタ(HMCharacteristic)のmetadataプロパティに入っているメタデータ(HMCharacteristicMetadata)を参照することで、キャラクタのvalueプロパティが取り得る値の詳細を知ることができます。
メタデータには表. HMCharacteristicMetadataのプロパティ一覧に示すプロパティがあります。

表. HMCharacteristicMetadataのプロパティ一覧

プロパティ 説明
format String? valueのフォーマット
units String? valueの単位
minimumValue NSNumber? 最小値
maximumValue NSNumber? 最大値
stepValue NSNumber? 最小単位
validValues [NSNumber]? valueが取り得る値
maxLength NSNumber? valueが許容するStringの長さ
manufacturerDescription String? 製造元による説明

まず見るべきプロパティはformatです。formatには"int""bool"といったvalueプロパティに入る値のフォーマットを示す文字列が入っています。unitsプロパティはvalueに単位がある場合にその単位を示す文字列を持ちます。
formatunitsの値の一覧をそれぞれHomeKitお役立ちリファレンスHMCharacteristicMetadata.format一覧とのHMCharacteristicMetadata.units一覧で紹介しています。
minimumValuemaximumValuestepValueは、それぞれvalueが数値型の場合の最小値、最大値、最小単位を表します。

一例としてPhilips HueのライトのBrightnessキャラクタ(明度・明るさ)の実際の値を表. Philips Hueライトの明るさのメタデータに示します。

表. Philips Hueライトの明るさのメタデータ

プロパティ 実際の値
format int
units percentage
minimumValue 0
maximumValue 100
stepValue 1
validValues nil
maxLength nil
manufacturerDescription Brightness

これにより、Brightnessキャラクタのvalueは0〜100までの数値で1単位で値の上げ下げができることがわかります。

validValuesプロパティにはvalueが取り得る数値が限定される場合に、サポートされる数値が列挙されます。
たとえばAirQualityキャラクタ(空気質)が取り得る値として、表. AirQualityの値一覧に示す値が定義されています。このうち数個の値しかサポートしていないケースでは[0,1,2,4]といったNSNumberの配列がvalidValuesプロパティにセットされています。

表. AirQualityの値一覧

rawValue
unknown 0
excellent 1
good 2
fair 3
inferior 4
poor 5

最後に、maxLengthプロパティはvalueに入る文字列の最大長を示し、format"string"の場合のみ使われます。

サービスとサービスグループ

必要であれば、複数のサービス(HMService)をサービスグループ(HMServiceGroup)としてまとめられます。これにより、複数サービスの取り扱いが簡単になります[3]
たとえば、自宅の間接照明だけをまとめた「間接照明すべて」というサービスグループを作ることができます(ソースコード. サービスグループの作成・削除)。

サービスグループの作成・削除
// 間接照明をまとめるサービスグループを作る
home.addServiceGroup(withName: "間接照明すべて") { serviceGroup, error in
    guard let serviceGroup = serviceGroup else {
        return
    }

    // すべての間接照明をサービスグループに追加
    for service in allIndirectLights {
        serviceGroup.addService(service) { error in
        }
    }
}

// サービスグループから任意のサービスを削除
serviceGroup.removeService(service) { error in
}

// ホームからサービスグループを削除
home.removeServiceGroup(serviceGroup) { error in
}

サービスグループ(HMServiceGroup)のnameプロパティはそのままSiriで利用できます。たとえば、Siriに「間接照明すべて消す」という命令をすることで、サービスグループにまとめた全ての間接照明を一度に消灯することができます。

アクションとシーンの利用

本節では、アクセサリを制御するアクションや、複数のアクションをまとめるシーンについて解説します。

アクション

HomeKitの要素を更新するアクション(HMAction)という概念があります。

iOS 11でサポートされているアクションは、HMCharacteristicWriteActionという各アクセサリ(HMAccessory)のキャラクタ(HMCharacteristic)のvalueプロパティを更新するアクション1つだけです。
たとえば、照明のSaturationキャラクタ(彩度)を最も大きく(100)するアクションは、ソースコード. 照明の明るさを最大にするアクションのように作ります。

照明の明るさを最大にするアクション
// 照明のキャラクタの中からSaturationを抽出
let candidates = lightbulb.characteristics
    .filter { $0.characteristicType == HMCharacteristicTypeSaturation }

guard let saturation = candidates.first else {
    return
}

// アクションの作成
let action = HMCharacteristicWriteAction(
  characteristic: saturation,
  targetValue: NSNumber(value: 100)
)

シーン

また、複数のアクションをまとめるシーン(HMActionSet)を作り(ソースコード. シーンを作成・削除)、任意に実行することができます(ソースコード. シーンを実行)。

シーンを作成・削除
// シーンを作成
home.addActionSet(withName: "サンプル") { actionSet, error in
    guard let actionSet = actionSet else {
        return
    }

    // 作成したシーンにアクションを追加
    actionSet.addAction(action) { error in
    }
}

// シーンを削除
home.removeActionSet(actionSet) { error in
}
シーンを実行
home.executeActionSet(actionSet) { error in
}

シーンを利用することで、たとえば、帰宅したときに

  • リビングの照明を点灯する
  • 玄関外の照明を消灯する
  • ヒーターをつける

といった一連のアクションをまとめて実行できます。

シーンは自分で作成できるほか、HomeKitに4つビルトインされています(表. ビルトインされているシーン一覧)。

表. ビルトインされているシーン一覧

HMActionSetType name
WakeUp おはよう
Sleep おやすみ
HomeArrival ただいま
HomeDeparture 行ってきます

ビルトインされたシーンにはデフォルトではアクションは1つも追加されていませんが、必要に応じて自由に利用できます。

トリガによるオートメーション

HomeKitにはシーン(HMActionSet)を実行するためのトリガ(HMTrigger)という機能があります。
トリガには、

  • タイマートリガ(HMTimerTrigger
  • イベントトリガ(HMEventTrigger

の2種があります。
これらのトリガを活用することで、

  • 日の入りと同時にクリスマスツリーのライトを点灯させる
  • リビングの気温が28度を超えたらファンを回す

といったオートメーションを実現できます。

タイマートリガ

タイマートリガ(HMTimerTrigger)は主に、指定した日時にシーンを実行するためのトリガです。
HMTimerTriggerは、発火する日時を指定して作成します(ソースコード. HMTimerTriggerを作成)。

```swift:HMTimerTriggerを作成
// UIDatePickerの日付を利用
guard let date = datePicker?.date else {
return nil
}

let calendar = Calendar(identifier: .gregorian)

// 秒が0以外だとHMTimerTriggerの作成に失敗するため注意
let comp = calendar.dateComponents(
[.minute, .hour, .day, .month, .year, .era],
from: date
)
let fireDate = calendar.date(from: comp)

// HMTimerTriggerを作成
let trigger = HMTimerTrigger(
name: "明日の15時",
fireDate: fireDate,
timeZone: datePicker?.timeZone,
recurrence: nil,
recurrenceCalendar: nil
)


`fireDate`に指定する日付は、将来の日付で、秒は0でなければならないことに注意が必要です[^fireDate]。

ここで作成したトリガはホームに追加して利用します(*ソースコード. トリガをホームに追加・削除*)。

```swift:トリガをホームに追加・削除
// トリガを追加
home.addTrigger(trigger) { error in
}

// トリガを削除
home.removeTrigger(trigger) { error in
}

なお、HMTimerTriggerisEnabledプロパティはデフォルトでfalseになっており、トリガは無効な状態です。トリガを有効にするには、シーンを追加したうえでenable(_:completionHandler:)を呼んでisEnabledtrueに更新します(ソースコード. トリガにシーンを追加して有効にする)。
また、繰り返し設定を指定していない場合、isEnabledプロパティは一度実行された時点でfalseに戻されます。

トリガにシーンを追加して有効にする
// トリガにシーンを追加する
trigger.addActionSet(actionSet) { error in
}

// トリガを有効にする
trigger.enable(true) { error in
}

イベントトリガ

イベントトリガ(HMEventTrigger)は、特定のイベント(HMEvent)が発生したときにシーンを実行するためのトリガです。
イベントには、iOS 10以前から利用できた

  • HMCharacteristicEvent
  • HMLocationEvent

の2つと、iOS 11で追加された

  • HMCalendarEvent
  • HMSignificantTimeEvent
  • HMCharacteristicThresholdRangeEvent
  • HMPresenceEvent
  • HMDurationEvent

の5つで、計7つがあります。
iOS 11で加えられたものについてはiOS 11でのアップデートまとめで紹介しますので、ここではHMCharacteristicEventHMLocationEventの2つについて解説します。

HMCharacteristicEvent

HMCharacteristicEventはキャラクタ(HMCharacteristic)のvalueプロパティが指定の値になったときに発生するイベントです。
このイベントをトリガ(HMEventTrigger)に設定することで、たとえば、ドアが開いた時に電球を点灯するといったオートメーションが実現できます。

「ドアが開いたとき」をトリガとするHMEventTriggerを作成するコードをソースコード. ドアが開いたときに発火するトリガに示します。

ドアが開いたときに発火するトリガ
let contactState = //< ドアの開閉状態を示すContactStateキャラクタ

// .none なら非接触(ドアが開いている状態)
let value = NSNumber(value: HMCharacteristicValueContactState.none.rawValue)

// HMCharacteristicEventを使ってトリガ作成
let event = HMCharacteristicEvent(
    characteristic: contactState,
    triggerValue: value
)
let trigger = HMEventTrigger(
    name: "ドアが開いたら",
    events: [event],
    predicate: nil
)

HMLocationEvent

HMLocationEventは自分が指定したエリアに入った(もしくは出た)タイミングで発生するイベントです。
このイベントをトリガ(HMEventTrigger)に設定することで、たとえば、自宅から半径1000メートル以内に入ったときに玄関の電灯を点ける、といったオートメーションが実現できます(ソースコード. 自宅から半径1000メートル以内に入ったときに発火するトリガ)。

自宅から半径1000メートル以内に入ったときに発火するトリガ
// 自宅から半径1000メートルのregion
let center = CLLocationCoordinate2D(
    latitude: 35.70206910,
    longitude: 139.77532690
)
let radius = 1000.0
let region = CLCircularRegion(
    center: center,
    radius: radius,
    identifier: "MyHome"
)

// regionに入ったときのみ対象
region.notifyOnEntry = true
region.notifyOnExit = false

// HMLocationEventを使ってトリガ作成
let event = HMLocationEvent(region: region)
let trigger = HMEventTrigger(
    name: "半径1km以内",
    events: [event],
    predicate: nil
)

predicateプロパティ

イベントトリガ(HMEventTrigger)にはpredicateプロパティがあり、ここにシーンを実行するための条件を加えることができます。
たとえば、イベントトリガにHMLocationEventを設定し、指定したエリアに自分が入ったとしても、このpredicateプロパティに追加した条件を満たしていなければシーンは実行されません。

HomeKitには、この条件(NSPredicate)を作るためのメソッドがいくつか用意されています(このうちiOS 11で追加されたものについてはiOS 11でのアップデートまとめpredicate用メソッドのアップデートで紹介します)。

まず、「指定した時分まで」「指定した時分より後」という条件を作るための

class func predicateForEvaluatingTrigger(occurringBefore: DateComponents)

class func predicateForEvaluatingTrigger(occurringAfter: DateComponents)

があります。
それぞれ指定したい時分をDateComponents型で指定するだけです。
具体的に「18時以降」という条件を指定するコードの例をソースコード. 18時以降という条件を指定に示します。

18時以降という条件を指定
// 18時以降
let comp = DateComponents(hour: 18)
let predicate = HMEventTrigger.predicateForEvaluatingTrigger(
    occurringBefore: comp
)

let trigger = HMEventTrigger(
    name: "18時以降",
    events: [event],
    predicate: predicate
)

次に、特定のキャラクタ(HMCharacteristic)のvalueプロパティが任意の範囲かどうかという条件を作るための

class func predicateForEvaluatingTrigger(HMCharacteristic,
                        relatedBy: NSComparisonPredicate.Operator, toValue: Any)

があります。
値の範囲の指定(relatedBy)にはNSComparisonPredicate.Operator型を利用し、.greaterThan.lessThanOrEqualToなど柔軟な範囲指定ができます。
具体的に「気温が28度を超える」という条件を指定するコードの例をソースコード. 気温が28度を超えるという条件を指定に示します。

気温が28度を超えるという条件を指定
let temperature = //< 現在の気温のキャラクタ

// 気温が28度を超える場合
let predicate = HMEventTrigger.predicateForEvaluatingTrigger(
    temperature,
    relatedBy: .greaterThan,
    toValue: 28.0
)

let trigger = HMEventTrigger(name: "28度超", events: [event], predicate: predicate)

ユーザの管理

自分が作成・管理しているホームにゲストユーザを追加することもできます。
HMHomemanageUsers(completionHandler:)を呼ぶとユーザ管理用の画面が表示されます(ソースコード. ユーザを管理する)。

ユーザを管理する
home.manageUsers { error in
}

この画面でユーザの追加や削除など全ての操作ができます(図. ユーザの管理画面)。

ユーザの管理画面
図. ユーザの管理画面

現在利用中のユーザ(HMUser)はHMHomecurrentUserプロパティで参照でき、HMUsernameプロパティでユーザ名を、isAdministratorプロパティで管理者かどうかをそれぞれ取得できます。

変更の監視

HomeKitが取り扱う情報は常に他のアプリから変更される可能性があります。そのため、HomeKitが持つデリゲートメソッドを利用し、外部からの変更を検知してアプリの状態を正しく保つ必要があります。

なお、これらのデリゲートメソッドは変更を行ったプログラム自体では呼び出されません。
たとえば、プログラムでホーム(HMHome)にルーム(HMRoom)を追加したとしても、そのプログラム自身のHMHomeDelegate.home(_ home: HMHome, didAdd room: HMRoom)が呼ばれることはありません。

カメラの利用

HomeKitに対応したカメラデバイスがあれば、カメラで撮影している動画をアプリの画面に表示したり、静止画を撮ったり、マイクやスピーカーの設定を更新したり、といったことが簡単に実現できます。

これらの操作をするには、カメラ機能を持ったアクセサリ(HMAccessory)のcameraProfilesプロパティからHMCameraProfileのインスタンスを取得し、表. HMCameraProfileのプロパティ一覧に示すプロパティ群を利用します。

表. HMCameraProfileのプロパティ一覧

プロパティ名 説明
streamControl HMCameraStreamControl ビデオストリーム(動画)
snapshotControl HMCameraSnapshotControl スナップショット(静止画)
microphoneControl HMCameraAudioControl マイク
speakerControl HMCameraAudioControl スピーカー
settingsControl HMCameraSettingsControl カメラ設定

以降の節では、各プロパティの利用方法について紹介していきます。

streamControl

HMCameraProfilestreamControlプロパティ(HMCameraStreamControl)を使うことで、カメラで撮影中の動画をアプリ内で表示することができます。

具体的には、

  1. streamControldelegateを設定
  2. HMCameraViewUIViewのサブクラス)を適当な場所に追加する
  3. HMCameraStreamControlstartStream()メソッドで撮影を開始する
  4. HMCameraStreamControlDelegatecameraStreamControl(_:)を受けて、HMCameraViewcameraSourceプロパティに、HMCameraStreamControlcameraStreamプロパティをセットする
  5. 必要なくなったらHMCameraStreamControlstopStream()メソッドで撮影を停止する

とするだけです(ソースコード. HMCameraViewを設置し撮影開始ソースコード. cameraSource]cameraSourceを設定)。

HMCameraViewを設置し撮影開始
cameraProfile.streamControl?.delegate = self

let view = //< CameraViewをaddするUIView

// HMCameraViewを設置
let cameraView = HMCameraView()
cameraView.frame = view.bounds
cameraView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(cameraView)
self.cameraView = cameraView

// 撮影を開始
cameraProfile.streamControl?.startStream()
cameraSource]cameraSourceを設定
extension SampleViewController: HMCameraStreamControlDelegate {
    func cameraStreamControlDidStartStream(
        _ cameraStreamControl: HMCameraStreamControl
    )
    {
        cameraView?.cameraSource = cameraStreamControl.cameraStream
    }
}

これだけで、設置したHMCameraViewの枠内に、撮影中の動画がリアルタイムに描画されます(図. 撮影中の動画を画面に表示)。

撮影中の動画を画面に表示
図. 撮影中の動画を画面に表示

また、動画撮影の状態はHMCameraStreamControlstreamStateプロパティで参照できます。
streamStateプロパティが取り得る値はHMCameraStreamStateというenumとして定義されています(表. HMCameraStreamStateの値一覧)。

表. HMCameraStreamStateの値一覧

説明
notStreaming 撮影中ではない
starting 撮影開始中
stopping 撮影停止中
streaming 撮影中

この他、撮影中の音声の設定をHMCameraStreamControlcameraStreamaudioStreamSettingプロパティで設定できます。
audioStreamSettingプロパティが取り得る値はHMCameraAudioStreamSettingというenumとして定義されています(表. HMCameraAudioStreamSettingの値一覧)。

表. HMCameraAudioStreamSettingの値一覧

説明
muted 音声は利用しない
incomingAudioAllowed カメラ側のマイクによる収音のみ許可
bidirectionalAudioAllowed カメラ側のマイクによる収音も、カメラのスピーカーからこちらの音声を再生することも許可

bidirectionalAudioAllowedを設定する場合、アプリのInfo.plistにNSMicrophoneUsageDescriptionを設定しておく必要があります)

audioStreamSettingプロパティの更新には、updateAudioStreamSetting(_:completionHandler:)メソッドを利用します(ソースコード. audioStreamSettingの更新)。

audioStreamSettingの更新
guard let cameraStream = cameraProfile.streamControl?.cameraStream else {
    return
}

cameraStream.updateAudioStreamSetting(.incomingAudioAllowed) { error in
}

snapshotControl

HMCameraProfilesnapshotControlプロパティ(HMCameraSnapshotControl)を使うことで、カメラで静止画を撮影し、アプリ内で表示することができます。

具体的には、

  1. snapshotControldelegateを設定
  2. HMCameraViewUIViewのサブクラス)を適当な場所に追加する
  3. HMCameraSnapshotControltakeSnapshot()メソッドで静止画を撮影する
  4. HMCameraSnapshotControlDelegatecameraSnapshotControl(_:didTake:error)を受けて、引数のHMCameraSnapshotHMCameraViewcameraSourceプロパティにセットする

とするだけです(ソースコード. HMCameraViewを設置し静止画撮影ソースコード. cameraSourceを設定)。streamControlの利用方法とほぼ同じです。

HMCameraViewを設置し静止画撮影
cameraProfile.snapshotControl?.delegate = self

let view = //< CameraViewをaddするUIView

// HMCameraViewを設置
let cameraView = HMCameraView()
cameraView.frame = view.bounds
cameraView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(cameraView)
self.cameraView = cameraView

// 静止画撮影
cameraProfile.snapshotControl?.takeSnapshot()
HMCameraViewを設置し静止画撮影
extension SampleViewController: HMCameraSnapshotControlDelegate {
    func cameraSnapshotControl(
        _ cameraSnapshotControl: HMCameraSnapshotControl,
        didTake snapshot: HMCameraSnapshot?, error: Error?
    )
    {
        cameraView?.cameraSource = snapshot
    }
}

これにより、HMCameraViewの枠内に、撮影した静止画が描画されます(図. 静止画を撮影して表示)。

静止画を撮影して表示
図. 静止画を撮影して表示

もう1つ、HMCameraSnapshotControlには、最後に撮影した静止画を参照できるmostRecentSnapshotプロパティがあります。ここにHMCameraSnapshotのインスタンスが入っている場合、これをHMCameraViewcameraSourceプロパティにセットして、最後に撮影した静止画を画面に表示することができます。
対象の静止画が撮影された日時は、HMCameraSnapshotcaptureDateプロパティで参照できます。

また、他のアプリなどから静止画が撮影された場合、それをHMCameraSnapshotControlDelegatecameraSnapshotControlDidUpdateMostRecentSnapshot(_:)メソッドで検知することができます。

microphoneControl/speakerControl

HMCameraProfileには、マイクの設定をするためのmicrophoneControlプロパティと、スピーカーの設定をするためのspeakerControlプロパティがあります。

それぞれにHMCameraAudioControlのインスタンスがセットされており、HMCameraAudioControlのプロパティから、消音と音量のキャラクタ(HMCharacteristic)を参照できます(表. HMCameraAudioControlPropertiesのプロパティ一覧)。

表. HMCameraAudioControlPropertiesのプロパティ一覧

プロパティ名 HMCharacteristicType 説明
mute Mute 消音
volume Volume 音量

たとえば、スピーカーの音量を変更するには、ソースコード. スピーカーの音量を変更のようにします。

スピーカーの音量を変更
guard let volume = cameraProfile.speakerControl?.volume else {
    return
}

volume.writeValue(50) { error in
}

settingsControl

settingsControlにはHMCameraSettingsControlのインスタンスがセットされています。HMCameraSettingsControl表. HMCameraSettingsControlのプロパティ一覧に示す9つのキャラクタ(HMCharacteristic)をプロパティとして持ちます。

表. HMCameraSettingsControlのプロパティ一覧

プロパティ名 HMCharacteristicType 説明
currentHorizontalTilt CurrentHorizontalTilt 現在の横方向の傾斜角度
currentVerticalTilt CurrentVerticalTilt 現在の縦方向の傾斜角度
targetHorizontalTilt TargetHorizontalTilt 横方向の傾斜角度の目標値
targetVerticalTilt TargetVerticalTilt 縦方向の傾斜角度の目標値
digitalZoom DigitalZoom デジタルズーム
opticalZoom OpticalZoom 光学ズーム
imageMirroring ImageMirroring 画像反転
imageRotation ImageRotation 画像回転
nightVision NightVision ナイトビジョン

デバイスごとに設定可能なキャラクタのインスタンスが保持されており、サポート外のキャラクタはnilになっています。
たとえば、カメラの画像を反転させるには、ソースコード. カメラの画像を反転させるのようにします。

カメラの画像を反転させる
guard let imageMirroring = cameraProfile.settingsControl?.imageMirroring else {
    return
}

imageMirroring.writeValue(true) { error in
}

Siriの利用

HomeKit構成要素のnameプロパティに設定した名前を利用し、Siriへ各種操作を命令することができます。
現在サポートされているのは以下6つです。

  • ホーム(HMHome
  • ルーム(HMRoom
  • ゾーン(HMZone
  • サービス(HMService
  • サービスグループ(HMServiceGroup
  • シーン(HMActionSet

ホーム指定での操作

たとえばホーム(HMHome)のnameプロパティに「オフィス」と設定している場合、Siriに

「オフィス」の電気をつけて

と命令することで、そのホームの全ての電気を点けることができます。
なお、

電気をつけて

と名前を省略して命令をした場合、プライマリホーム(primaryHome)のすべての電気が点灯します。

ルーム・ゾーン指定での操作

ルーム(HMRoom)のnameプロパティを使って、

「リビング」の電気をつけて

と命令することができます。
また、複数のルームを束ねたゾーン(HMZone)のnameプロパティを使って、

「2階」の電気をつけて

と命令することもできます。

アクセサリ指定での操作

現在のところ、アクセサリ(HMAccessory)のnameプロパティはSiriに認識されないようです。

サービス・サービスグループ指定での操作

サービス(HMService)のnameプロパティを指定して、

「間接照明」をつけて

と命令することも、もちろんできます。
なお、サービス(HMService)のnameプロパティは重複可能で、複数のサービスに同じ名前をつけ、1回の命令でそれら全てを操作することもできます。

また、複数のサービスをサービスグループ(HMServiceGroup)にまとめ、サービスグループのnameプロパティ指定で、

「間接照明すべて」をつけて

と命令することもできます。

シーン指定での操作

シーン(HMActionSet)のnameプロパティを指定して、

「スペシャル設定」にして

と命令し、複数のアクセサリの複数のキャラクタを、シーンに登録した状態にまとめて変更することもできます。

トリガ指定での操作

現在のところ、トリガ(HMTrigger)のnameプロパティはSiriに認識されないようです。

脚注
  1. 割り当てを解除するための専用のメソッドは存在しない ↩︎

  2. 正確には未定義(カスタム)の特性をもう1つ持っています ↩︎

  3. HMServiceTypeAccessoryInformationなどserviceTypeによってはサービスグループに追加できないものがあります ↩︎