GPSマルチユニットSORACOM Edition を使って、自宅のドアの鍵を開けよう。

# はじめに
自宅に近づいたらドアの鍵が自動で開いたらいいなとの思いから開発しました。
# 構成図
![bb86a9df4fe6d2a11df6_01.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2297743/6650fa29-60e3-96e9-4d4e-74f8f9a491c0.png)
# 解説
1. みちびきからGPS情報を取得
1. GPSマルチユニットで受信し、SORACOMに送信
1. 受け取った情報から、SORACOM Funkを起動
1. SORACOM Funkから、AWS Lambdaを実行
1. Lambdaで受け取ったGPS情報から、Amazon Location Service [Places](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/location.html#LocationService.Client.search_place_index_for_position)と[Tracking](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/location.html#LocationService.Client.batch_update_device_position)を起動
1. GPS情報を元に、Placesで可視化
1. GPS情報を元に、Trackingに登録
1. Trackingで取得した情報を元に、Geofencingを用いて自宅周辺の地理的境界を判定
1. 地理的境界に入った場合、EventBridgeでLambdaを起動
1. Lambdaで、SESAMEとNature RemoのAPIにリクエスト
1. APIリクエストを元に、SESAMEがドアの鍵を開け、Nature Remoがリビングの電気ONを実行
1. 作業用PCでコード修正し、GitHubにPush
1. GitHubのPushから、CircleCIが起動
1. CircleCIでLambdaにコードをデプロイ
1. CircleCIのデプロイ成否をSlackに通知

#GPS マルチユニット SORACOM Editionについて
ユーザーガイドが詳しいです。

https://users.soracom.io/ja-jp/guides/iot-devices/gps-multiunit/

#SORACOM Funkから、AWS Lambdaが呼び出される時にeventに連携されるJSON

https://users.soracom.io/ja-jp/guides/iot-devices/gps-multiunit/payload/

```
{
    "lat": 35.6892712,
    "lon": 139.6927462,
    "bat": 3,
    "rs": 0,
    "temp": 27.5,
    "humi": 63,
    "x": -64,
    "y": 832,
    "z": 576,
    "type": 0
}
```

#受け取ったJSONの経度緯度情報から、現在地の地名をAmazon Location Place indexesで検索

Amazon Location Place indexesに、Nameを入力し作成してください。このNameはあとで使用します。

![bb86a9df4fe6d2a11df6_02.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2297743/7bcefb45-574a-1591-20e2-8bb2b1675d56.png)

以下のドキュメントを元に、簡単なコードを書くとこのようになります。Boto3は最新版にしてください。

https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/location.html#LocationService.Client.search_place_index_for_position

```
# -*- coding:utf-8 -*-
import json
import boto3

location = boto3.client('location')

# GPSユニットから受信したJSONとする。
event = {
    "lat": 35.6892712,
    "lon": 139.6927462,
    "bat": 3,
    "rs": 0,
    "temp": 27.5,
    "humi": 63,
    "x": -64,
    "y": 832,
    "z": 576,
    "type": 0
}

# 先に作成したPlace indexesの、Nameを使用します。
response = location.search_place_index_for_position(
    IndexName = 'xxx',
    Language = 'ja',
    MaxResults = 1,
    Position = [event['lon'],event['lat']]
)
print(json.dumps(response, ensure_ascii = False))
```

緯度経度情報から取得した情報です。日本語名で取得できました。

```
{
    "ResponseMetadata": {
        "RequestId": "d83ddd7a-bb15-4e67-81a5-bb20f33ecc35",
        "HTTPStatusCode": 200,
        "HTTPHeaders": {
            "date": "Sat, 22 Jan 2022 06:57:18 GMT",
            "content-type": "application/json",
            "content-length": "387",
            "connection": "keep-alive",
            "x-amzn-requestid": "d83ddd7a-bb15-4e67-81a5-bb20f33ecc35",
            "access-control-allow-origin": "*",
            "x-amz-apigw-id": "MVegUH8sNjMF0tg=",
            "access-control-expose-headers": "x-amzn-errortype,x-amzn-requestid,x-amzn-errormessage,x-amzn-trace-id,x-amz-apigw-id,date",
            "x-amzn-trace-id": "Root=1-61ebaace-151333031b17ac202e457aa6"
        },
        "RetryAttempts": 0
    },
    "Results": [
        {
            "Distance": 97.11527157871623,
            "Place": {
                "AddressNumber": "１",
                "Country": "JPN",
                "Geometry": {
                    "Point": [
                        139.69170523793048,
                        35.689486328367835
                    ]
                },
                "Interpolated": false,
                "Label": "東京都新宿区西新宿２-８-１",
                "Neighborhood": "２",
                "PostalCode": "1600023",
                "Region": "東京都"
            }
        }
    ],
    "Summary": {
        "DataSource": "Esri",
        "Language": "ja",
        "MaxResults": 1,
        "Position": [
            139.6927462,
            35.6892712
        ]
    }
}
```

#経度緯度情報から、ある地点間の直線距離を測定する

```
from math import sin, cos, acos, radians
earth_rad = 6378.137

def latlng_to_xyz(lat, lng):
    rlat, rlng = radians(lat), radians(lng)
    coslat = cos(rlat)
    return coslat*cos(rlng), coslat*sin(rlng), sin(rlat)

def dist_on_sphere(pos0, pos1, radius=earth_rad):
    xyz0, xyz1 = latlng_to_xyz(*pos0), latlng_to_xyz(*pos1)
    return acos(sum(x * y for x, y in zip(xyz0, xyz1)))*radius

# 呼び出すとkm(キロメートル)
# position(計測地点) = 経度(lat), 緯度(lon)
# unit(GPSマルチユニット位置) = 経度(lat), 緯度(lon)
km = dist_on_sphere(position, unit)
```