Android で家を初期化する

Android 用 Home API を使用する前に、アプリでホームを初期化する必要があります。このステップでは、ローカル コンテキストの Homeシングルトン インスタンスを作成します。

一度にアクティブにする Home のインスタンスは 1 つのみにしてください。

これは Home API のエントリ ポイントであり、Device & Structure API と Automation API で使用するトレイトとデバイスタイプを宣言することも含まれます。Google Home エコシステムを使い始めたばかりで、登録する特性やデバイスタイプがわからない場合は、このガイドで一般的な特性やデバイスタイプをいくつかご紹介しています。

Home インスタンスを作成する

まず、次のパッケージをアプリにインポートします。

import android.content.Context
import com.google.home.FactoryRegistry
import com.google.home.HomeConfig
import com.google.home.Home

Home API を初期化するには:

  1. Application コンテキストへの参照を取得します。このコンテキストはアクティビティのライフサイクルに依存せず、アプリが動作している限り存続します。Activity または Service 内で getApplicationContext() を呼び出すことで取得できます。

    val context = getApplicationContext()
    
  2. アプリで使用するすべてのトレイトとデバイスタイプを含む FactoryRegistry インスタンスを作成します。

    このガイドでは、必要なものがわからない場合に備えて、一般的なもの(照明、電源、センサー、スイッチ、サーモスタットのデバイスタイプ、自動化のプレゼンスとアシスタントのトレイト)をいくつか提案しています。詳しくは、トレイトとデバイスタイプの登録をご覧ください。

    val registry = FactoryRegistry(
      traits = listOf(
                AirQuality,
                AreaAttendanceState,
                AreaPresenceState,
                AssistantBroadcast,
                AssistantFulfillment,
                BooleanState,
                ColorControl,
                ExtendedColorControl,
                FlowMeasurement,
                IlluminanceMeasurement,
                LevelControl,
                Notification,
                OccupancySensing,
                OnOff,
                RelativeHumidityMeasurement,
                Switch,
                TemperatureMeasurement,
                Thermostat),
      types = listOf(
                AirQualitySensorDevice,
                ColorDimmerSwitchDevice,
                ColorTemperatureLightDevice,
                ContactSensorDevice,
                DimmableLightDevice,
                DimmablePlugInUnitDevice,
                DimmerSwitchDevice,
                ExtendedColorLightDevice,
                FlowSensorDevice,
                GenericSwitchDevice,
                HumiditySensorDevice,
                LightSensorDevice,
                OccupancySensorDevice,
                OnOffLightDevice,
                OnOffLightSwitchDevice,
                OnOffPluginUnitDevice,
                OnOffSensorDevice,
                SpeakerDevice,
                TemperatureSensorDevice,
                ThermostatDevice))
    

    ここで登録された個々のトレイトとデバイスタイプのインポート文が必要です(Android Studio で追加を求めるプロンプトが表示されます)。

  3. Dispatchers.IO コルーチン コンテキストとレジストリ インスタンスを使用して HomeConfig をインスタンス化します。

    val homeConfig = HomeConfig(
            coroutineContext = Dispatchers.IO,
            factoryRegistry = registry)
    
  4. 最後に、コンテキストと HomeConfig を使用して、API のエントリ ポイントである Homeシングルトン インスタンスを作成します。

    val homeManager: HomeClient = Home.getClient(context, homeConfig)
    

無効なセッションのエラーを回避するには、オブジェクト宣言でラップして、Home のシングルトン インスタンスのみを作成することが重要です。

たとえば、サンプルアプリでは次のように行っています。

internal object HomeClientModule {
  @Provides
  @Singleton
  fun provideHomeClient(@ApplicationContext context: Context): HomeClient {
    return Home.getClient(
      context,
      HomeConfig(
        coroutineContext = IODispatcherModule.provideIoDispatcher(),
        factoryRegistry = registry,
      ),
    )
  }
}

アプリを起点とする Google ログイン

アプリ内でユーザーの Google 認証を管理したい場合があります。そうすることで、Google Home、ドライブ、マップなどのさまざまな Google サービスで同じユーザー アカウントを使用できます。

アプリから開始される Google ログインを使用すると、特定のユーザーに明示的に関連付けられた HomeClient インスタンスを取得できます。これにより、アカウントがすでに承認されている場合は、Google アカウント選択ツールと同意画面がバイパスされます。

また、このアプローチでは、アプリのログイン画面と Google Home の画面という 2 つのアカウント選択画面がユーザーに表示されるのを防ぐことができます。

これを行うには、「Google でログイン」を使用してユーザーを認証するを参照し、次の手順を完了する必要があります。

OAuth ウェブ アプリケーション クライアント ID を作成する

  1. Google Cloud コンソールを開きます。
    • Google Cloud コンソールの [認証情報] ページに移動します。
    • 既存のプロジェクトを選択するか、新しいプロジェクトを作成します。
  2. OAuth 同意画面を構成する(まだ構成していない場合)
    • 認証情報を作成する前に、OAuth 同意画面が、プライバシー ポリシーと利用規約の URL を含むアプリの詳細で構成されていることを確認してください。
  3. OAuth クライアント ID(ウェブ アプリケーション タイプ)を作成する
    • [認証情報] ページで + CREATE CREDENTIALS をクリックし、プルダウン メニューから [OAuth クライアント ID] を選択します。
    • [アプリケーションの種類] で [ウェブ アプリケーション] を選択します。
    • ウェブ クライアントの名前を入力します(例: 「My App Web Backend」)。
    • [作成] をクリックします。
  4. クライアント ID を取得する
    • 作成後、コンソールに新しいクライアント ID が表示されます。これは、Android アプリケーションで使用する値です(例: 「{project number}-.....apps.googleusercontent.com」)。
    • クライアント ID は、直接ハードコードするのではなく、外部(build.gradle など)に保存することをおすすめします。

Google ログイン リクエストをインスタンス化する

ウェブアプリ ID を使用して Google ログイン リクエストを作成します。

// Your Google Cloud console Web Client ID for Google Sign-In
val serverClientId = BuildConfig.DEFAULT_WEB_CLIENT_ID

// Build the request for Google ID token
val googleIdOption = GetGoogleIdOption.Builder()
    .setFilterByAuthorizedAccounts(false) // Show all Google Accounts on the device
    .setServerClientId(serverClientId) // embed WebClientID in token
    .build()

// Build the GetCredentialRequest
val request = GetCredentialRequest.Builder().addCredentialOption(googleIdOption).build()

「Google でログイン」フローを作成する

ログインフローを実装するには、CredentialManager を使用して Sign in with Google リクエストを実行します。ユーザーがアカウントを選択したら、結果の Google ID トークンからメールアドレスを抽出し、android.accounts.Account を作成します。このアカウントは、ログインしたユーザーに固有の HomeClient インスタンスを初期化するために使用されます。

  try {
    // CredentialManager is responsible for interacting with various credential providers on the device
    val credentialManager = CredentialManager.create(context)
    // Credential returns when user has selected an account and the getCredential call completes
    val result = credentialManager.getCredential(context = context, request = request)
    val credential = result.credential

    if (
      credential is CustomCredential &&
      credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL
    ) {
      try {
        val googleCredential = GoogleIdTokenCredential.createFrom(credential.data)
        googleCredential.id.let { userEmail ->
          Log.i(TAG, "Email found in Google ID Token: $email")
          /*
           Why "com.google"?
           The string "com.google" is a standard identifier used in Android's android.accounts.
           Account system to represent accounts managed by Google. This is often used when
           interacting with Android's Account Manager or when using Google-specific APIs. So,
           even if the email ends in "@gmail.com", the underlying account type or provider is
           still considered "com.google" within the Android system.
          */
          val account = Account(userEmail, "com.google")
          Log.d(TAG,"Switched account to : $userEmail")
          // Get the new Home Client Instance with the userEmail
        }
        Log.i(TAG, "Account switch complete. Emitting navigation event.")
      } catch (e: Exception) {
        Log.e(TAG,"Could not convert CustomCredential to Google ID Token", e)
      }
    }
  } catch (e: Exception) {
    Log.e(TAG, "Google Sign-In failed with unexpected error", e)
  }

新しい HomeClient インスタンスを取得する

Home インスタンスを作成するで説明されている手順に沿って操作しますが、ステップ 4 で Home.getClient(context, homeConfig) を呼び出す代わりに、Home.getClient(context, userAccount, homeConfig) を呼び出します。ここで、2 番目のパラメータは Lazy<UserAccount> です。これにより、指定された Google アカウントに明示的に関連付けられた HomeClient のサブクラスである HomeClientWithProvidedAccount のインスタンスが返されます。

val client =
     Home.getClient(
       context = context.applicationContext,
       account =
         lazy {
         // 1. Create the Account object.
           val androidAccount = Account(userEmail,
                                        GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE)
         // 2. Wrap it in UserAccount.GoogleAccount.
           UserAccount.GoogleAccount(androidAccount)
         },
       homeConfig = HomeConfig()
     )

指定されたユーザーが承認されていない場合は、HomeClientWithProvidedAccount インスタンスで次のメソッドを呼び出して、ユーザーに権限を求めるメッセージを表示します。

  1. 使用する ActivityResultCaller への参照を含む registerActivityResultCallerForPermissions()
  2. requestPermissions()。これにより、GHP 同意画面が表示され、ユーザーは権限を付与できます。

UserAccount を使用して HomeClient を作成し、forceLaunch==true を使用して requestPermissions() を呼び出して同意画面を再度起動し、ユーザーが権限付与を更新できるようにします。

val client =
     Home.getClient(
       context = context.applicationContext,
       account =
         lazy {
              UserAccount.GoogleAccount(androidAccount)
         },
       homeConfig = HomeConfig()
     )

client.registerActivityResultCallerForPermissions(this)
client.requestPermissions(forceLaunch= true)

Home API の権限の管理について詳しくは、Permissions API をご覧ください。

新しい HomeClient でアクティビティ全体を更新

新しい HomeClient インスタンスを取得したら、アクティビティ全体を更新して再登録し、このユーザー アカウントに関連付けられた完全な構造、デバイス、その他の関連データを取得する必要があります。

トレイトとデバイスタイプの登録

FactoryRegistry クラスを使用すると、アプリで使用される特性とデバイスタイプを明示的に指定できるため、アプリのバイナリサイズを最適化できます。

権限とファクトリー レジストリは分離されています。そのため、権限を使用してアプリで利用できるものの、ファクトリー レジストリに含まれていない未登録のトレイトと型は、Automation API を使用してアクセスすることはできません。また、一括 traits() メソッド呼び出しや types() メソッド呼び出しで返されることもありません。