AWS IoT Core Job 基本操作

# Job基本動作

![image-20210220112425258.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695430/acc21a27-5123-6130-83e3-ee2dc6f1a7e0.png)

### 1．Job document を格納

`Job document`は、以下のようなJSON形式のファイルで、デバイスに対する指示内容を記載したもの

   ```json
   {
     "operation": "update"
   }
   ```

### 2．デバイスでダウンロードしたいファイルを格納

   デバイスでダウンロードしたいファイルがない場合は本手順不要
   デバイスでダウンロードしたいファイルがある場合はS3にファイルを格納すると共に、
   S3のURLを`Job document`に記載する、URLは以下の形式で記載する

   ```yaml
   ${aws:iot:s3-presigned-url:https://s3.amazonaws.com/bucket/key} # bucket,keyは置き換える
   ```

   この形式で記載することでJobが実行されたタイミング（デバイスに対して`Job document`の内容が通知されるタイミング）で署名付きURLに変換される
   以下`Job document`例

   ```json
   {
     "operation": "update",
     "url": "${aws:iot:s3-presigned-url:https://s3.amazonaws.com/test-bucket/folder/job.zip}"
   }
   ```

### 3．Job を作成

   Jobの作成は、`Job ID`、`Jobの対象`、`Jobのタイプ`を指定する

- `Job ID`：Jobを識別するもの、値は任意、デバイスにも通知される
- `Jobの対象`：Jobを実行する対象、モノまたはグループを選択可能
- `Jobのタイプ`：`snapshot`または`continuous`
    - `snapshot`：Job作成時に指定したJobの対象にJobが実行される
    - `continuous`：Job作成時に指定したJobの対象（グループ）と、Job作成後に指定したJobの対象（グループ）に追加されたモノに対してJobが実行される

### 4．Job document の内容を通知

   Jobを作成するとデバイスに対して`Job ID`、`Job document`の内容が通知される

### 5．Job document の内容に応じた処理を実行
### 6．ファイルダウンロード等

   デバイスは`Job document`の内容に応じてファイルダウンロード等の処理を実行する

### 7．Job 状態の通知

   デバイスはJobの実行結果をIoT Coreに対して通知する

# Job状態

Jobの状態はマネジメントコンソール上で以下のように表示される

![image-20210220145019980.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695430/72b80291-6dd8-73fd-62e5-aa9153523664.png)

Job生成時は、Jobの対象モノ数分キュー状態となる
デバイスがJobを開始すると進行中となり、
デバイスからの状態更新によって各状態のモノ数が変化する

# デバイス側のJob操作

[ジョブデバイス MQTT および HTTPS APIs](https://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/jobs-mqtt-api.html)
デバイスでJob関連の通知を受けるために各種TopicのSubscribe、Jobの開始、状態更新を行うために各種TopicのPublishが必要となる
※TOPICに含まれる`thingName`、`jobId`は適切なものに置き換える

### Job追加等の変化検出

Pub/Sub：`Subscribe`
コマンド：`NextJobExecutionChanged`
TOPIC：`$aws/things/thingName/jobs/notify-next`

### 保留中Job開始

Pub/Sub：`Publish`
コマンド：`StartNextPendingJobExecution`
TOPIC：`$aws/things/thingName/jobs/start-next`

デバイスがオンライン時にJobが追加された場合は、`NextJobExecutionChanged`でJobの追加を検出し保留中Job開始を行う
デバイスがオフライン時にJobが追加された場合は、デバイス起動時に保留中Job開始を行う

※`$aws/things/thingName/jobs/start-next`は`Publish`専用Topicのため、ルールを使用してLambda等で受けることはできない（[予約済みトピック](https://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/reserved-topics.html)）

### 保留中Job開始結果の取得

Pub/Sub：`Subscribe`
コマンド：`StartNextPendingJobExecution`
TOPIC（成功時）：`$aws/things/thingName/jobs/start-next/accepted`
TOPIC（失敗時）：`$aws/things/thingName/jobs/start-next/rejected`

`$aws/things/thingName/jobs/start-next/accepted`に以下のように`jobId`、`jobDocument`が通知される

```json
{
  "execution" : {
    "jobId" : "022",
    "thingName" : "MyThing",
    "jobDocument" : "< contents of job document >",
    "status" : "IN_PROGRESS",
    "queuedAt" : 1489096123309,
    "lastUpdatedAt" : 1489096123309,
    "versionNumber" : 1,
    "executionNumber" : 1234567890
},
  "clientToken" : "client-1",
  "timestamp" : 1489088524284,
}
```

### Job状態更新

Pub/Sub：`Publish`
コマンド：`UpdateJobExecution`
TOPIC：`$aws/things/thingName/jobs/jobId/update`

TopicでJobIdを指定し、ペイロードのstatusにIN_PROGRESS、FAILED、SUCCEEDED、または REJECTEDを指定しJob状態の更新を行う

※`$aws/things/thingName/jobs/jobId/update`は`Publish`専用Topicのため、ルールを使用してLambda等で受けることはできない（[予約済みトピック](https://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/reserved-topics.html)）

### Job状態更新結果の取得

Pub/Sub：`Subscribe`
コマンド：`UpdateJobExecution`
TOPIC（成功時）：`$aws/things/thingName/jobs/jobId/update/accepted`
TOPIC（失敗時）：`$aws/things/thingName/jobs/jobId/update/rejected`

# AWS側のJob操作

[ジョブ管理と制御の API](https://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/jobs-http-api.html)
[Boto3 API](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/iot.html#IoT.Client.describe_job_execution)

### Job作成（[create_job](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/iot.html#IoT.Client.create_job)）

パラメータ`document`を指定することで、事前に`Job document`を作成してS3に格納する必要はない
以下コード例

```python
document = {
  'operation': 'update',
  'version': version,
  'url': '${aws:iot:s3-presigned-url:https://s3.amazonaws.com/%s/%s}' % (FirmwareBucketName, S3key)
}
target = f'arn:aws:iot:{REGION}:{ACCOUNT_ID}:thing/{thingName}'
iotClient.create_job(
  jobId = jobId,
  targets = [target],
  document = json.dumps(document),
  targetSelection = 'SNAPSHOT',
  presignedUrlConfig={
      'roleArn': RoleArn,
      'expiresInSec': 3600
  }
```



### Job状態の取得（[describe_job_execution](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/iot.html#IoT.Client.describe_job_execution)）

thingNameまで限定したJob状態の取得
以下コード例

```python
response = iotClient.describe_job_execution(
  jobId = jobId,
  thingName = thingName
)
status = response['execution']['status']
```