Alexa センシングAPI（Entity Sensing Extension）をスキルに追加するには

# はじめに

Alexaの首振りデバイスである、Echo Show 10 が日本でも発売されたので、プライマリーユーザーを追尾（首振り）するモーションAPIを試してみた。

# センシングAPIとは

Echo Show 10などのスクリーンが自動的に回転する、センシング対応のデバイスを制御するためのAPIで、`APL Extensions` で制御する。`Extensions`は、APLランタイムに追加のデータソース、コマンド、イベントハンドラーを提供する拡張機能で、APL 1.4 以上で使用可能となっている。

スキルマニフェストとAPLドキュメントの双方に設定が必要。

<img width="40%" border="1" alt="Echo Show 10" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/29b4ef27-4273-1ff1-1284-45bc5ac56065.png">

# どんな事ができるのか

- ユーザーを検出したかどうかを判断するためのプロパティを取得する。（<b>ライブデータプロパティ</b>）
- デバイスを基準としたユーザーの位置を把握する。（<b>ライブデータプロパティ</b>）
- <b>イベントハンドラー</b>を使用して、デバイスの状態の変化を検知する。

# 注意

以下のような場合、デバイスが検出を行えない。

- 物理的な障害物がある。
- 光が少ない。
- Echoデバイスが「おやすみモード」になっている。
- Echoデバイスのカメラレンズが閉じられている。


# Entity Sensing extension を有効にするには

skill.json の `interfaces` に追加する。
このExtentionは `autoInitializedExtensions` プロパティで設定する項目はない。

```
        "interfaces": [
          {
            "type": "ALEXA_PRESENTATION_APL"
          },
          {
            "type": "ALEXA_EXTENSION",
            "requestedExtensions": [
              {
                "uri": "alexaext:entitysensing:10"
              }
            ]
          }
        ]
```

Alexa Developer Console で設定する場合は、メニューから `ビルド` `スロットタイプ` `インターフェース` と辿り `Alexa Extensions` で、使用するエクステンションをチェックする。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/8d444a84-f466-35ce-dedb-81b84b31c0d4.png)


# APLドキュメントの記述

`Smart Motion extension` はAPLで制御するため、スキルにAPLを追加する必要がある。

### extensions

まず、`extensions` で `Entity Sensing extension` を使用できるようにする。
以下の例では `EntitySensing` という名前（任意）を割り当てたので、以降の操作は `EntitySensing` に対して行うことになる。

```
{
  "type": "APL",
  "version": "1.5",
  "theme": "dark",
  "extensions": [
    {
      "name": "EntitySensing",
      "uri": "alexaext:entitysensing:10"
    }
  ],
```

### settings

`settings` には、`entitySensingStateName` と `primaryUserName` が設定できる。設定方法は以下。

> settings.AssignedName

`AssignedName` は、extensionsで `Entity Sensing extension` に割り当てた名前。ここでは`EntitySensing`がそれ。この `EntitySensing` にプロパティ`entitySensingStateName` および `primaryUserName` を設定することで情報を取得できるようになる。

```
  "settings": {
    "EntitySensing": {                                     ←---  AssignedName
      "entitySensingStateName": "MyEntitySensingState",　　←---  データバインディングコンテキスト（※1）
      "primaryUserName": "MyPrimaryUser"                 　←---  データバインディングコンテキスト（※1）
    }
  },
```
※1 `Entity Sensing extension` では、データバインディングコンテキストに、それぞれライブデータオブジェクト `EntitySensingState` と `PrimaryUser` が追加される。 `EntitySensingState`は、デバイスの現在のEntity Sensing機能を表すオブジェクト。`PrimaryUser`は、検出されたユーザーのうち、デバイスとのエンゲージメントが最も高いユーザーが格納されるオブジェクトで、`EntitySensingState` と `PrimaryUser` に含まれるプロパティがライブデータプロパティである

<b>entitySensingStateName</b> に名前（任意）を設定することで、`EntitySensingState`にアクセス可能となる。この設定そのものが無かったっり、設定値が""の場合は、ライブデータプロパティにはアクセスできないので注意。ライブデータプロパティは、APLドキュメントのライフサイクル中に変更できるデータオブジェクト。

<b>EntitySensingStateプロパティ</b>

|No.|プロパティ名|型|動作|
|:--|:--|:--|:--|
|1.|error|文字|読み取り可能なエラーメッセージ。エラーがない場合は""。|
|2.|errorCode|数値|エラーコード。エラーがない場合は0。|

<b>primaryUserName</b> に名前（任意）を設定することで、`PrimaryUser`にアクセス可能となる。条件は`entitySensingStateName` と同じ。


<b>PrimaryUserプロパティ</b>

|No.|プロパティ名|型|動作|
|:--|:--|:--|:--|
|1.|id|文字|デバイス割り当てのエンティティID。ユーザーが検出されない場合、文字列は空です。|
|2.|isActive|ブール値|ユーザーがウェイクワードを発話したことを示します。|
|3.|isSeen|ブール値|デバイスがユーザーを検出したことを示します。|
|4.|poise|オブジェクト|ユーザーの位置。|

> <b>APL Extensionのライブデータ</b>
> APL Extensionsは、ドキュメントのグローバルデータバインディングコンテキストに、カスタムの「ライブ」データオブジェクトを追加できます。ドキュメント読み込み時のextensionの静的な状態のみを報告するenvironment.extension環境プロパティと異なり、これらの「ライブ」データオブジェクトは、ドキュメントのライフサイクルを通じ、extensionのユースケースに応じて変化します。

> <b>データバインディングコンテキスト</b>
> データバインディングを目的としてAPLドキュメントで使用できるデータソースとデータのコレクションです。データはキーと値のペアで構成され、APLドキュメント、デバイスのviewportに関する情報（viewport.widthなど）、デバイスの機能、環境情報（environment.aplVersionなど）に付随するデータソースを含みます。

### イベントハンドラー

<b>イベントハンドラー</b>には `OnEntitySensingStateChanged` と `OnPrimaryUserChanged` が設定できる。 それぞれ`EntitySensingState` と `PrimaryUser` に変化があった場合に実行される。

> AssignedName:EventHandlerName

`AssignedName` には、extensionsで設定した名前 `EntitySensing` を設定し、EventHandlerNameにはハンドラーの名前`OnEntitySensingStateChanged` を設定し `EntitySensing:OnEntitySensingStateChanged` とする。イベントが発生した時に `"SetValue"` 等で値を変更する。その際 `"componentId"` で対象のコンポーネントと関連づけ、`"property"` で値を紐づける。

|No.|ハンドラ名|動作|
|:--|:--|:--|
|1.|OnEntitySensingStateChanged|EntitySensingStateのプロパティが変更された。|
|2.|OnPrimaryUserChanged|PrimaryUserプロパティが変更された（<b>Poise</b>を除く）。新しいPrimaryUserを検出し割り当てた。既存のPrimaryUserを検出しなくなった。|


```イベントハンドラー
  "EntitySensing:OnEntitySensingStateChanged": [　　　←---　ハンドラ
    {
      "type": "SetValue",
      "componentId": "mainContainerId",　　　         ←---　コンポーネントID
      "property": "SensingStatus",            　　    ←---　プロパティ
      "value": "${event.changed.errorCode == 0 ? 'Active' : 'Faulted'}"
    }
  ],
  "EntitySensing:OnPrimaryUserChanged": [　　       　←---　ハンドラ
    {
      "type": "SetValue",
      "componentId": "mainContainerId",              ←---　コンポーネントID
      "property": "UserStatus",     　　             ←---　プロパティ
      "value": "${event.changed.id}"
    }
  ],
  "mainTemplate": {
    "items": [
      {
        "type": "Container",
        "id": "mainContainerId",　　       　         ←---　コンポーネントID
        "bind": [
          {
            "name": "SensingStatus",     　     　    ←---　プロパティ
            "value": ""
          },
          {
            "name": "UserStatus",     　　            ←---　プロパティ
            "value": ""
          }
        ],
      }
```

イベントオブジェクトの構造はこんな感じ。

```:マニュアルより転載
{
  "event": {
    "changed": {
        //変更されたプロパティのみ
    },
    "current": {
        //現在のすべてのプロパティ
    },
    "source": {
      "type": "Document",
      "handler": "OnPrimaryUserChanged",
      "id": null,
      "uid": null,
      "value": null
    }
  }
}
```

イベントオブジェクトの使い方。

```
  "when": "${event.changed.errorCode != 0}"
```


# コマンド

`Entity Sensing extension` には、extension<b>コマンド</b>は追加されていない。

# Entity Sensing Extensionの仕組み

![仕組み](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/5c57729d-13a4-183e-f8b7-0eac879faff1.png)


# 使い方

`APL Smart Motion extension` との組み合わせで使用するのがメインで、センシングAPIで捉えて、モーションAPIで動かすみたいな使い方になる。

# おわりに

動作はしていてほぼ期待どおりの動きにななっているものの、センシングの動きがいまいちわからない。`event.changed` に期待したタイミングで値が入ってくれないような気がするので、もう少し遊ん理解を深めたいと思う。

# 参考

- [APL Entity Sensing Extension（公式）](https://developer.amazon.com/ja-JP/docs/alexa/alexa-presentation-language/apl-ext-entity-sensing.html)
- [Alexa Presentation Language（APL）の概要](https://developer.amazon.com/ja-JP/docs/alexa/alexa-presentation-language/understand-apl.html)
- [Extensionのライブデータ](https://developer.amazon.com/ja-JP/docs/alexa/alexa-presentation-language/apl-extensions.html#extension-live-data)