[Amazon Connect] GetCurrentMetricData でキューの状態をリアルタイムでブラウザに表示してみました

2019.03.05

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

1 はじめに

AIソリューション部の平内(SIN)です。

Amazon Connect(以下、Connect)では、コンソールからリアルタイムでキューの状況を確認することが可能です。

今回は、このようなリアルタイムのキューの情報をブラウザで確認できるものを雑に作成してみました。

2 構成

構成は、以下の通りです。

  • CloudWatch EventsでキックされたLambdaでConnectのAPIを叩き、取得したリアルタイムのデータをS3上に置きます。
  • Connectの管理者は、CloudFront経由でS3上のファイル(index.html)を閲覧します。
  • index.html内のJavaScriptは、更新されているリアルタイムのデータに基づいて表示します。
  • データが更新されたことは、MQTTのpublishで伝達され、ブラウザ表示が更新されます。
  • 表示する内容は、一般に公開するようなものではないので、WAFでIP制限しています。

3 GetCurrentMetricData

ConnectのAPIに用意されているGetCurrentMetricDataを使用するとリアルタイムなキューの状況を取得することができます。

https://docs.aws.amazon.com/connect/latest/APIReference/API_GetCurrentMetricData.html

(1) Request

リクエストのシンタックスは、次のとおりです。

POST /metrics/current/InstanceId HTTP/1.1
Content-type: application/json

{
   "CurrentMetrics": [ 
      { 
         "Name": "string",
         "Unit": "string"
      }
   ],
   "Filters": { 
      "Channels": [ "string" ],
      "Queues": [ "string" ]
   },
   "Groupings": [ "string" ],
   "MaxResults": number,
   "NextToken": "string"
}
  • Filters(Required) 最大100個のキューID、又は、キューARNを指定する
  • Groupings QUEUE | CHANNEL
  • MaxResults 1ページあたりに返される結果の最大数を1〜100の範囲
  • NextToken 次の結果セットを取得するためにトークン 期限は5分 後続のリクエストは、同じパラメータを使用する必要がある
  • CurrentMetrics(Required) 取得するメトリックを名前と単位のセットで指定
メトリックス 単位
AGENTS_AVAILABLE(利用可能) COUNT
AGENTS_ONLINE(オンライン) COUNT
AGENTS_ON_CALL(通話中) COUNT
AGENTS_STAFFED COUNT
AGENTS_AFTER_CONTACT_WORK(ACW) COUNT
AGENTS_NON_PRODUCTIVE(使用不可) COUNT
AGENTS_ERROR(エラー) COUNT
CONTACTS_IN_QUEUE(キュー内の呼数) COUNT
OLDEST_CONTACT_AGE(最も長く待機している時間) SECONDS
CONTACTS_SCHEDULED(コールバックがスケジュールされている呼数) COUNT

(2) Rersponse

レスポンスのシンタックスは、次のとおりです。

HTTP/1.1 200
Content-type: application/json

{
   "DataSnapshotTime": number,
   "MetricResults": [ 
      { 
         "Collections": [ 
            { 
               "Metric": { 
                  "Name": "string",
                  "Unit": "string"
               },
               "Value": number
            }
         ],
         "Dimensions": { 
            "Channel": "string",
            "Queue": { 
               "Arn": "string",
               "Id": "string"
            }
         }
      }
   ],
   "NextToken": "string"
}

MetricResultsに、取得を希望したメトリックスが配列で返ります。

以下は、取得データの一例です。

{
    "NextToken": null,
    "MetricResults": [
        {
            "Collections": [
                {
                    "Metric": {
                        "Name": "AGENTS_AVAILABLE",
                        "Unit": "COUNT"
                    },
                    "Value": 35
                },
                {
                    "Metric": {
                        "Name": "AGENTS_ONLINE",
                        "Unit": "COUNT"
                    },
                    "Value": 32
                },
                {
                    "Metric": {
                        "Name": "AGENTS_ON_CALL",
                        "Unit": "COUNT"
                    },
                    "Value": 15
                },
                {
                    "Metric": {
                        "Name": "AGENTS_STAFFED",
                        "Unit": "COUNT"
                    },
                    "Value": 45
                },
                {
                    "Metric": {
                        "Name": "AGENTS_AFTER_CONTACT_WORK",
                        "Unit": "COUNT"
                    },
                    "Value": 2
                },
                {
                    "Metric": {
                        "Name": "AGENTS_NON_PRODUCTIVE",
                        "Unit": "COUNT"
                    },
                    "Value": 0
                },
                {
                    "Metric": {
                        "Name": "AGENTS_ERROR",
                        "Unit": "COUNT"
                    },
                    "Value": 0
                },
                {
                    "Metric": {
                        "Name": "CONTACTS_IN_QUEUE",
                        "Unit": "COUNT"
                    },
                    "Value": 1
                },
                {
                    "Metric": {
                        "Name": "OLDEST_CONTACT_AGE",
                        "Unit": "SECONDS"
                    },
                    "Value": 1
                },
                {
                    "Metric": {
                        "Name": "CONTACTS_SCHEDULED",
                        "Unit": "COUNT"
                    },
                    "Value": 38
                }
            ]
        }
    ],
    "DataSnapshotTime": "2019-03-03T00:33:02.663Z"
}

(3) パーミッション

Lambdaに必要なパーミッションは、下記のみです。

connect:GetCurrentMetricData

(4) Lambdaの実装

今回実装したLambdaのコードは、以下のとおりです。

取得対象のキューは3つになっています。CurrentMetricsパラメータに、取得可能な全てのメトリックスを指定して、取得できたデータをそのままS3上にアップロードしています。

最後のAWSIotエンドポイントへのpublishは、ブラウザに更新を伝えています。

const aws = require('aws-sdk');
const connect = new aws.Connect({region:'ap-northeast-1'});

// ConnectのインスタンスID
const instanceId ='xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx';
// 対象キューのID
const queueId =['xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
                'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
                'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'];

const bucket = "bucketName";
const key = "CurrentMetricData.json";

exports.handler = async function (event) {

    // getCurrentMetricDataによるデータ取得
    const params = {
        CurrentMetrics: [
          { Name: 'AGENTS_AVAILABLE', Unit: 'COUNT' },
          { Name: 'AGENTS_ONLINE', Unit: 'COUNT' },
          { Name: 'AGENTS_ON_CALL', Unit: 'COUNT' },
          { Name: 'AGENTS_STAFFED', Unit: 'COUNT' },
          { Name: 'AGENTS_AFTER_CONTACT_WORK', Unit: 'COUNT' },
          { Name: 'AGENTS_NON_PRODUCTIVE', Unit: 'COUNT' },
          { Name: 'AGENTS_ERROR', Unit: 'COUNT' },
          { Name: 'CONTACTS_IN_QUEUE', Unit: 'COUNT' },
          { Name: 'OLDEST_CONTACT_AGE', Unit: 'SECONDS' },
          { Name: 'CONTACTS_SCHEDULED', Unit: 'COUNT' }
        ],
        Filters: { 
          Channels: ['VOICE'],
           Queues: queueId
        },
        InstanceId: instanceId,
    };
    const currentMetricData = await connect.getCurrentMetricData(params).promise();

    // S3上のデータファイルの更新
    const s3 = new aws.S3();
    const params = {
        Bucket: bucket,
        Key: key,
        Body: JSON.stringify(currentMetricData),
        ContentType: "application/json",
    };
    await s3.upload(params).promise();

    // MQTTによるブラウザのリフレッシュ
    const endpoint = 'xxxxxxxxxxx-ats.iot.ap-northeast-1.amazonaws.com';
    const region = 'ap-northeast-1';
    const topic = "Refresh_Topic"
    const iotdata = new aws.IotData({
        endpoint: endpoint,
        region: region
    });
    const params = {
        topic: topic,
        payload: '{"action":"refresh"}',
        qos: 0
    };
    let result = await iotdata.publish(params).promise();
}

4 html(JavaScript)

こちらは、表示用のhtmlです。

設定ファイルconfig.jsonを読み込んで、その設定(表示対象項目、表示名及び、しきい値に応じた表示色)に基づいて、リアルタイムのデータを表示しています。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="style.css">
<script src="//code.jquery.com/jquery-1.12.4.min.js"></script>
<script type="text/javascript" src="https://sdk.amazonaws.com/js/aws-sdk-2.355.0.min.js"></script>
<script src="aws-iot-sdk-browser-bundle.js"></script>
<script src="mqtt_refresh.js"></script>


<title>Amazon Conect Realtime Report</title>
</head>
<body>
    <h1>Amazon Connect Realtime Report <div id="datetime"></div></h1>
    <div class="mainContainer"></div>
</body>

<script>
    const colors = {"default":"#aaaaaa", "green":"#05c46b", "yellow":"#ffd32a", "red":"#f53b57"};
    let panels = [];
    let config;

    $.getJSON("config.json" , results => {
        config = results;

        // config.jsonから表示パネルを生成する
        let lineContainer; // 各列のコンテナ
        config.items.forEach( (item, i) => {
            if(i % config.colmax == 0) { // コンテナを追加して改行する
                lineContainer = $('<div class="lineContainer" />').appendTo('.mainContainer');
            }
            const panel = $('<div class="panel" />')
            $('<div class="key" />').appendTo(panel);
            $('<div class="detail">' + item.key + '<br>( '+ item.detail +' )</div>').appendTo(panel);
            panel.appendTo(lineContainer);
            panels.push(panel);
        });
    });

    // ブラウザの更新
    async function refresh() {

        // APIから取得したデータ
        const data = await getData();

        for(var i=0;i<config.items.length; i++) {
            const key = config.items[i].key;
            const value = data[key];

            const bgColor = getBgColor(key, value);
            panels[i].css('background-color', bgColor);
            panels[i].find('.key').text(value);
        }

        const now = new Date();
        $('#datetime').html('['+ now.toLocaleTimeString() + ']');
    }

    // リアルタイムのデータ(CurrentMetricData.json)の取得
    async function getData() {
        return new Promise((resolve,reject)=>{
            $.getJSON("CurrentMetricData.json" , results => {
                let data = {};
                results.MetricResults[0].Collections.forEach( collection => {
                    data[collection.Metric.Name] = collection.Value;
                })
                console.log(JSON.stringify(data))
                resolve(data);
            });
        })
    }

    // 値に応じたバックグラウンドカラーを取得する
    function getBgColor(key, value) {
        let bgColor = colors["default"];
        config.items.forEach(item => {
            if(item.key == key) { // 該当キーを検索する
                Object.keys(colors).forEach( color => {
                    if (item[color]) { // valueがどの色の範囲にあるかを検索する
                        const start = Number(item[color].split('-')[0]);
                        const end = Number(item[color].split('-')[1]);
                        if (start <= value && value <= end) {
                            bgColor = colors[color];
                        }
                    }
                });
            }
        })
        return bgColor;
    }

    // MQTTリフレッシュのsubscribe
    mqtt_refresh(refresh);

</script>
</html>

5 設定ファイル

先のJavaScriptが読み込んでいる設定ファイルは、次のような形式になっています。

colmaxは、ラベル横に並べる数です。この数を超えると改行されます。 keyが表示対象のメトリックスで、red,yellow,greenで表示色を決定するためのしきい値を指定しています。これは、使用環境によって、その数値の意味するところが変わるため、それに対応できるようにしてみました。

{
    "colmax":5,
    "items" : [
        {"key": "AGENTS_AVAILABLE", "detail":"利用可能", "red":"0-1", "yellow":"2-3", "green":"4-999"},
        {"key": "AGENTS_ONLINE", "detail":"オンライン", "red":"0-1", "yellow":"2-3", "green":"4-999"},
        {"key": "AGENTS_ON_CALL", "detail":"通話中", "green":"0-20", "yellow":"21-30", "red":"31-999"},
        {"key": "AGENTS_STAFFED", "detail":"スタッフ数", "red":"0-1", "yellow":"2-4", "green":"5-999"},
        {"key": "AGENTS_AFTER_CONTACT_WORK", "detail":"通話後作業", "green":"0-1", "yellow":"2-3", "red":"4-999"},
        {"key": "AGENTS_NON_PRODUCTIVE", "detail":"使用不可", "green":"0-1", "yellow":"2-3", "red":"4-999"},
        {"key": "AGENTS_ERROR", "detail":"エラー", "green":"0-0", "yellow":"1-1", "red":"2-999"},
        {"key": "CONTACTS_IN_QUEUE", "detail":"キュー内呼数", "green":"0-1", "yellow":"2-3", "red":"4-999"},
        {"key": "CONTACTS_SCHEDULED", "detail":"コールバック待機数", "green":"0-1", "yellow":"2-3", "red":"4-999"}
    ]
}

例えば、表示項目を絞って、設定ファイルを次のように編集すると、表示が変化します。

{
    "colmax":5,
    "items" : [
        {"key": "AGENTS_AVAILABLE", "detail":"利用可能", "red":"0-1", "yellow":"2-3", "green":"4-999"},
        {"key": "AGENTS_ONLINE", "detail":"オンライン", "red":"0-1", "yellow":"2-3", "green":"4-999"},
        {"key": "AGENTS_ON_CALL", "detail":"通話中", "green":"0-20", "yellow":"21-30", "red":"31-999"},
        {"key": "AGENTS_AFTER_CONTACT_WORK", "detail":"通話後作業", "green":"0-1", "yellow":"2-3", "red":"4-999"},
        {"key": "AGENTS_ERROR", "detail":"エラー", "green":"0-0", "yellow":"1-1", "red":"2-999"}
    ]
}

6 その他

(1) CloudWatch Events

APIコールのためのLambdaの起動は、CloudWatch Eventsのスケージュールを使用しています。

一応、営業時間(09:00-18:00)の間だけ、5分おきに更新するという意味で、下記のように設定してみました。本当は、平日のみ(月〜金)としたかったのですが、UTCタイムゾーンからの変換が良くわからなくなって・・・ すいません、?にしちゃてます。

(2) S3 + Cloudfront + WAF

S3に配置されたオブジェクトは、以下のようになります。

こちらをCloudfront経由で、公開しています。

WAFでは、デフォルトで全てBlockとし、特定のIPだけAllowしています。

(3) MQTT

MQTT(AWSIot)経由で、ブラウザを更新している仕組みは、以下を利用しています。

参考:[AWS IoT] MQTTを使用して、Lambdaからブラウザを更新する方法〜aws-iot-device-sdk(aws-iot-sdk-browser-bundle.js)を使用する場合〜

7 最後に

今回は、ConnectのAPI(GetCurrentMetricData)を使用して、リアルタイムなメトリック表示を作ってみました。何も作らなくても、コンソールで確認することが可能ですが、「ちょっっと、ここを変えたい、アラームを出したい」みたいな要望を実現するとなると、こんな感じになって来るのかも知れません。

弊社ではAmazon Connectのキャンペーンを行なっております。

3月に「無料Amazon Connectハンズオンセミナー」を開催致します。導入を検討されておられる方は、是非、お申し込み下さい。

また音声を中心とした各種ソリューションの開発支援も行なっております。