AWS IoT ルールアクションで AWS IoT Core Device Location を使ってデバイスの IP アドレスから位置情報を取得してみた

2022.11.30

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

概要

先日リリースされた新機能である「AWS IoT Core Device Location」は、コンソール画面から試してみることができますが、今回は SDK で使ってみました。

位置情報を取得するための諸元となるデータは複数種類ありますが、今回は一番簡単な 「IP アドレス」で試してみたいと思います。

構成

今回は、次のような構成を想定しています。
実際にはデバイスを用意できていないので、ダミーデータを元に試すことにします。

01-diagram-for-device-location

この構成のポイントとしては、IoT ルールの SQL ステートメント内で Lambda を実行している点です。
単純に考えれば、IoT ルールの「Lambda アクション」で位置情報を取得してから他のサービスへ転送する方法がオーソドックスかと思いますが、今回はルールアクション内で位置情報を取得したかったので、このようにしています。

そして Lambda の実行結果として得られた位置情報を「CloudWatch Logs アクション」のインプットとして利用しています。(インプットといいつつ、宛先が CloudWatch Logs なので単にログとして記録されるだけですが…)

Lambda 関数の作成

前提として、この Lambda 関数で受け取るデータは、デバイスから送られてくる次のようなものを想定しています。

{
	"DeviceId": "SampleThing01",
	"Timestamp": 1669710470,
	"Ip": "54.240.198.35"
}

コードは下記になります。
やっていることは単純で、get_position_estimate() で Solver に IP アドレスとタイムスタンプを送って、推定位置情報を GeoJSON 形式で取得しています。

import boto3
import json

iotwireless = boto3.client('iotwireless')

def my_iotwireless(ip,timestamp):
    response = iotwireless.get_position_estimate(
        Ip={
            'IpAddress': ip
        },
        Timestamp=timestamp,
    )
    return json.loads(response['GeoJsonPayload'].read().decode())

def lambda_handler(event, context):
    ip = event['Ip']
    deviceid = event['DeviceId']
    timestamp = event['Timestamp']
    long = my_iotwireless(ip,timestamp).get('coordinates')[0]
    lat = my_iotwireless(ip,timestamp).get('coordinates')[1]

    resp_json = {
        'long': long,
        'lat': lat,
        'deviceid': deviceid,
        'timestamp': timestamp
    }

    print(json.dumps(resp_json))
    return resp_json

なお、GetPositionEstimate はリリースされた直後の新しい API であるため 2022 年 11 月 29 日現在では、Lambda 標準の Boto3 には含まれていません。
そのため最新バージョンである 1.26.17 をパッケージングして使う必要があったので、AWS SAM の requirements.txtに下記を記載してデプロイしています。

boto3 >= 1.26.17

関連ドキュメント・サイト

「IP 逆引きソルバー」のペイロード例が下記に記載されています。今回は、ダミーデータをペイロード例にあるフォーマットに変換して「 GetPositionEstimate API 」に渡しています。

「GetPositionEstimate API」のリファレンスは下記になります。

Boto3 のget_position_estimate() に関するドキュメントは以下になります。

IoT ルールの作成

  • SQL Statement

02-iot-rule-sql

今回は SELECT 句で Lambda 関数を実行して、取得した位置情報を ルールアクションで呼び出した後段のサービスに渡します。

SELECT aws_lambda("[Lambda Function ARN]", *) as payload 
FROM '[Your Topic Filter]'

今回はルールアクションを CloudWatch Logs としています。
ロググループを新規作成するか既存のものを指定して下さい。

03-iot-rule-cw-action

Lambda に付与する IAM 権限

「GetPositionEstimate API」を使うために以下の内容で権限を追加します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "iotwireless:GetPositionEstimate",
            "Resource": "*"
        }
    ]
}

動作確認してみる

それでは適当な クライアント から下記のデータを publish してみます。

{
	"DeviceId": "SampleThing01",
	"Timestamp": 1669710470,
	"Ip": "54.240.198.35"
}

今回も「MQTT X」で Publish しました。

06-mqttx-publish

IoT ルールで呼び出された CloudWatch Logs のログを見ると、IP アドレスから取得された地理情報が格納されていました。

05-cw-logs-action-log

{
    "payload": {
        "long": -122.29940032958984,
        "lat": 47.6859016418457,
        "deviceid": "SampleThing01",
        "timestamp": 1669785833
    }
}

Lambda のログからも確認できました。

04-lambda-cw-logs

{
    "long": -122.29940032958984,
    "lat": 47.6859016418457,
    "deviceid": "SampleThing01",
    "timestamp": 1669785833
}

最後に

本当は取得した地理情報を Amazon Location Service に渡したかったのですが、うまく渡せなかったのでそちらは改めて検証してみたいと思います。

現状では、何らかの形で API を叩いて地理情報を取得する必要がありますが、GPS デバイスなどが無くても地理情報を簡単に取得できる手段が増えたのは嬉しい機能かと思います。

今回は以上です。