SORACOMのDownlink APIでパラメーターを渡す

# はじめに
[前々回の記事](https://qiita.com/ozk009/items/2c48eb7eec3cf5aff121)では、Windows PCで動くNode-REDで[SORACOMのDownlink API](https://soracom.com/ja/news/20250324-downlink-api-supports-device-control)を受信する手順、[前回の記事](https://qiita.com/ozk009/items/677d01d17a3c32098d7d)ではDownlink APIを[SORACOM Flux](https://soracom.jp/services/flux/)から呼ぶ手順を説明しました。

ここまでは単に通信をするだけでしたが、今回の記事では、現場のデバイス（本記事ではWindows PC）に文字列や数値のパラメーターを渡す手順を紹介します。
今回は以下の2つのパラメーターを渡してみます。

- param1：test（文字列）
- param2：12.3（数値）

# 事前準備
## Node-REDで受信フローの変更
受信したデータを確認するため、赤枠で囲ったデータ受信フローを変更します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/916611/bbb9fbec-747c-43fe-96fe-f3468ed3d6ee.png)

- change ノードへの接続を削除
- json ノードを追加し、http in ノード、http responseノードとつなぐ（json ノードはデフォルト設定のまま）
- json ノードとdebug ノードをつなぐ

## Fluxアプリの編集
[前回](https://qiita.com/ozk009/items/677d01d17a3c32098d7d)作ったFluxアプリ「Downlink API」を変更します。
SORACOM APIアクションを選択して、HTTP ボディを変更し、「更新する」をクリックしてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/916611/ecdf5394-5abd-47a2-84c6-0da5b2af7e56.png)

HTTP ボディの変更点は以下の通り、「"ssl": false」の後の`,`（カンマ）と、`"body" : "{\"param1\": \"${payload.param1}\", \"param2\": ${payload.param2}}"`の行を追加する。
```json
{
  "method": "POST",
  "path": "/api",
  "port": 1880,
  "skipVerify": true,
  "ssl": false,
  "body" : "{\"param1\": \"${payload.param1}\", \"param2\": ${payload.param2}}"
}
```

# 動作確認
コマンドプロンプトで以下を実行します。

```
curl -X POST -H "Content-Type: application/json" -d "{\"param1\": \"test\", \"param2\": 12.3}" https://api.soracom.io/v1/flux/incoming_webhooks/…（Incoming WebhookのURL）
```

Node-REDのデバッグウィンドウに「`{ param1: "test", param2: 12.3 }`」と表示されれば正常に受信できています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/916611/b7073098-d680-4164-9ec6-2b66cdfac414.png)

:::note warn
受信に失敗する場合、実行前にNode-REDのデータ送信を実行（injectノードの左のボタンをクリック）した後にAPIを実行すると成功することがあります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/916611/238a00b2-d7df-4ec8-9b4a-0b958b81d3b5.png)
:::

# 応用
ここまでパラメーターを固定で送っていましたが、Webブラウザから編集、送信できるようにしてみます。

## ローカルに置いたHTMLから送る（失敗）
Windows PCに以下のHTMLファイルを作成し、PCに保存します。

```html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Downlink API</title>
</head>
<body>
  <h1>SORACOM Flux に送信</h1>

  <label for="param1">param1：</label>
  <input type="text" id="param1" placeholder="例：test"><br>

  <label for="param2">param2：</label>
  <input type="text" id="param2" placeholder="例：12.3"><br><br>

  <button onclick="sendRequest()">送信</button>
  <pre id="result"></pre>

  <script>
    async function sendRequest() {
    
      const url = 'https://api.soracom.io/v1/flux/incoming_webhooks/…（Incoming WebhookのURL）';

      const param1 = document.getElementById('param1').value;
      const param2 = document.getElementById('param2').value;

      const data = new URLSearchParams({
        param1: param1,
        param2: param2
      });

      try {
        const res = await fetch(url, {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json'
          },
          body: JSON.stringify(data)
        });

        const text = await res.text();
        document.getElementById('result').textContent = 'レスポンス：' + text;
      } catch (err) {
        document.getElementById('result').textContent = 'エラー：' + err;
      }
    }
  </script>
</body>
</html>
```

:::note warn
上記HTMLは最小限の機能としています。実際に使う場合は入力チェックや数値チェックなどの実装も必要です。
:::

### 動作確認
HTMLファイルをブラウザで開くと、パラメーターの入力欄と送信ボタンがある画面になります。
param1に文字列、param2に数値を入れて送信すると、以下の通り「Failed to fetch」と表示され、**エラーとなります**。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/916611/4efb7a09-c286-4ad8-b3a1-a5976ccad3ce.png)

調べたところ、**CORS（クロスオリジン・リソース共有）制限**が原因だそうです。

## GAS経由で送る（成功）
どうしたものか困ってChatGPTに相談したところ、GAS（Google Apps Script）を経由して送れば良いとのことでした。

<font color="red">**×**</font> HTML(JavaScript)→SORACOM Flux
**<font color="green">○</font> HTML(JavaScript)→GAS→SORACOM Flux**

:::note warn
ここからの作業はGoogleアカウントにログインして行います。
Googleアカウントをお持ちでない人は[Googleアカウントの作成](https://support.google.com/accounts/answer/27441)からお願いします。
:::


### GASの設定
以下の手順でリクエストを中継するGASを作ります。
1. [Google ドライブ](https://drive.google.com/)で「＋新規」ボタンをクリックし、「その他」「Google Apps Script」を選択する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/916611/dc8370d5-abe3-4385-abaf-c00c35e76349.png)
1. 警告メッセージが出たら「スクリプトを作成」をクリックする
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/916611/7d77c2fa-1e57-4f22-b378-e6377f5fdabc.png)
1. ファイル名、プロジェクト名をFlaxRelayとして、コードを以下のようにします
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/916611/60c81282-8ccc-4162-9757-a2ac4bb87d0a.png)
    ```go
    function doPost(e) {
      try {
        // フォームパラメータから取得
        const param1 = e.parameter.param1;
        const param2 = e.parameter.param2;
 
        const fluxUrl = 'https://api.soracom.io/v1/flux/incoming_webhooks/…（Incoming WebhookのURL）';

        const payload = {
          param1: param1,
          param2: Number(param2)
        };

        const options = {
          method: 'post',
          contentType: 'application/json',
          payload: JSON.stringify(payload)
        };

        UrlFetchApp.fetch(fluxUrl, options);

        return ContentService.createTextOutput("OK");
      } catch (error) {
        return ContentService.createTextOutput("Error: " + error.toString());
      }
    }
    ```
1. 「デプロイ」「新しいデプロイ」を選択する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/916611/0876f230-cff0-43ae-996d-d79a4605d4fb.png)
1. 歯車アイコンをクリックし、「ウェブアプリケーション」を選択する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/916611/cfd37cec-bbd5-4795-a52b-3f3ffc2bde48.png)
1. 「次のユーザーとして実行」で「自分（…）」、「アクセスできるユーザー」で「全員」を選択して、「デプロイ」をクリックする
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/916611/8b5ad0a6-50a9-42b7-9f73-5bbd2c580f78.png)
1. URLをメモ（コピー）しておく
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/916611/441ba498-f80f-4524-b112-ce0665f28500.png)


### ローカルHTMLの書き換え
HTMLファイルを以下のようにして、保存してください。
```html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Downlink API</title>
</head>
<body>
  <h1>SORACOM Flux に送信</h1>

  <label for="param1">param1：</label>
  <input type="text" id="param1" placeholder="例：test"><br>

  <label for="param2">param2：</label>
  <input type="text" id="param2" placeholder="例：12.3"><br><br>

  <button onclick="sendRequest()">送信</button>
  <pre id="result"></pre>

  <script>
    async function sendRequest() {
    
      const url = 'https://script.google.com/macros/s/…/exec（前項でコピーしたGASのURL）';

      const param1 = document.getElementById('param1').value;
      const param2 = document.getElementById('param2').value;

      const data = new URLSearchParams({
        param1: param1,
        param2: param2
      });

      try {
        const res = await fetch(url, {
          method: 'POST',
          headers: {
            'Content-Type': 'application/x-www-form-urlencoded'
          },
          body: data
        });

        const text = await res.text();
        document.getElementById('result').textContent = 'レスポンス：' + text;
      } catch (err) {
        document.getElementById('result').textContent = 'エラー：' + err;
      }
    }
  </script>
</body>
</html>
```

### 動作確認
HTMLファイルをブラウザで開いた画面でparam1に文字列、param2に数値を入れて送信し、以下の通り「レスポンス：OK」と表示されたらNode-REDの画面を確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/916611/86ea2021-7f3b-444b-9a83-07e8a6bff02d.png)

Node-REDのデバッグウィンドウに「`{ param1: "…", param2: … }`」と入力した値が表示されれば正常に受信できています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/916611/5d0d4c98-5c75-4214-b401-d6f221d2f0f6.png)

以上でDownlink APIを使ってパラメーターを渡せるようになりました。

:::note warn
テストに失敗する場合、前述の注記同様、Node-REDのデータ送信を実行した後にAPIを実行すると成功することがあります
:::


# おわりに
いかがでしたでしょうか。
当初考えていたよりも全3回とボリュームが大きくなってしまいましたが、無事書ききれて一安心です。
そして今回もChatGPT先生に助けてもらって、何とかやりたかったことが実現できました。
ありがたい！！

内容についてはCORSなど基本的なことにつまずいたり、プロの方から見るとイケてない部分もあるかと思いますが、よろしければコメントでご指摘いただけると幸いです。