ソラカメAPIとソラカメに対応したCLIを使う

![SACM02b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75892/6d6a4b4b-34b7-0a7e-83f3-33f981bdc455.png)

# はじめに

祝、2022年10月6日に[ソラカメ](https://users.soracom.io/ja-jp/guides/soracom-cloud-camera-services/)の[API](https://users.soracom.io/ja-jp/tools/api/reference/#/SoraCam)が正式にリリースされました！
[SORACOM UG Online #13](https://soracomug-tokyo.connpass.com/event/253414/) のLT登壇で Limited Preview で公開できなかった内容をこちらに記載します。 

https://speakerdeck.com/yukataoka/sorakame-api-limited-preview-woshi-tutemita-apihafei-gong-kai-nafalsedeyu-remasenkedo

# 最新のSORACOM CLI でソラカメを試す
SORACOM CLI はAPIでできることを手軽に試すことができますので、先ずはこちらを試してみます。
SORACOM CLI でソラカメを試すためには、CLI の最新バージョン（2022年10月7日時点でv0.13.0）を入手します。
OS毎の SORACOM CLI 最新バージョンの入手や、セットアップについてはこちらを参照します。
https://github.com/soracom/soracom-cli/releases

ソラカメは ATOM Cam 1台、ATOM Cam Swing 1台、合計2台を所有しています。
以下にWindowsで実際に試した結果をご紹介します。

## ソラカメデバイス情報の取得
**カメラのデバイスリストを取得します。**
**soracom sora-cam devices list**

```
C:\work>soracom sora-cam devices list
[
        {
                "configuration": {
                        "audioAlarmEnabled": false,
                        "motionDetectionEnabled": true,
                        "smokeAlarmEnabled": false
                },
                "connected": true,
                "deviceCategory": "Camera",
                "deviceId": "デバイスID",
                "firmwareVersion": "4.58.0.97",
                "lastConnectedTime": 1665133143944,
                "name": "ATOM Cam 2",
                "productDisplayName": "ATOM Cam 2"
        },
        {
                "configuration": {
                        "audioAlarmEnabled": true,
                        "motionDetectionEnabled": true,
                        "smokeAlarmEnabled": false
                },
                "connected": true,
                "deviceCategory": "Camera",
                "deviceId": "デバイスID2",
                "firmwareVersion": "4.37.1.102",
                "lastConnectedTime": 1665133143379,
                "name": "ATOM Cam Swing",
                "productDisplayName": "ATOM Cam Swing"
        }
]
```

**カメラのデバイスを指定して情報を取得します。**
**soracom sora-cam devices get --device-id デバイスID**

```
C:\work>soracom sora-cam devices get --device-id デバイスID
{
        "appliedLicenses": [
                {
                        "name": "Cloud Continuous Recording",
                        "value": "storage_life=7"
                }
        ],
        "configuration": {
                "audioAlarmEnabled": false,
                "motionDetectionEnabled": true,
                "smokeAlarmEnabled": false
        },
        "connected": true,
        "deviceCategory": "Camera",
        "deviceId": "デバイスID",
        "firmwareVersion": "4.58.0.97",
        "lastConnectedTime": 1665133143944,
        "name": "ATOM Cam 2",
        "productDisplayName": "ATOM Cam 2"
}
```

取得できる情報はデバイスリストと同じです。

## ソラカメライブ映像の取得
**ソラカメのライブ映像を閲覧できるMPEG-DASH情報を取得**
**soracom sora-cam devices get-streaming-video --device-id デバイスID**

```
C:\work>soracom sora-cam devices get-streaming-video --device-id デバイスID
{
        "playList": [
                {
                        "url": "https://b-87178fb5.kinesisvideo.ap-northeast-1.amazonaws.com/dash/v1/getDASHManifest.mpd?SessionToken=CiCqC2Qbo（中略）hFu5H18~"
                }
        ]
}
```

MPEG-DASHについては以下を参照。
[NHK放送研究所 MPEG-DASHとハイブリッドキャスト](https://www.nhk.or.jp/strl/publica/rd/156/3.html )

## ソラカメ録画情報の取得
**ソラカメの録画情報を取得**
**soracom sora-cam devices videos export --device-id デバイスID --from 開始時刻 --to 終了時刻**

時刻にはUNIXタイムスタンプのミリ秒を指定します。
UNIXタイムスタンプの計算はWebサイトの[日付⇒UNIX時間変換](https://keisan.casio.jp/exec/system/1526003938)で算出したUNIXタイムスタンプ秒を利用するか、WindowsのPowerShellで以下のように変換できます。（2022/9/16 17:00:00を変換した例）

```
PS C:\work> (((Get-Date("2022/10/17 18:22:00")).AddHours(1) - (Get-Date("1970/1/1 0:0:0 GMT"))).totalSeconds) * 1000
1666002120000
```

コマンドを実行します。

```
C:\work>soracom sora-cam devices videos export --device-id デバイスID2 --from 1665134520000 --to 1665134535000
{
        "deviceId": "デバイスID",
        "exportId": "7ab4d（中略）b081e3",
        "operatorId": "OP0032113518",
        "requestedTime": 1665134924421,
        "status": "initializing"
}
```

statusで処理状況を確認できます。
当然最初はinitializingになっていますので、以下のコマンドで状況を再度確認します。
**soracom sora-cam devices videos get-exported --device-id デバイスID --export-id 出力ID**
```
C:\work>soracom sora-cam devices videos get-exported --device-id デバイスID --export-id 7ab4d（中略）b081e3
{
        "deviceId": "デバイスID",
        "exportId": "7ab4d（中略）b081e3",
        "operatorId": "OP0032113518",
        "requestedTime": 1665135201538,
        "status": "processing"
}
```

処理中はprocessingと表示されます。

```
C:\work>soracom sora-cam devices videos get-exported --device-id デバイスID2 --export-id 7ab4d（中略）b081e3
{
        "deviceId": "デバイスID",
        "expiryTime": 1665135940000,
        "exportId": "7ab4d（中略）b081e3",
        "operatorId": "OP0032113518",
        "requestedTime": 1665135201538,
        "status": "completed",
        "url": "https://soracom-sora-cam-devices-api-export-file-prod.s3.amazonaws.com/2022-10-07-09-33-21_OP0032113518_7CDDE9038AB5_ATOM%20Cam%202_be1693a1-9452-43fd-9d69-5fe2146d08f0.zip?AWSAccessKeyId=ASIA（中略）7Ao%3D&Expires=1665135940"
}
```

completedで完了していますので、urlからmpeg4動画を含むzipファイルをダウンロードできます。
指定できる動画の長さは5分で、urlでダウンロード可能なのは24時間以内です。

現在取得可能な動画は以下のコマンドで確認できます。
**soracom sora-cam devices videos list-exports --device-id デバイスID**

```
C:\Users\ykata>soracom sora-cam devices videos list-exports --device-id デバイスID
[
        {
                "deviceId": "デバイスID",
                "expiryTime": 1665135537000,
                "exportId": "7ab4d（中略）b081e3",
                "operatorId": "OP0032113518",
                "requestedTime": 1665134924421,
                "status": "expired",
                "url": "https://soracom-sora-cam-devices-api-export-file-prod.s3.amazonaws.com/2022-10-07-09-28-44_OP0032113518_7CDDE9038AB5_ATOM%20Cam%202_7ab4de40-0ecf-4a9a-9f14-c0ad49b081e3.zip?AWSAccessKeyId=ASIA（中略）7Ao%3D&Expires=1665135537"
        },
        {
                "deviceId": "デバイスID",
                "expiryTime": 1665135940000,
                "exportId": "be169（中略）d08f0",
                "operatorId": "OP0032113518",
                "requestedTime": 1665135201538,
                "status": "completed",
                "url": "https://soracom-sora-cam-devices-api-export-file-prod.s3.amazonaws.com/2022-10-07-09-33-21_OP0032113518_7CDDE9038AB5_ATOM%20Cam%202_be1693a1-9452-43fd-9d69-5fe2146d08f0.zip?AWSAccessKeyId=ASIA（中略）7Ao%3D&Expires=1665135940"
        }
]

```

**ソラカメの指定時刻の画像を取得**
**soracom sora-cam devices images export --device-id デバイスID --time 取得時刻**

取得の流れは録画情報を取得と変わりません。
コマンドを実行します。

```
C:\Users\ykata>soracom sora-cam devices images export --device-id デバイスID --time 1665134520000
{
        "deviceId": "デバイスID",
        "exportId": "844740（中略）67168",
        "operatorId": "OP0032113518",
        "requestedTime": 1665135360391,
        "status": "initializing"
}
```

処理状況は以下のコマンドで確認できます。
**soracom sora-cam devices images get-exported --device-id デバイスID --export-id 出力ID**
ですが、静止画はすぐに処理が完了するためprocessingを確認する前にcompletedになります。
urlからmpeg4動画を含むzipファイルをダウンロードできます。

```
C:\Users\ykata>soracom sora-cam devices images get-exported --device-id デバイスID --export-id 844740（中略）67168
{
        "deviceId": "デバイスID",
        "expiryTime": 1665135961000,
        "exportId": "844740（中略）67168",
        "operatorId": "OP0032113518",
        "requestedTime": 1665135360391,
        "status": "completed",
        "url": "https://soracom-sora-cam-devices-api-export-file-prod.s3.amazonaws.com/2022-10-07-09-36-00_OP0032113518_7CDDE9038AB5_ATOM%20Cam%202_84474035-9b4c-42ac-846c-8a2894667168.jpg?AWSAccessKeyId=ASIA3D（中略）7Ao%3D&Expires=1665135961"
}
```

ダウンロードした画像
![2022-10-07-09-36-00_OP0032113518_7CDDE9038AB5_ATOM Cam 2_84474035-9b4c-42ac-846c-8a2894667168.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75892/1930b92b-98ce-89e5-0bb1-a82ff29f3261.jpeg)

現在取得可能な画像も以下のコマンドで確認できます。
**soracom sora-cam devices images list-exports --device-id デバイスID**

```
C:\Users\ykata>soracom sora-cam devices images list-exports --device-id デバイスID
[
        {
                "deviceId": "デバイスID",
                "expiryTime": 1665135961000,
                "exportId": "844740（中略）67168",
                "operatorId": "OP0032113518",
                "requestedTime": 1665135360391,
                "status": "completed",
                "url": "https://soracom-sora-cam-devices-api-export-file-prod.s3.amazonaws.com/2022-10-07-09-36-00_OP0032113518_7CDDE9038AB5_ATOM%20Cam%202_84474035-9b4c-42ac-846c-8a2894667168.jpg?AWSAccessKeyId=ASIA3D（中略）7Ao%3D&Expires=1665135961"
        },
        {
                "deviceId": "デバイスID",
                "expiryTime": 1665136376000,
                "exportId": "5dadd（中略）cb91e",
                "operatorId": "OP0032113518",
                "requestedTime": 1665135776030,
                "status": "completed",
                "url": "https://soracom-sora-cam-devices-api-export-file-prod.s3.amazonaws.com/2022-10-07-09-42-56_OP0032113518_7CDDE9038AB5_ATOM%20Cam%202_5daddf9c-2650-426b-8a62-e5d135bcb91e.jpg?AWSAccessKeyId=ASIA3D（中略）7Ao%3D&Expires=1665136376"
        }
]
```

## ソラカメAPI、CLIでまだできないこと
2022-10-07 現在では残念ながら**イベント情報や保管した映像を取得することはできません**。こちらは今後追加されることを期待してお待ちしています！

# SORACOM API でソラカメのライブ映像を表示

ソラカメのライブ映像をPCのブラウザで表示するため、以下の構成でMPEG-DASH情報PCのブラウザに送付するしくみを実装しました。

![SACM06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75892/9f4a2329-c4e6-b977-3f0c-34fdbbe6ee03.png)

AWS Lambda Function URL で、ブラウザからGET命令が届いたら、nodejsのプログラムでソラカメのMPEG-DASHの情報を取得し、ブラウザに返します。各設定は AWS Lambda の環境変数に保管しています。

```index.js
'use strict';

const fetch = require('node-fetch'); 

exports.handler = async (event) => {
    
    console.log(event);
    
    // GET引数の取得
    let seacret  = "";
    let deviceId = "";
    const query = event.rawQueryString.split('&');
    if(query.length < 1){
        return {
            statusCode: 400,
            body: JSON.stringify('Bad Request.'),
        };
    }
    
    for(let i=0; i<query.length; i++){
        let parm = query[i].split('=');
        if(parm.length > 0){
            if(parm[0] == 'seacret'){
                seacret = parm[1];
            }else if(parm[0] == 'deviceId'){
                deviceId = parm[1];
            }
        }
    }
    
    // 簡単なセキュリティ対策（Lambda環境変数seacretとGET引数seacretが合致しないと400を返す）
    if(seacret !== process.env['seacret']){
        return {
            statusCode: 400,
            body: JSON.stringify('Bad Request.'),
        };
    }
    
    // SORACOM認証（認証情報はLambda環境変数に保管）
    const auth = await PostSoracomAuth(process.env['id'], process.env['key']);
    
    // 引数のデバイスIDを確認、指定がない場合はデバイスリストの先頭IDを取得
    if(deviceId == ""){
        const cameras = await GetSoracomCameraList(auth);
        if(cameras.length < 1){
            return {
                statusCode: 404,
                body: JSON.stringify('Not Found.'),
            };
        }
        deviceId = cameras[0].deviceId;
    }
    
    // 現在のソラカメライブ映像用URLを取得し返す
    const live = await GetSoracomCamerLive(auth, deviceId);
    if(live.playList.length < 1){
        return {
            statusCode: 404,
            body: JSON.stringify('Not Found.'),
        };
    }else{
        return {
            statusCode: 200,
            body: live.playList[0].url,
        };
    }
};

// SORACAME AUTH
async function PostSoracomAuth(id, key)
{
    const url = "https://api.soracom.io/v1/auth";
    const headers = {
        'Content-type': 'application/json',
    };
    const body = JSON.stringify({ "authKeyId" : id, "authKey" : key });
    const response = await fetch(url, {
        method: 'post',
        body: body,
        headers: headers
    });
    const auth = await response.json();
    console.log(auth);
    return auth;
}

// SORACAME CAMERA DEVICE
async function GetSoracomCameraList(auth)
{
    const url = "https://api.soracom.io/v1/sora_cam/devices";
    const headers = {
        'X-Soracom-API-Key': auth.apiKey,
        'X-Soracom-Token'  : auth.token
    };
    const response = await fetch(url, {
        method: 'get',
        headers: headers
    });
    const devices = await response.json();
    console.log(devices);
    return devices;
}

// SORACAME DEVICE
async function GetSoracomCamerLive(auth, deviceId)
{
    const url = "https://api.soracom.io/v1/sora_cam/devices/" + deviceId + "/stream";
    const headers = {
        'X-Soracom-API-Key': auth.apiKey,
        'X-Soracom-Token'  : auth.token
    };
    const response = await fetch(url, {
        method: 'get',
        headers: headers
    });
    const liveUrl = await response.json();
    console.log(liveUrl);
    return devices;
}
```

今回の試験用HTMLでは MPEG-DASH を表示するため、ライブラリィの dash.js を利用しました。
https://github.com/Dash-Industry-Forum/dash.js


```test.html
<html>
<head>
<title>SORACAME LIVE TEST</title>
<style>
video { width: 960px; height: 540px; }
</style>
<script src="http://cdn.dashjs.org/v3.1.0/dash.all.min.js"></script>

<script>
const GetSrc = function(){
    const url = "https://<AWS Lambda FunctionのURL>/?seacret=<AWS Lambda の環境変数に設定したseacret>&deviceId=<ソラカメのデバイスID>";
    const xmlHttp = new XMLHttpRequest();
    xmlHttp.open("GET", url, false);
    xmlHttp.send();
    console.log(xmlHttp.status);
    console.log(xmlHttp.responseText);
    if (xmlHttp.status == 200) {
        setTimeout(Live, 3000, xmlHttp.responseText);
    }
    setTimeout(GetSrc , 295000);
};

const Live = function(url){
    var video = document.getElementById('video');
    const player = dashjs.MediaPlayer().create();
    player.initialize(video, url, true);
};
</script>

</head>
<body>
<h1>ソラカメ ライブ試験</h1>
<div>画面表示後3秒程度で画像表示、その後再生ボタンをクリック！</div>
<div><video id="video" autoplay controls></video></div>

<script>
GetSrc();
</script>
</body>
</html>
```

ソラカメライブ映像が表示されました。
![SACM05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75892/be647a2f-89ab-f901-2ee9-9c4022235524.png)
今後は時間があれば以下を試してみたいと思います。
・距離センサ、人感センサ連動した映像取得
・取得した映像から物体や、人、顔の抽出（OpenCV）
・クラウドAIサービスとの連携（Amazon Rekognition）