AWS IoT Eventsの特定状態のデバイス一覧について、Lambdaでメール通知してみた

AWS IoT Eventsで状態管理しているデバイスについて、Lambdaからデバイス一覧を取得しました。 取得したデバイス一覧は、SNSトピック経由でEメール送信しました。
2020.07.29

以前にAWS IoT Eventsで作成したハートビート監視は、次のタイミングでデバイス毎にメール送信していました。

  • オフライン状態(デバイス切断)になったとき
  • オンライン状態(デバイス復帰)になったとき

しかし、ハートビート監視をしていると「1時間毎に現在オフライン状態のデバイス一覧がメールで欲しい」ことがあります。

というわけで、AWS IoT Eventsを使っている場合にどうするのかを試してみました。

おすすめの方

  • AWS IoT Eventsでディテクターの一覧を取得したい方
  • SNSトピック経由でメール送信したい方

サーバーレスアプリの作成

作成する全体像は下記です。1時間毎に動くLambdaを作成し、AWS IoT Eventsからデバイス(ディテクター)の一覧を取得し、SNSトピック経由でメール送信しています。

全体構成図

AWS SAMの初期化

sam init \
    --runtime python3.7 \
    --name iot-events-get-sample \
    --app-template hello-world

template.yamlの作成

template.yamlは下記です。Lambdaを定義しています。なお、以前に手動作成したSNSトピックを流用しています。

template.yaml

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: iot-events-get-sample

Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: hello_world/
      Handler: app.lambda_handler
      Runtime: python3.7
      Timeout: 10
      Policies:
        - arn:aws:iam::aws:policy/AWSIoTEventsReadOnlyAccess
        - arn:aws:iam::aws:policy/AmazonSNSFullAccess
      Environment:
        Variables:
          # 以前、手動作成しちゃったSNSトピックを使う
          SNS_TOPIC_ARN: arn:aws:sns:ap-northeast-1:1234567890:heartbeat-send-mail-topic
      Events:
        CheckHeartbeat:
          Type: Schedule
          Properties:
            Schedule: cron(0 0/1 * * ? *) # 日本時間で毎日0時から1時間毎

  HelloWorldFunctionLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub /aws/lambda/${HelloWorldFunction}

Lambdaコードの作成

Lambdaコードは下記です。IoT Eventsのディテクターについて、offline状態の一覧を取得し、メッセージを作成してSNSトピックにPublishしています。

app.py

import boto3
import json
import os

iot_events_client = boto3.client('iotevents-data')
sns_client = boto3.client('sns')

def lambda_handler(event, context):
    detectors = get_detectors_offline('SampleHeartbeatModel')
    if len(detectors) != 0:
        message = create_message(detectors)
        send_email(message)


def get_detectors_offline(model_name, token=None):
    options ={
        'detectorModelName': model_name,
        'stateName': 'offline'
    }
    if token is not None:
        options['nextToken'] = token

    res = iot_events_client.list_detectors(**options)

    detectors = res.get('detectorSummaries', [])

    if 'nextToken' in res:
        detectors += get_detectors_offline(model_name, res['nextToken'])

    return detectors


def create_message(detectors):
    devices = []
    for item in detectors:
        device_id = item['keyValue']
        devices.append(device_id)
    return '\n'.join(devices)


def send_email(message):
    sns_client.publish(
        TopicArn=os.environ['SNS_TOPIC_ARN'],
        Subject='お知らせ:切断中のデバイス一覧',
        Message=message
    )

デプロイ

sam build

sam package \
    --output-template-file packaged.yaml \
    --s3-bucket cm-fujii.genki-deploy

sam deploy \
    --template-file packaged.yaml \
    --stack-name IoT-Events-Get-Sample-Stack \
    --capabilities CAPABILITY_NAMED_IAM \
    --no-fail-on-empty-changeset

動作確認

オフライン状態のデバイスが2個のとき

ディテクターは2つ存在しており、すべてoffline状態です。

IoT Eventsのディテクター(オフラインが2つ)

この状態でLmabdaを動かすと、メールが来ました!

メールの様子

オフライン状態のデバイスが1個のとき

1つのデバイスをonline状態に変更しました。

IoT Eventsのディテクター(オフラインが1つ)

この状態でLambdaを動かすと、メールが来ました!

メールの様子

内容もoffline状態のデバイスだけです。

オフライン状態のデバイスが0個のとき

2つのデバイスをonline状態に変更しました。

IoT Eventsのディテクター(オフラインが0個)

この状態でLambdaを動かすと、メールが来ませんでした。期待通りです。

さいごに

AWS IoT EventsとLambdaを組み合わせることで、特定状態のデバイス一覧を取得してメール送信することができました。 発想次第でもっとたくさんのことができそうですね。

参考