AWS IoTでフリートインデックスを使って、シャドウの状態によるモノのクエリを試したいと思います。
今回使ったコードはこちらにあります。
https://github.com/sabmeua/aws-iot-device-fleet-test
フリートプロビジョニング
まず、デバイスを複数作りたいですが、準備するのは面倒なので、フリートプロビジョニングを使います。
$ git clone https://github.com/sabmeua/aws-iot-device-fleet-test.git
$ cd aws-iot-device-fleet-test
$ ./setup.sh
./setup.sh
ではAWS IoTのプロビジョニングテンプレートとそのブートストラップテンプレート、IAMロールなどを作成します。
TerraformにAWS IoTのI/Fがなかったためaws cliでやっています。あとでリソース削除したい場合は手動でやらないといけないのでご注意ください。
モノ側は、デバイスを用意はできないので、dockerでalpine上でpython SDKを使って行います。docker-composeで複数のモノを立ち上げるので、イメージのビルドもdocker-composeで行います。
$ docker-compose build
このイメージは https://github.com/aws-samples/aws-iot-fleet-provisioning フリートプロビジョニングのサンプル実装をチェックアウトしてきます。コンテナを起動すると run_device.sh
から、プロビジョニングとシャドウのアップデートを開始するようになっています。
シャドウのアップデート
シャドウのアップデートは下のようなスクリプトになっています。
from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTShadowClient
import time
import json
import random
def callback(payload, status, token):
if status == "timeout":
print(f'Timeout: {token}')
if status == "accepted":
payloadDict = json.loads(payload)
print(f'Accepted: {token}')
print(payload)
if status == "rejected":
print("Rejected: " + token)
with open('device_info.json') as f:
dev = json.load(f)
shadowClient = AWSIoTMQTTShadowClient(dev['thingName'])
shadowClient.configureEndpoint(dev['endpoint'], 8883)
shadowClient.configureCredentials(
f'{dev["certPath"]}/{dev["rootCert"]}',
f'{dev["certPath"]}/{dev["privateKey"]}',
f'{dev["certPath"]}/{dev["cert"]}')
shadowClient.configureAutoReconnectBackoffTime(1, 32, 20)
shadowClient.configureConnectDisconnectTimeout(10)
shadowClient.configureMQTTOperationTimeout(5)
print(f'Start updating the battery status : {dev["thingName"]}')
shadowClient.connect()
shadowHandler = shadowClient.createShadowHandlerWithName(dev['thingName'], True)
shadowHandler.shadowDelete(callback, 5)
prop = {'state':{'reported':{'batt': 100.0}}}
while True:
batt = random.betavariate(6, 3) * 100
prop['state']['reported']['batt'] = batt
shadowHandler.shadowUpdate(json.dumps(prop), callback, 5)
if batt < 50:
print(f'Stop updating due to the battery remaining is less than 50%')
break
time.sleep(30)
プロビジョニングで取得したデバイス毎の証明書を使って、30秒おきに reported.batt
というバッテリ残量を模したシャドウのプロパティを betavariate(6, 3)
のランダム値で更新します。
この値が50%を切ったら更新を停止するようにしています。
フリートインデックスの有効化
フリートインデックスが作成されるように設定を変更します。フリートインデックスの作成は課金されるので、不要なら、あとで設定を戻すように注意してください。
AWSマネジメントコンソールのAWSIoTのコンソールから ”設定 -> フリートインデックス作成の設定” で下記のように設定します。
以上で準備完了です。
実行
docker-composeで5台起動してみます。
1台ごとのプロビジョニングは下記のように表示されれば成功です。
$ docker-compose up --scale fleet_device=5
Starting aws-iot-device-fleet-test_fleet_device_1 ... done
Starting aws-iot-device-fleet-test_fleet_device_2 ... done
Starting aws-iot-device-fleet-test_fleet_device_3 ... done
Starting aws-iot-device-fleet-test_fleet_device_4 ... done
Starting aws-iot-device-fleet-test_fleet_device_5 ... done
Attaching to aws-iot-device-fleet-test_fleet_device_1, aws-iot-device-fleet-test_fleet_device_2, aws-iot-device-fleet-test_fleet_device_3, aws-iot-device-fleet-test_fleet_device_4, aws-iot-device-fleet-test_fleet_device_5
fleet_device_3 | ______ __ __
fleet_device_3 | / ____/ / / ___ ___ / /_
fleet_device_3 | / /_ / / / _ \ / _ \ / __/
fleet_device_3 | / __/ / / / __/ / __/ / /_
fleet_device_3 | /_/ /_/ \___/ \___/ \__/
fleet_device_3 |
fleet_device_3 |
fleet_device_3 | ____ _ _ _
fleet_device_3 | / __ \_________ _ __(_)____(_)___ ____ (_)___ ____ _
fleet_device_3 | / /_/ / ___/ __ \ | / / / ___/ / __ \/ __ \/ / __ \/ __ `/
fleet_device_3 | / ____/ / / /_/ / |/ / (__ ) / /_/ / / / / / / / / /_/ /
fleet_device_3 | /_/ /_/ \____/|___/_/____/_/\____/_/ /_/_/_/ /_/\__, /
fleet_device_3 | /____/
fleet_device_3 |
fleet_device_3 |
fleet_device_3 |
fleet_device_3 | ____________________________________________________________
fleet_device_3 | /_____/_____/_____/_____/_____/_____/_____/_____/_____/_____/
fleet_device_3 |
fleet_device_3 |
fleet_device_3 |
fleet_device_3 | ##### CONNECTING WITH PROVISIONING CLAIM CERT #####
fleet_device_3 | ##### SUCCESS. SAVING KEYS TO DEVICE! #####
fleet_device_3 | ##### CREATING THING ACTIVATING CERT #####
fleet_device_3 | ##### CERT ACTIVATED AND THING device-1590879565906 CREATED #####
fleet_device_3 | ##### CONNECTING WITH OFFICIAL CERT #####
fleet_device_3 | ##### ACTIVATED AND TESTED CREDENTIALS (0c2fa73a60-private.pem.key, 0c2fa73a60-certificate.pem.crt). #####
fleet_device_3 | ##### FILES SAVED TO /aws-iot-fleet-provisioning/certs #####
fleet_device_3 | {'service_response': '##### RESPONSE FROM PREVIOUSLY FORBIDDEN TOPIC #####'}
fleet_device_3 | Start updating the battery status : device-1590879565906
複数起動するとMQTTのコネクションがTimeoutしてプロビジョニングが失敗することがありました。
これについて、なぜかわかっていませんが、一度プロビジョニングして証明書を取得したデバイスのコンテナは、2回目以降の起動で再度プロビジョニングは行わないようにしているので、 docker-compose stop / start
を繰り返したら全てのデバイスのプロビジョニングが出来ます。
シャドウの更新が行われている状態では、下記のように、シャドウのトピックのMQTTのペイロードのログが表示されます。
このログでは、途中でデバイス5がバッテリー残量50%を切って終了しています。
このように状態の変化が起きている状態でフリートインデックスのクエリをしてみます。
fleet_device_3 | Start updating the battery status : device-1590879565906
fleet_device_1 | Start updating the battery status : device-1590879257006
fleet_device_2 | Start updating the battery status : device-1590879253998
fleet_device_5 | Start updating the battery status : device-1590879255597
fleet_device_4 | Start updating the battery status : device-1590879572901
fleet_device_3 | Accepted: 9b33888e-862e-453c-ad1a-d7ee626b0324
fleet_device_3 | {"version":15,"timestamp":1590884818,"clientToken":"9b33888e-862e-453c-ad1a-d7ee626b0324"}
fleet_device_1 | Accepted: aa3911c3-4da8-4cce-a746-4583ab85b238
fleet_device_1 | {"version":10,"timestamp":1590884818,"clientToken":"aa3911c3-4da8-4cce-a746-4583ab85b238"}
fleet_device_2 | Accepted: 792d90a4-82c7-425a-b66d-56339a7bedef
fleet_device_2 | {"version":7,"timestamp":1590884819,"clientToken":"792d90a4-82c7-425a-b66d-56339a7bedef"}
fleet_device_5 | Accepted: 429820d6-7dba-4a87-8589-3da5b9cef61e
fleet_device_5 | {"version":11,"timestamp":1590884819,"clientToken":"429820d6-7dba-4a87-8589-3da5b9cef61e"}
fleet_device_4 | Accepted: 0d72b28b-23d3-4023-bb3e-2728c55b4c0f
fleet_device_4 | {"version":6,"timestamp":1590884819,"clientToken":"0d72b28b-23d3-4023-bb3e-2728c55b4c0f"}
fleet_device_3 | Accepted: b3552043-6167-4b60-8bfc-1b1a5312b828
fleet_device_3 | {"state":{"reported":{"batt":54.56623259478952}},"metadata":{"reported":{"batt":{"timestamp":1590884821}}},"version":17,"timestamp":1590884821,"clientToken":"b3552043-6167-4b60-8bfc-1b1a5312b828"}
fleet_device_5 | Stop updating due to the battery remaining is less than 50%
fleet_device_1 | Accepted: e04057ed-7c12-4a74-bc8e-4e4496111803
fleet_device_1 | {"state":{"reported":{"batt":55.60684342775718}},"metadata":{"reported":{"batt":{"timestamp":1590884821}}},"version":12,"timestamp":1590884821,"clientToken":"e04057ed-7c12-4a74-bc8e-4e4496111803"}
fleet_device_2 | Accepted: dceb237f-0a90-437b-8d04-1aa03c661830
fleet_device_2 | {"state":{"reported":{"batt":55.73068072917988}},"metadata":{"reported":{"batt":{"timestamp":1590884821}}},"version":9,"timestamp":1590884821,"clientToken":"dceb237f-0a90-437b-8d04-1aa03c661830"}
fleet_device_4 | Accepted: 5f301135-2715-41d5-a7ec-372a0c251540
fleet_device_4 | {"state":{"reported":{"batt":78.55606282570174}},"metadata":{"reported":{"batt":{"timestamp":1590884821}}},"version":8,"timestamp":1590884821,"clientToken":"5f301135-2715-41d5-a7ec-372a0c251540"}
aws-iot-device-fleet-test_fleet_device_5 exited with code 0
fleet_device_3 | Accepted: b4161f0d-5b33-43ae-b8c8-a06ee42a6417
fleet_device_3 | {"state":{"reported":{"batt":60.10810045271757}},"metadata":{"reported":{"batt":{"timestamp":1590884851}}},"version":18,"timestamp":1590884851,"clientToken":"b4161f0d-5b33-43ae-b8c8-a06ee42a6417"}
フリートインデックスのクエリ
クエリはとりあえずはaws cliで試すことができます。
まず、シャドウのバッテリ残量でクエリしてみます。値が50~80、80~100でレポートされているモノを shadow.reported.batt:{50 TO 80}
のようなクエリで抽出することができました。
$ aws iot search-index --query-string 'shadow.reported.batt:{50 TO 80}'
{
"things": [
{
"thingName": "device-1590879565906",
"thingId": "2c387914-1426-4076-a8de-24876b9d213e",
"thingTypeName": "device-type-1",
"thingGroupNames": [
"test-devices"
],
"shadow": "{\"reported\":{\"batt\":58.422574552849426},\"metadata\":{\"reported\":{\"batt\":{\"timestamp\":1590887635}}},\"version\":26}",
"connectivity": {
"connected": true,
"timestamp": 1590887601460
}
},
{
"thingName": "device-1590879253998",
"thingId": "5da7ed87-9535-451a-8211-77fa1936a67c",
"thingTypeName": "device-type-1",
"thingGroupNames": [
"test-devices"
],
"shadow": "{\"reported\":{\"batt\":71.36448863151011},\"metadata\":{\"reported\":{\"batt\":{\"timestamp\":1590887633}}},\"version\":32}",
"connectivity": {
"connected": true,
"timestamp": 1590887599149
}
},
{
"thingName": "device-1590879255597",
"thingId": "e4bdd3c6-85b1-4d47-9947-621a0ed5dcdd",
"thingTypeName": "device-type-1",
"thingGroupNames": [
"test-devices"
],
"shadow": "{\"reported\":{\"batt\":70.52375514441887},\"metadata\":{\"reported\":{\"batt\":{\"timestamp\":1590887604}}},\"version\":26}",
"connectivity": {
"connected": true,
"timestamp": 1590887599730
}
}
]
}
$ aws iot search-index --query-string 'shadow.reported.batt:{80 TO 100}'
{
"things": [
{
"thingName": "device-1590879255597",
"thingId": "e4bdd3c6-85b1-4d47-9947-621a0ed5dcdd",
"thingTypeName": "device-type-1",
"thingGroupNames": [
"test-devices"
],
"shadow": "{\"reported\":{\"batt\":83.16154864112754},\"metadata\":{\"reported\":{\"batt\":{\"timestamp\":1590887634}}},\"version\":27}",
"connectivity": {
"connected": true,
"timestamp": 1590887599730
}
}
]
}
次に、バッテリ残量50%を切って、更新を停止してしまったモノを抽出します。 connectivity.connected
というインデックスが作成されており、これでクエリできます。
$ aws iot search-index --query-string 'connectivity.connected:false'
{
"things": [
{
"thingName": "device-1590879257006",
"thingId": "6b9efeb0-a668-4c2c-83b5-8a0ced063bf0",
"thingTypeName": "device-type-1",
"thingGroupNames": [
"test-devices"
],
"shadow": "{\"reported\":{\"batt\":40.81584157412556},\"metadata\":{\"reported\":{\"batt\":{\"timestamp\":1590887635}}},\"version\":29}",
"connectivity": {
"connected": false,
"timestamp": 1590887635292
}
}
]
}
まとめ
今回は5台でシャドウも1プロパティだけでしたが、フリートインデックスを使って、基本的なモノの検索をすることができました。
connectivity.connected
による検索が可能なのは、デバイスの管理にとても有効だと思いました。