Device Address APIを使って住所を取得する

  • 0
    いいね
  • 0
    コメント

    Alexaにデバイス(Echo)に登録されている住所、郵便番号、国を取得できる Device Address API が追加されました。
    これによって、ユーザーがSkillを実行したデバイスに紐付いている位置情報(住所、郵便番号、国)をSkill側から取得できるようになりました。
    今までは郵便番号しか持っていなかったため、細かい位置を知る方法はありませんでしたが、これで個々のSkillで住所を質問するようなIntentを用意する必要がなくなったかと思います。

    Echoに住所を紐付ける

    住所の情報を取得する場合は、事前にユーザーがAlexa Appでデバイスに住所情報を登録している必要があります。

    Alexa Appを開いて、SettingsDevicesから登録するデバイスを選択します。
    http://alexa.amazon.com/spa/index.html#cards

    スクリーンショット 2017-04-08 15.41.25.png

    デフォルトの状態だと郵便番号だけが登録されている状態ですので、右側のEditをクリックして入力していきます。
    入力した住所に合う候補があると選択することが出来きます。
    スクリーンショット 2017-04-08 16.56.07.png

    試しにエンパイア・ステート・ビルのの住所を入れてみました。

    デバイスの位置情報を取得するSkillを作成

    AmazonのデベロッパーコンソールからSkillの一覧を表示して、Add a New Skillをクリックして新規で追加します。
    https://developer.amazon.com/edw/home.html#/skills

    カスタムSkillを選択し、適当な名前を付けます。
    この例では Invocation Nameをaddress demoとして登録しました。
    スクリーンショット 2017-04-08 14.08.48.png

    Configurationの所に今回試す、Permissionsが追加されているので、Device Addressにチェックを付けてFull Addressを選びます。
    スクリーンショット 2017-04-08 15.50.20.png

    注意)
    ここで許可した情報を返すエンドポイントしか利用することが出来ません。
    FullAddressを許可したとしても、国、郵便番号のエンドポイントを叩いた場合、403 Forbiddenになります。

    Skillの実装

    デバイスに紐付けられた住所はSkillに送られてくるわけではなく、送られてきた情報を元に別のAPIを叩いて取得する必要があります。

    Alexaから送られてくるJSONは以下のような物

    {
      "session": {
        "new": true,
        "sessionId": "some session id",
        "user": {
          "userId": "amzn1.ask.account.some_user_id",
          "permissions": {
            "consentToken": "some_token"
          }
        },
        "application": {
          "applicationId": "amzn1.ask.skill.some_app_id"
        }
      },
      "version": "1.0",
      "request": {
        "locale": "en-US",
        "timestamp": "2017-04-08T05:52:11Z",
        "type": "LaunchRequest",
        "requestId": "amzn1.echo-api.request.some_request_id"
      },
      "context": {
        "AudioPlayer": {
          "playerActivity": "IDLE"
        },
        "System": {
          "device": {
            "deviceId": "amzn1.ask.device.some_device_id",
            "supportedInterfaces": {
              "AudioPlayer": {}
            }
          },
          "application": {
            "applicationId": "amzn1.ask.skill.some_app_id"
          },
          "user": {
            "userId": "amzn1.ask.account.some_user_id",
            "permissions": {
              "consentToken": "some_token"
            }
          },
          "apiEndpoint": "https://api.amazonalexa.com"
        }
      }
    }
    

    このJSONの中の 以下の項目を利用します

    JSONのキー 意味
    context.System.device.deviceId device id
    context.System.user.permissions.consentToken token
    context.System.apiEndpoint api endpoint

    https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/device-address-api

    住所全部を取得

    この情報を取得する場合のエンドポイントは以下の通りとなっています。

    /v1/devices/{deviceId}/settings/address
    

    これで、echosimから Open address demo と話しかけると、取得した住所を答えてくれます。
    APIを叩いて取得できたJSONはこんな情報

    {
      "addressLine1": "5th Ave",
      "addressLine2": null,
      "addressLine3": null,
      "districtOrCounty": "New York",
      "stateOrRegion": "NY",
      "city": "New York",
      "countryCode": "US",
      "postalCode": "10016"
    }
    

    カードの表示

    ユーザーが住所の提供を許可していない場合は、AlexaからのリクエストにconsentTokenが含まれてきません。
    そのような場合は、パーミッションを許可してねってカードが新たに用意されたので、Alexaに返すJSONにそのカードを指定してあげます。

    'card': {
        'type': 'AskForPermissionsConsent',
        'permissions': [
            'read::alexa:device:all:address'
        ]
    }
    

    国と郵便番号だけの場合は、read::alexa:device:all:address:country_and_postal_codeをpermissionsに指定します。

    こんな感じでAlexa Appにカードが表示されます。
    スクリーンショット 2017-04-08 16.29.44.png

    サンプルソース

    # -*- coding: utf-8 -*-
    from __future__ import print_function
    import os
    import json
    import urllib2
    
    
    def describe_device_address(api_host, device_id, access_token):
        req = urllib2.Request("{}/v1/devices/{}/settings/address".format(api_host, device_id))
        req.add_header("Authorization", "Bearer {}".format(access_token))
        response = urllib2.urlopen(req)
        if response.getcode() == 200:
            return json.loads(response.read())
        else:
            print(response.getcode())
            raise Exception(response.msg)
    
    def lambda_handler(event, context):
        print(event)
    
        api_host = event["context"]["System"]["apiEndpoint"]
        device_id = event["context"]["System"]["device"]["deviceId"]
        token = event["context"]["System"]["user"]["permissions"]["consentToken"]
    
        address = describe_device_address(api_host, device_id, token)
    
        print(address)
    
        speech_output = "country is {0}. zipcode is {1}. Address is {2} {3} {4} ".format(
            address["countryCode"],
            address["postalCode"],
            address["addressLine1"],
            address["city"],
            address["stateOrRegion"]
            )
        response = build_response(build_speechlet_response(speech_output))
    
        return response
    
    
    def build_speechlet_response(output):
        title = 'Address Demo'
    
        return {
            'outputSpeech': {
                'type': 'PlainText',
                'text': output
            },
            'card': {
                'type': 'AskForPermissionsConsent',
                'permissions': [
                    'read::alexa:device:all:address'
                ]
            },
            'reprompt': {
                'outputSpeech': {
                    'type': 'PlainText',
                    'text': title
                }
            },
            'shouldEndSession': True
        }
    
    
    def build_response(speechlet_response):
        return {
            'version': '1.0',
            'response': speechlet_response
        }