Databricks にて Azure IoT Hub に対してダイレクトメソッドの呼び出しを実施する方法

## 概要

Databricks にて Azure IoT SDK により Azure IoT Hub に対してダイレクトメソッドの呼び出しを実施する方法を紹介します。本記事で詳細する方法は、検証目的で実行する方法です。

## 事前準備

### 環境構築

- Azure IoT Hub の構築 （本手順は Free tier で実施）
- Databricks （本手順は Serveless ではなく汎用コンピュートで実行）

### デバイスの作成と接続文字列の取得

Azure IoT Hub のリソース画面にて、`デバイス`タブを選択後、`+ デバイスの追加`を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/32708e89-0264-4b64-b5f2-494be974a7d4.png)

`デバイス ID`に任意の値を入力し、`保存`を選択してデバイスを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/37ac238e-cbd3-48d6-82f5-68cf9038354e.png)

作成したデバイスを表示して、`プライマリ接続文字列`の`コピー済み`を選択して値をコピーします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/4f8d5549-6a72-4ff4-9e10-0e23214201f6.png)

### 組み込みのエンドポイントの接続文字列を取得

Azure IoT Hub のリソース画面にて、`組み込みのエンドポイント`タブを選択して、`イベントハブ互換エンドポイント`の値をコピーします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/0d7b0ebb-998d-473d-8ee6-cbee3af3bbed.png)

### Azure IoT Hub の接続文字列を取得

Azure IoT Hub のリソース画面にて、`共有アクセス ポリシー`タブ -> `iothubowner`ポリシーを選択して、`プライマリ接続文字列`の値をコピーします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/ac063b7d-54f5-4dbd-a27a-92cde4a1a04a.png)

## ダイレクトメソッドの呼び出しを実施する方法

### ダイレクトメソッドコールバックリスナーを起動

Databricks にて下記のコードを実行します。

```python
%pip install azure-iot-device -q
dbutils.library.restartPython()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/43d9e867-2fa0-42dd-a599-e6de7b76e1d5.png)

```python
device_conn_str = "HostName=iot-hub-test-001.azure-devices.net;DeviceId=qiita_test_01;SharedAccessKey=r2fxXUdn/4w9iNJdhqnIJZ+h0qxxxxx="
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/7d10a795-bdfa-4e3e-8d12-be85d852e64a.png)

```python
import time
from datetime import datetime, timezone
from azure.iot.device import IoTHubDeviceClient, MethodResponse

def handle_reboot(request):
    delay = request.payload.get("delay", 0)
    print("payload: ",request.payload)
    print(f"Rebooting in {delay} sec …")
    status = 200
    payload = {
        "result": "OK",
        "delay": delay,
        "timestamp": datetime.now(timezone.utc).isoformat(),
    }
    client.send_method_response(
        MethodResponse.create_from_method_request(request, status, payload)
    )

client = IoTHubDeviceClient.create_from_connection_string(device_conn_str)
client.on_method_request_received = handle_reboot
client.connect()

print("Listening for direct methods.")

# 一定時間待機後に停止
time.sleep(60)
client.shutdown()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/d8092ff3-b6f0-4f35-b775-835b9177299f.png)


### ダイレクトメソッドの呼び出しを実施

Databricks にて異なるノートブックを作成して、下記のコードを実行します。

```python
%pip install azure-iot-hub -q
dbutils.library.restartPython()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/26d04035-1068-44ad-a13c-8f86e09cc2cf.png)


```python
iot_conn_str = "HostName=iot-hub-test-001.azure-devices.net;SharedAccessKeyName=iothubowner;SharedAccessKey=FUum/tiOgEuhye/JqAb+XXXXX="
device_id = "qiita_test_01"
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/951b8b81-b534-43fc-b8e6-c6152d00598f.png)

```python
from datetime import datetime, timezone
from azure.iot.hub import IoTHubRegistryManager
from azure.iot.hub.models import CloudToDeviceMethod

rgm = IoTHubRegistryManager(iot_conn_str)
method = CloudToDeviceMethod(
    method_name="reboot",
    payload={
        "delay": 5, 
        "timestamp": datetime.now(timezone.utc).isoformat(),
    },
    response_timeout_in_seconds=30,
)
resp = rgm.invoke_device_method(
    device_id=device_id,
    direct_method_request=method,
)
print(resp.status, resp.payload)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/764ec480-ae6c-45d5-a47a-85a4c832cbbc.png)

:::note info

ダイレクトメソッドコールバックリスナーを起動せずにダイレクトメソッドの呼び出しを実施した際には下記のエラーが発生します。リスナーを起動して再実行してください。

> HttpOperationError: Operation returned an invalid status code 'Not Found'

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/afd8af0e-7bd3-42e3-8ebb-a43bded7c09c.png)


:::


### ダイレクトメソッドの呼び出しが実行されたことを確認

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/2a4b4eb2-6e8f-4d10-a79d-7f1d84aabeb3.png)