Android 向けカメラ デバイス ガイド

カメラ デバイスタイプは、3 つのトレイトを使用して実装されます。PushAvStreamTransport は、プッシュベースのプロトコルを使用して音声と動画のストリーム転送を処理します。WebRtcLiveView は、ライブ配信とトークバックを制御する機能を提供します。CameraSnapshot は、キャッシュに保存されたプレビュー スナップショットとライブ スナップショット キャプチャを提供します。

機能を使用したり、属性を更新したりする前に、必ずデバイスの属性とコマンドのサポートを確認してください。詳細については、Androidでデバイスを操作するをご覧ください。

Home API のデバイスタイプ トレイト Kotlin サンプルアプリ ユースケース

カメラ

GoogleCameraDevice

home.matter.6006.types.0158

静止画像または動画を撮影するデバイス。カメラには、アクセス可能なライブストリーム、双方向トークバック、検出アクティビティなどの機能が搭載されている場合があります。

必須のトレイト
     google PushAvStreamTransport
     google WebRtcLiveView

カメラ

デバイスに関する基本情報を取得する

   Android 向けサンプルアプリに実装済み   

BasicInformation トレイトには、デバイスのベンダー名、ベンダー ID、プロダクト ID、プロダクト名(モデル情報を含む)、ソフトウェア バージョンなどの情報が含まれます。

// Get device basic information. All general information traits are on the RootNodeDevice type.
    device.type(RootNodeDevice).first().standardTraits.basicInformation?.let { basicInformation ->
        println("vendorName ${basicInformation.vendorName}")
        println("vendorId ${basicInformation.vendorId}")
        println("productId ${basicInformation.productId}")
        println("productName ${basicInformation.productName}")
        println("softwareVersion ${basicInformation.softwareVersion}")
    }

シリアル番号を確認する

デバイスのシリアル番号を取得するには、ExtendedBasicInformation トレイトの GetSerialNumber コマンドを使用します。この例では、シリアル番号を serialNumber という名前の変数に保存しています。

val basicInfo: ExtendedBasicInformation = device.getTrait(ExtendedBasicInformation)
val serialNumber = basicInfo.getSerialNumber().serialNumber

デバイスがクラウドに最後に接続した時刻を取得する

デバイスがクラウドに接続した最新の時刻を確認するには、ExtendedGeneralDiagnostics トレイトの lastContactTimestamp 属性を使用します。

fun getLastContactTimeStamp(trait: ExtendedGeneralDiagnostics): java.time.Instant {
  val timestamp = trait.lastContactTimestamp
  return Instant.ofEpochSecond(timestamp.toLong())
}

カメラマウントのタイプの設定

Mount トレイトには、カメラマウントの設定とステータス情報が含まれます。マウント状態、検出タイプ、マウントタイプ名などの属性を読み取ることができます。また、Mount トレイトを使用して、デフォルトのマウントタイプ構成をオーバーライドすることもできます。

// Get the Mount trait
val mountTrait: Mount = device.getTrait(Mount)

// Read the current mount state and detection type
val mountState = mountTrait.mountState
val mountDetectionType = mountTrait.mountDetectionType

// Read the current mount type name
val mountTypeName = mountTrait.mountTypeName

// Update the mount type override
mountTrait.update {
  setMountTypeOverride(MountTrait.MountTypeOverrideEnum.Official)
}

デバイスの接続性を確認する

一部のデバイスは複数のデバイスタイプをサポートしているため、デバイスの接続性は実際にはデバイスタイプ レベルで確認されます。返される状態は、そのデバイスのすべてのトレイトの接続状態を組み合わせたものです。

val cameraConnectivity = cameraDevice.metadata.sourceConnectivity.connectivityState

インターネット接続がない場合に、デバイスタイプが混在していると、PARTIALLY_ONLINE の状態が観察されることがあります。Matter 標準トレイトは、ローカル ルーティングによりオンラインのままになる可能性がありますが、クラウドベースのトレイトはオフラインになります。

デバイスの IP アドレスを取得する

デバイスの IP アドレスを確認するには、GeneralDiagnostics トレイトの networkInterfaces 属性を使用します。アドレスはバイト配列として返されます。これは、標準の IPv4 または IPv6 文字列にフォーマットできます。

val ipAddresses =
  trait.networkInterfaces?.flatMap { networkInterface ->
    (networkInterface.ipv4Addresses + networkInterface.ipv6Addresses).mapNotNull { bytes ->
      try {
        java.net.InetAddress.getByAddress(bytes).hostAddress
      } catch (e: java.net.UnknownHostException) {
        null
      }
    }
  } ?: emptyList()

ライブ配信を開始する

   Android 向けサンプルアプリに実装済み   

ライブ配信を開始するには、Session Description Protocol(SDP)文字列を WebRtcLiveView トレイトの startLiveView() メソッドに送信します。このメソッドは、次の 3 つの値を含む WebRtcLiveViewTrait.StartLiveViewCommand.Response を返します。

  • セッションの SDP。
  • セッション継続時間(秒単位)。
  • セッション ID。セッションの延長または終了に使用できます。
suspend fun getWebRtcLiveViewTrait(cameraDevice: HomeDevice) {
 return cameraDevice.type(GoogleCameraDevice).trait(WebRtcLiveView).first {
    it?.metadata?.sourceConnectivity?.connectivityState == ConnectivityState.ONLINE
  }

}

// Start the live view
suspend fun startCameraStream(trait: WebRtcLiveView, offerSdp: String) {
  val response = trait.startLiveView(offerSdp)
  // Response contains three fields (see below)
  return response
}
  ...

// This is used to manage the WebRTC connection
val peerConnection: RTCPeerConnection = ...

   ...

val startResponse = startCameraStream(sdp)
val answerSdp = startResponse?.answerSdp
val sessionDuration = startResponse?.liveSessionDurationSeconds
val mediaSessionId = startResponse?.mediaSessionId

peerConnection.setRemoteDescription(SessionDescription.Type.ANSWER,
                                    answerSdp)

ライブ配信を延長する

   Android 向けサンプルアプリに実装済み   

ライブ配信には、有効期限が切れるまでの時間が事前に設定されています。アクティブなストリームの期間を延長するには、WebRtcLiveView.extendLiveView() メソッドを使用して延長リクエストを発行します。

// Assuming camera stream has just been started
suspend fun scheduleExtension(trait: WebRtcLiveView, mediaSessionId: String, liveSessionDurationSeconds: UShort ) {
  delay(liveSessionDurationSeconds - BUFFER_SECONDS * 1000)
  val response = trait.extendLiveView(mediaSessionId)
  // returns how long the session will be live for
  return response.liveSessionDurationSeconds
}

TalkBack を開始、停止する

   Android 向けサンプルアプリに実装済み   

トークバックを開始するには、WebRtcLiveView トレイトの startTalkback() メソッドを呼び出します。停止するには、stopTalkback() を使用します。

// Make sure camera stream is on
suspend fun setTalkback(isOn: Boolean, trait: WebRtcLiveView, mediaSessionId: String) {
  if(isOn) {
    trait.startTalkback(mediaSessionId)
  } else {
    trait.stopTalkback(mediaSessionId)
  }
}

ライブ配信を管理する

ライブ配信の画質を調整すると、クライアントの視聴コンテキストに基づいて帯域幅の使用量を最適化できます(たとえば、小さなプレビュー タイル、ギャラリー表示、ピクチャー イン ピクチャー モードを表示するときに、より低い解像度に切り替えるなど)。

WebRtcLiveView トレイトを使用して品質を動的に変更すると、特定のクライアントでアクティブなライブ配信セッションの解像度が管理されます。これは、デバイスで直接デバイス全体の帯域幅使用量を設定する場合とは異なります。デバイスで直接設定すると、同時視聴者全員と、クラウドに保存される過去の録画の画質に影響します。

次の例は、デバイスのライブ配信の画質を取得して更新する方法を示しています。

  • サポートされている画質オプションを取得する: デバイスでサポートされている、利用可能なストリーミング解像度を取得します。このコードは、デバイスタイプ フローで WebRtcLiveView トレイトの supportedQualityHints 属性をクエリし、サポートされている品質を QualityHint 値(QUALITY_HINT_SD、QUALITY_HINT_HD、QUALITY_HINT_FHD、QUALITY_HINT_QHD、QUALITY_HINT_UHD など)のリストを含む StateFlow として公開します。

  • ライブ配信の画質を変更する: 選択した QualityHint を適用して、アクティブなライブ配信のストリーミング解像度を変更します(標準画質から高画質への切り替えなど)。changeQuality 関数は、デバイスのライブ ストリーミング トレイトを解決し、アクティブな mediaSessionId と選択した QualityHint 構成を使用して changeLiveViewQuality を呼び出します。

// Assuming you have a HomeDevice instance 'device'
val availableQualityHints: StateFlow<List> =
    device.type(GoogleCameraDevice)
        .trait(WebRtcLiveView)
        .map { trait -> 
            trait?.supportedQualityHints ?: emptyList() 
        }
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), emptyList())

// Assuming you have a HomeDevice instance 'device'
suspend fun changeQuality(mediaSessionId: String, qualityHint: QualityHint) {
    // Get the trait from the device
    val trait = device.type(GoogleCameraDevice).trait(WebRtcLiveView).first() ?: return
    try {
        trait.changeLiveViewQuality(mediaSessionId, qualityHint)
    } catch (e: Exception) {
        // Handle error
    }
}

録画機能を有効または無効にする

カメラの録画機能を有効にするには、TransportStatusEnum.Active を PushAvStreamTransport トレイトの setTransportStatus() メソッドに渡します。録画機能を無効にするには、TransportStatusEnum.Inactive を渡します。次の例では、これらの呼び出しを Boolean を使用する単一の呼び出しにラップして、録音機能を切り替えています。

// Start or stop recording for all connections.
suspend fun setCameraRecording(trait: PushAvStreamTransport, isOn: Boolean) {
  if(isOn) {
    trait.setTransportStatus(TransportStatusEnum.Active)
  } else {
    trait.setTransportStatus(TransportStatusEnum.Inactive)
  }
}

カメラの録画機能を有効または無効にすることは、カメラの動画をオンまたはオフにすることと同じです。カメラの動画がオンの場合、録画が行われます(アクティビティや関連するクリップのため)。

録画機能が無効になっている場合(カメラの動画がオフの場合):

  • カメラは、デバイスタイプの connectivityState に従ってオンラインとして表示されます。
  • ライブ配信にアクセスできず、カメラがクラウド イベントを検出することもありません。

録画機能が有効になっているかどうかを確認する

カメラの録画機能が有効になっているかどうかを判断するには、アクティブな接続があるかどうかを確認します。次の例では、これを行う 2 つの関数を定義しています。

// Get the on/off state
suspend fun onOffState(pushAvStreamTransport: PushAvStreamTransport) {
  return pushAvStreamTransport
    .currentConnections?.any { it.transportStatus == TransportStatusEnum.Active } ?: false
}

// Check if the camera's recording capability is enabled
fun PushAvStreamTransport.recordModeActive(): Boolean {
  return currentConnections?.any { it.transportStatus == TransportStatusEnum.Active } ?: false
}

別の方法として、findTransport() 関数を述語とともに使用して確認することもできます。

// Fetch the current connections
suspend fun queryRecordModeState(trait: PushAvStreamTransport) {
  return trait.findTransport().let {
      it.transportConfigurations.any { it.transportStatus == TransportStatusEnum.Active
    }
}

カメラのスナップショットを取得する

   Android 向けサンプルアプリに実装済み   

CameraSnapshot トレイトを使用して、カメラから静止画像のスナップショットを取得します。これらのスナップショットは、次のようなシナリオで役立ちます。

  • WebRTC ライブ配信の読み込み中に、UI のプレースホルダ タイルとして表示されます。
  • ユーザー操作によってライブ配信にアップグレードされる静的なプレビュー画像として。
  • カスタム アクティビティ エリアを描画して設定するための背景キャンバスとして。

CameraSnapshot トレイトには、高速レンダリング用のキャッシュに保存されたスナップショットが必要か、オンデマンドのライブ スナップショットが必要かに応じて、スナップショットを取得する 2 つの方法が用意されています。

キャッシュに保存されたプレビュー スナップショットを取得する

プレビュー スナップショットは、SDK がカメラを起動せずに取得できるキャッシュに保存された画像です。これは、静止画像を取得する最も速い方法であり、カメラの概要ページやダッシュボード グリッドに最適です。

プレビュー スナップショットを取得するには、preview_image_url トレイト属性を読み取ります。

// Get a flow of the CameraSnapshot trait
val cameraSnapshotFlow: Flow<CameraSnapshot?> = cameraDevice
  .type(GoogleCameraDevice)
  .trait(CameraSnapshot)

// Get the current trait state
val cameraSnapshot = cameraSnapshotFlow.firstOrNull()
if (cameraSnapshot != null) {
  val previewImageUrl = cameraSnapshot.previewImageUrl
  // Load the preview image from the URL
} else {
  // CameraSnapshot trait not supported on this device
}

カメラの設定とアクティビティに応じて、プレビュー URL は、最近の動画映像(最新の動画フラグメントの最初の I フレーム)から生成されたキャッシュに保存された画像、またはカメラによって定期的にアップロードされたスナップショットを指します。

画像の鮮度を示すため、Google Home クラウドは、提供される JPEG/PNG 画像に X-Home-Image-Captured-At ヘッダーを埋め込みます。このヘッダーは UTC 形式です。このヘッダーをクライアント側で解析して、アプリにスナップショットの経過時間を表示できます。

オンデマンドのライブ スナップショットを取得する

アクティビティ エリアの設定など、カメラの現在のライブビューが必要なユーザー ジャーニーでは、ライブ スナップショットをリクエストできます。ライブ スナップショットをトリガーすると、カメラが起動して新しいフレームをキャプチャし、クラウドにアップロードします。

ライブ スナップショットをリクエストするには、getLiveSnapshot() コマンドを使用します。

try {
  val response = cameraSnapshot.getLiveSnapshot()
  val snapshotUrl = response.snapshotUrl
  // Load the live snapshot image from the URL
} catch (e: Exception) {
  Log.e("CameraSnapshot", "Failed to get live snapshot: ${e.message}")
}

スナップショット リクエストを認証する

   Android 向けサンプルアプリに実装済み   

すべてのスナップショット URL は安全なメディアを指しており、Home APIs v2 スコープの OAuth 2.0 トークンが必要です。画像をダウンロードしてレンダリングするには、OAuth トークンを HTTP リクエストの Authorization ヘッダーに署名なしトークンとして挿入する必要があります。

次の例は、スナップショットをダウンロードするための安全なネットワーク リクエストを構成する方法を示しています。

import android.graphics.Bitmap
import android.graphics.BitmapFactory
import java.io.IOException
import java.net.HttpURLConnection
import java.net.URL
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

/**
 * Downloads a snapshot from a URL using an OAuth access token.
 */
suspend fun loadSnapshotImage(urlString: String, oauthToken: String): Bitmap? =
  withContext(Dispatchers.IO) {
    var connection: HttpURLConnection? = null
    try {
      val url = URL(urlString)
      connection = url.openConnection() as HttpURLConnection
      // 1. Configure the HTTP GET request with the Bearer token
      connection.setRequestProperty("Authorization", "Bearer $oauthToken")

      // 2. Fetch the image data
      if (connection.responseCode == HttpURLConnection.HTTP_OK) {
        connection.inputStream.use { inputStream ->
          BitmapFactory.decodeStream(inputStream)
        }
      } else {
        null
      }
    } catch (e: IOException) {
      e.printStackTrace()
      null
    } finally {
      connection?.disconnect()
    }
  }

バッテリーの設定

さまざまなバッテリー設定を Home API で制御できます。

バッテリー使用量の設定

エネルギー バランスを設定すると、デバイスのバッテリー駆動時間とパフォーマンスのトレードオフを構成できます。「拡張」、「バランス」、「パフォーマンス」など、さまざまなバッテリー プロファイルを作成して切り替えることができます。

この機能は、EnergyPreference トレイトの currentEnergyBalance 属性を更新することで実装されます。この属性は、デバイスの energyBalances リストで定義されている特定のプロファイルに対応する整数インデックスを受け入れます(たとえば、EXTENDED の場合は 0、BALANCED の場合は 1、PERFORMANCE の場合は 2)。

currentEnergyBalance の null 値は、デバイスがカスタム プロファイルを使用していることを示します。これは読み取り専用の状態です。

次の例は、currentEnergyBalance 属性が使用する構造の例と、その属性を使用する実際のコード スニペットを示しています。

// Example energyBalances list
energy_balances: [
  {
    step: 0,
    label: "EXTENDED"
  },
  {
    step: 50,
    label: "BALANCED"
  },
  {
    step: 100,
    label: "PERFORMANCE"
  }
]
// The index parameter must be within the UByte range (0-255).
suspend fun setEnergyBalance(trait: EnergyPreference, index: Int) {
  trait.update { setCurrentEnergyBalance(index.toUByte()) }
}

// Setting the battery usage to more recording ie performance
setEnergyBalance(energyPreference, 2)

自動バッテリー セーバーをオンにする

この機能を構成するには、EnergyPreference トレイトの currentLowPowerModeSensitivity 属性を更新します。この属性は、インデックスを使用して感度レベルを選択します。通常、0 は Disabled を表し、1 は Enabled または Automatic を表します。

suspend fun setAutomaticBatterySaver(enable: Boolean, trait: EnergyPreference) {
  // 0 is Disabled, 1 is Enabled
  val value = if (enable) 1.toUByte() else 0.toUByte()
  trait.update { setCurrentLowPowerModeSensitivity(value) }
}

バッテリーの充電状態を取得する

デバイスの現在の充電状態(充電中、フル充電、充電していない)を取得するには、PowerSource トレイトの batChargeState 属性を使用します。

// Get the battery charging state
val batteryChargeState = powerSource.batChargeState

when (batteryChargeState) {
    PowerSourceTrait.BatChargeStateEnum.IsCharging -> "Charging"
    PowerSourceTrait.BatChargeStateEnum.IsAtFullCharge -> "Full"
    PowerSourceTrait.BatChargeStateEnum.IsNotCharging -> "Not Charging"
    else -> "Unknown"
}

バッテリー残量を取得する

現在のバッテリー残量を取得するには、PowerSource トレイトの batChargeLevel 属性を使用します。レベルは OK、Warning(低)、Critical のいずれかです。

// Get the battery charge level
val batteryLevel = powerSourceTrait.batChargeLevel

when (batteryLevel) {
    PowerSourceTrait.BatChargeLevelEnum.OK -> "OK"
    PowerSourceTrait.BatChargeLevelEnum.Warning -> "Warning"
    PowerSourceTrait.BatChargeLevelEnum.Critical -> "Critical"
    else -> "Unknown"
}

電源を入手する

デバイスが使用している電源を特定するには、PowerSource トレイトの BatPresent 属性と wiredPresent 属性を使用します。

  val trait: PowerSource
  val isWired = trait.wiredPresent
  val hasBattery = trait.batPresent

バッテリーの交換が必要かどうかを確認する

batReplaceability 属性は、バッテリーが交換不可、ユーザー交換可能、工場交換可能のいずれであるかを示します。

PowerSource トレイトの batReplacementNeeded 属性は、バッテリーの交換が必要かどうかを示します。

// Get the battery replacement status
val batteryStatus = powerSourceTrait.batReplacementNeeded

if (batteryStatus) {
  println("Replace the battery.")
}

バッテリーがユーザー交換可能な場合、batReplacementDescription には、フォーム ファクタ、化学、推奨メーカーなどの情報を含むバッテリーの説明が記載されます。

音声設定

Home API を使用して、さまざまなオーディオ設定を制御できます。

マイクのオンとオフを切り替える

デバイスのマイクをオンまたはオフにするには、組み込みの setMicrophoneMuted Kotlin 関数を使用して、CameraAvStreamManagement トレイトの microphoneMuted 属性を更新します。

// Turn the device's microphone on or off
suspend fun turnOffMicrophone(disableMicrophone: Boolean, trait: CameraAvStreamManagement) {
  trait.update { setMicrophoneMuted(disableMicrophone) }
}

録音のオンとオフを切り替える

デバイスの録音をオンまたはオフにするには、組み込みの setRecordingMicrophoneMuted Kotlin 関数を使用して、CameraAvStreamManagement トレイトの recordingMicrophoneMuted 属性を更新します。

// Turn audio recording on or off for the device
suspend fun turnOffAudioRecording(disableAudioRecording: Boolean, trait: CameraAvStreamManagement) {
  trait.update { setRecordingMicrophoneMuted(disableAudioRecording) }
}

スピーカーの音量を調整する

デバイスのスピーカー音量を調整するには、組み込みの setSpeakerVolumeLevel Kotlin 関数を使用して、CameraAvStreamManagement トレイトの speakerVolumeLevel 属性を更新します。

// Adjust the camera speaker volume
suspend fun adjustSpeakerVolume(volume: Int, trait: CameraAvStreamManagement) {
  trait.update { setSpeakerVolumeLevel(volume.toUbyte()) }
}

アクティビティ エリアの設定

   Android 向けサンプルアプリに実装済み   

ZoneManagement トレイトは、カメラやドアホン デバイスでカスタムの関心領域(アクティビティ エリア)を管理するためのインターフェースを提供します。これらのエリアは、アクティビティ検出(人物や車両の動きなど)をデバイスの画角内の特定のエリアにフィルタするために使用されます。

アクティビティ エリアは、パートナー アプリケーション内でユーザーが設定します。これにより、カメラの画角内の特定のエリアにエリアを描画できます。これらのユーザー定義ゾーンは、このトレイトで使用される構造に変換されます。アクティビティ エリアの仕組みについて詳しくは、アクティビティ エリアを設定して使用するをご覧ください。

アクティビティ エリアは通常、2D デカルト座標を使用して定義されます。このトレイトは、頂点の TwoDCartesianVertexStruct と、ゾーン定義(名前、頂点、色、使用状況)の TwoDCartesianZoneStruct を提供します。

アクティビティ エリアを確認する

アクティビティ エリアを表示するには、ZoneManagement トレイトの zones 属性を確認します。

// 1. Obtain the trait flow from the device
private val zoneManagementFlow: Flow =
  device.type(CAMERA_TYPE).flatMapLatest { it.trait(ZoneManagement) }

// 2. Map the flow to the list of zone structures
val activityZones: Flow<List<ZoneManagementTrait.ZoneInformationStruct>> =
  zoneManagementFlow.map { trait ->
    trait.zones ?: emptyList()
  }

アクティビティ エリアを追加する

新しいゾーンを作成するには、createTwoDCartesianZone コマンドを使用します。このコマンドは、ゾーンの名前、頂点、色、使用状況を定義する TwoDCartesianZoneStruct を受け取ります。

次の例は、4 つの頂点を持つ「Front Porch」という名前のエリアを作成する方法を示しています。このエリアはサーモン色(#F439A0)で、モーション検知に使用されます。

import com.google.home.google.ZoneManagement
import com.google.home.google.ZoneManagementTrait
import com.google.home.matter.serialization.OptionalValue

/**
 * Creates a custom activity zone named "Front Porch" with a salmon color
 * configured for motion detection.
 */
suspend fun createFrontPorchZone(zoneManagement: ZoneManagement) {
  // 1. Define the vertices for the zone (2D Cartesian coordinates)
  // Values are typically scaled to a maximum defined by the device's twoDCartesianMax attribute.
  val vertices =
    listOf(
      ZoneManagementTrait.TwoDCartesianVertexStruct(x = 260u, y = 422u),
      ZoneManagementTrait.TwoDCartesianVertexStruct(x = 1049u, y = 0u),
      ZoneManagementTrait.TwoDCartesianVertexStruct(x = 2048u, y = 0u),
      ZoneManagementTrait.TwoDCartesianVertexStruct(x = 2048u, y = 950u),
      ZoneManagementTrait.TwoDCartesianVertexStruct(x = 1630u, y = 1349u),
      ZoneManagementTrait.TwoDCartesianVertexStruct(x = 880u, y = 2048u),
      ZoneManagementTrait.TwoDCartesianVertexStruct(x = 0u, y = 2048u),
      ZoneManagementTrait.TwoDCartesianVertexStruct(x = 638u, y = 1090u)
    )

  // 2. Define the zone structure
  val newZone =
    ZoneManagementTrait.TwoDCartesianZoneStruct(
      name = "Front Porch",
      vertices = vertices,
      // Usage defines what the zone filters (for example, Motion, Person, Vehicle)
      use = listOf(ZoneManagementTrait.ZoneUseEnum.Motion),
      // Color is typically a hex string (for example, Salmon/Pink)
      color = OptionalValue.present("#F439A0")
    )

  try {
    // 3. Execute the command to add the zone to the device
    zoneManagement.createTwoDCartesianZone(newZone)
    println("Successfully created activity zone.")
  } catch (e: Exception) {
    // Handle potential HomeException or Timeout
    println("Failed to create activity zone: ${e.message}")
  }
}

アクティビティ エリアを更新する

既存のゾーンを更新するには、updateTwoDCartesianZone コマンドを使用します。このコマンドには、zoneId と更新された TwoDCartesianZoneStruct が必要です。

private suspend fun ZoneManagement.updateZone(
  zoneId: UShort,
  zone: ZoneManagementTrait.TwoDCartesianZoneStruct
) {
  // Execute the command to update the zone
  this.updateTwoDCartesianZone(zoneId = zoneId, zone = zone)
}

アクティビティ エリアを削除する

ゾーンを削除するには、特定の zoneId を指定して removeZone コマンドを使用します。

private suspend fun ZoneManagement.deleteZone(zoneId: UShort) {
  // Execute the command to remove the zone
  this.removeZone(zoneId = zoneId)
}

サウンドに関するアクティビティのトリガー

AvStreamAnalysis トレイトは、カメラやドアホン デバイスのアクティビティ検出トリガーを管理するためのインターフェースを提供します。ビジョンベースのトリガー(人や車両など)はエリア固有にできますが、音関連のトリガーは通常、デバイスレベルの設定です。

EventTriggerTypeEnum のサウンド検知では、次のトリガータイプを使用できます。

モード 列挙値 説明
サウンド Sound 一般的なサウンド検知。
話し声 PersonTalking 音声を検出します。
犬の吠える声 DogBark 犬の鳴き声を検出します。
ガラスの割れる音 GlassBreak ガラスの割れる音を検知します。
煙警報 SmokeAlarm 煙警報を検出します。多くの場合、T3 音声パターン(3 回の短いビープ音の後に一時停止)で認識されます。
一酸化炭素警報 CoAlarm 一酸化炭素(CO)警報を検知します。通常、T4 音声パターン(4 回の短いビープ音の後に一時停止)で認識されます。

サウンド検知のステータスを確認する

サウンド検知の現在の状態をユーザーに表示するには、デバイスがサポートしている機能と、デバイス ハードウェアで有効になっている機能を確認する必要があります。確認する 2 つの属性は次のとおりです。

Kotlin Flow を使用した Android 開発では、通常、HomeDevice から AvStreamAnalysis 特性を監視します。

// Example structure to store the
data class EventTriggerAttribute(val type: EventTriggerTypeEnum, val enabled: Boolean)

// 1. Obtain the trait flow from the device
private val avStreamAnalysisFlow: Flow<AvStreamAnalysis> =
  device.traitFromType(AvStreamAnalysis, CAMERA_TYPES.first { device.has(it) })

// 2. Map the flow to a list of sound event attributes
val soundEventTriggersState: Flow<List<EventTriggerAttribute>> =
  avStreamAnalysisFlow.map { trait ->
    // Get raw lists from the trait attributes
    val supported = trait.supportedEventTriggers ?: emptyList()
    val enabled = trait.enabledEventTriggers ?: emptyList()

    // Define sound-specific triggers to filter for
    val soundTypes = setOf(
      EventTriggerTypeEnum.Sound,
      EventTriggerTypeEnum.PersonTalking,
      EventTriggerTypeEnum.DogBark,
      EventTriggerTypeEnum.GlassBreak,
      EventTriggerTypeEnum.SmokeAlarm,
      EventTriggerTypeEnum.CoAlarm,
    )

    // Filter and associate status
    supported
      .filter { soundTypes.contains(it) }
      .map { type ->
        EventTriggerAttribute(
          type = type,
          enabled = enabled.contains(type)
        )
      }
  }

有効なトリガーのセットを更新する

有効なトリガーのセットを更新するには、SetOrUpdateEventDetectionTriggers コマンドを使用します。このコマンドは、EventTriggerEnablement 構造のリストを受け取ります。

private suspend fun AvStreamAnalysis.updateEventTriggers(
  eventTriggers: List<EventTriggerAttribute>
) {
  val toUpdate = eventTriggers.map {
    EventTriggerEnablement(
      eventTriggerType = it.type,
      enablementStatus = if (it.enabled) {
        EnablementStatusEnum.Enabled
      } else {
        EnablementStatusEnum.Disabled
      },
    )
  }

  // Execute the command on the device
  setOrUpdateEventDetectionTriggers(toUpdate)
}

録画モード

   Android 向けサンプルアプリに実装済み   

RecordingMode トレイトは、カメラやドアホン デバイスでの動画や画像の録画動作を管理するためのインターフェースを提供します。ユーザーは、連続録画、アクティビティ検出時の録画、録画の完全な無効化(ライブビューのみ)のいずれかを選択できます。

RecordingModeEnum は、使用可能な録画戦略を定義します。

モード 列挙値 説明
無効 Disabled 録画は完全に無効になっています。主に以前のデバイスで使用されます。
CVR(連続録画) Cvr 動画は 24 時間 365 日録画されます。サブスクリプション(Google Home Premium など)が必要です。
EBR(アクティビティ検出時の録画) Ebr 録画はイベント(人物、動き)によってトリガーされます。 動画の長さは、アクティビティの長さと定期購入によって異なります。
ETR(アクティビティ検出時の録画) Etr イベントによってトリガーされる短いプレビュー録画(10 秒など)。
ライブビュー LiveView 録画は無効になりますが、ユーザーは引き続きライブ配信にアクセスできます。
静止画像 Images アクティビティが発生すると、動画ではなくスナップショットが記録されます。

録画モードを確認する

現在の録画構成を表示するには、RecordingMode トレイトの属性を確認します。

// 1. Obtain the trait flow from the device
private val recordingModeTraitFlow: Flow =
    device.traitFromType(RecordingMode, CAMERA_TYPES.first { device.has(it) })

// 2. Map the flow to recording mode options
data class RecordingModeOptions(
    val recordingMode: RecordingModeTrait.RecordingModeEnum,
    val index: Int,
    val available: Boolean,
    val readableString: String,
)

private val recordingModeOptions: Flow<List> =
    recordingModeTraitFlow.map { trait ->
        val supported = trait.supportedRecordingModes?.map { it.recordingMode } ?: emptyList()
        val available = trait.availableRecordingModes?.map { it.toInt() } ?: emptyList()

        supported.withIndex().map { (index, mode) ->
            RecordingModeOptions(
                recordingMode = mode,
                index = index,
                available = available.contains(index),
                readableString = mode.toReadableString(),
            )
        }
    }

録画モードを変更する

更新する前に、supportedRecordingModes 属性から選択したインデックスが availableRecordingModes 属性に存在することを確認してください。

選択したモードを更新するには、setSelectedRecordingMode 関数を使用して、選択したモードのインデックスを渡します。

private suspend fun RecordingMode.updateRecordingMode(index: Int) {
    // Execute the command to update the selected mode
    this.setSelectedRecordingMode(index.toUByte())
}

その他の設定

その他のさまざまな設定は、Home API を介して制御できます。

画像の向きを変更する

カメラの画像(動画)の向きを回転させることができます。動画は 180 度しか回転できません。

カメラの画像の向きを変更するには、組み込みの setImageRotation Kotlin 関数を使用して、CameraAvStreamManagement トレイトの imageRotation 属性を更新します。

// Change the camera's image orientation
val isRotated = false

cameraAvStreamManagement.update { setImageRotation(if (isRotated) 180.toUShort() else 0.toUShort()) }

ナイトビジョン機能をオンまたはオフにする

カメラのナイトビジョンをオンまたはオフにするには、TriStateAutoEnum を使用して、組み込みの setNightVision Kotlin 関数を使用して CameraAvStreamManagement トレイトの nightVision 属性を更新します。

// Turn night vision on
cameraAvStreamManagement.update {
  setNightVision(CameraAvStreamManagementTrait.TriStateAutoEnum.On)
}

// Turn night vision off
CameraAvStreamManagement.update {
  setNightVision(CameraAvStreamManagementTrait.TriStateAutoEnum.Off)
}

ステータス LED の明るさを変更する

ステータス LED の明るさを変更するには、ThreeLevelAutoEnum を使用し、組み込みの setStatusLightBrightness Kotlin 関数を使用して CameraAvStreamManagement トレイトの statusLightBrightness 属性を更新します。

// Set the LED brightness to high
cameraAvStreamManagement.update {
  setStatusLightBrightness(CameraAvStreamManagementTrait.ThreeLevelAutoEnum.High)
}

// Set the LED brightness to low
cameraAvStreamManagement.update {
  setStatusLightBrightness(CameraAvStreamManagementTrait.ThreeLevelAutoEnum.Low)
}

カメラのビューポートを変更する

カメラのビューポートは、Google Nest カメラの動画でズーム機能や補正機能を使用するのサポート記事に記載されているズームと切り抜き機能と同じです。

ビューポートは、4 つの値を含む ViewportStruct で定義されます。これらの値は、ビューポートの座標として使用されます。座標は次のように定義されます。

(x1,y1) -- (x2,y1)
   |          |
(x1,y2) -- (x2,y2)

ViewportStruct の値は、アプリの UI とカメラの実装によって異なります。ごく基本的なレベルでは、カメラ動画のビューポートを設定するには、組み込みの setViewport Kotlin 関数を使用して、CameraAvStreamManagement トレイトの viewport 属性を ViewportStruct で更新します。

cameraAvStreamManagement
  .update { setViewport(
    CameraAvStreamManagementTrait.ViewportStruct(
      x1 = horizontalRange.rangeStart.roundToInt().toUShort(),
      x2 = horizontalRange.rangeEnd.roundToInt().toUShort(),
      y1 = verticalRange.rangeStart.roundToInt().toUShort(),
      y2 = verticalRange.rangeEnd.roundToInt().toUShort(),
    )
) }

アナリティクスを有効または無効にする

各デバイスは、詳細な分析データを Google Home クラウドに送信するように個別にオプトインできます(Home APIs の Cloud Monitoring をご覧ください)。

デバイスのアナリティクスを有効にするには、ExtendedGeneralDiagnosticsTrait の analyticsEnabled プロパティを true に設定します。analyticsEnabled を設定すると、別のプロパティ logUploadEnabled が自動的に true に設定され、分析ログファイルを Google Home クラウドにアップロードできるようになります。

// Enable analytics
extendedGeneralDiagnostics.update {
  setAnalyticsEnabled(true)
}

// Disable analytics
extendedGeneralDiagnostics.update {
  setAnalyticsEnabled(false)
}

トランスポートと録画の構成

このセクションでは、カメラ ストリームの品質とイベントのトリガーに関連する設定について説明します。これらの設定は、PushAvStreamTransport トレイトによって管理されます。

トランスポート設定を読み取る

このセクションでは、カメラまたはドアホン デバイスから現在の構成を取得する方法について説明します。PushAvStreamTransport トレイトを取得し、録画に使用されている特定の接続を見つけて、帯域幅の品質、復帰感度、最大イベント長の現在の値を抽出します。

val trait: PushAvStreamTransport = device.getTrait(PushAvStreamTransport)
val connections = trait.findTransport().transportConfigurations

// Locate the connection designated for recording
val recordingConnection = connections.firstOrNull {
    it.transportOptions.getOrNull()?.streamUsage == StreamUsageEnum.Recording
}

val options = recordingConnection?.transportOptions?.getOrNull()

// 1. Bandwidth Quality (Video Stream ID)
val videoStreamId = options?.videoStreamId?.getOrNull()

// 2. Wake-up Sensitivity (Motion Sensitivity)
val wakeUpSensitivity = options?.triggerOptions?.motionSensitivity?.getOrNull()

// 3. Max Event Length (Motion Trigger Time Control)
val maxEventLength = options?.triggerOptions?.motionTimeControl?.getOrNull()?.maxDuration

トランスポート設定を更新する

このセクションでは、トランスポート設定を変更する方法について説明します。新しい値を含む新しい TransportOptionsStruct を作成し、modifyPushTransport コマンドを使用して、更新された設定をデバイスに送信し、前のステップで見つかった録音接続に適用します。

これらの設定を変更するには、TransportOptionsStruct を指定して modifyPushTransport コマンドを使用します。

val toUpdate = TransportOptionsStruct(
    videoStreamId = OptionalValue.present(2u), // e.g., Max Quality
    triggerOptions = TransportTriggerOptionsStruct(
        motionSensitivity = OptionalValue.present(5u), // e.g., Medium
        motionTimeControl = OptionalValue.present(
            TransportMotionTriggerTimeControlStruct(maxDuration = 30u)
        )
    )
)

if (recordingConnection != null) {
    trait.modifyPushTransport(
        connectionId = recordingConnection.connectionId,
        transportOptions = toUpdate
    )
}

帯域幅の品質を判断する

TransportOptionsStruct の videoStreamId プロパティは、特定の動画ストリーム構成に対応しています。

サポートされている動画ストリームを取得するには、VideoStreamStructs のリストである allocatedVideoStreams 属性を参照してください。デバイスの CameraAvStreamManagement トレイトから。

デバイスの復帰感度を調整する

TransportTriggerOptionsStruct の motionSensitivity プロパティは、次の値に対応しています。

ラベル 値(UByte)
低 1u
中 5u
高 10u

最長アクティビティ時間を調整する

TransportMotionTriggerTimeControlStruct の maxDuration プロパティは、次の期間(秒単位)に対応しています。

  • 10u、15u、30u、60u、120u、180u

デバイスをデコミッションする

ストラクチャからカメラを削除するには、device.decommissionDevice() を呼び出します。廃止すると、Google が管理する確認画面が表示され、録画された連続動画履歴、アクティビティの履歴、クリップ、認識済みの人物ライブラリのデータがすべて、ストラクチャ全体で完全に削除されることが警告されます。

詳細な手順とコード例については、デバイスを削除するをご覧ください。