前回の記事では、ポータブル電源自動充電システムに必要な EcoFlow IoT Developer Platform(開発者プラットフォーム)への申請とアクセスキーの発行までを紹介しました。
今回は、チュートリアルとして開発者プラットフォーム(API)との通信に必要な署名の生成方法をご紹介します。
ちなみにポータブル電源自動充電システムのシステム構成とメリット・デメリットは以下の記事で紹介しています。
EcoFlow IoT Developer Platform(開発者プラットフォーム)とは
EcoFlow IoT Developer Platform(開発者プラットフォーム)はざっくり言うと、プログラム上からポータブル電源の情報を取得したり、ポータブル電源の設定を変更する操作を行えるようにするためのサービスです。
通信方式
まずは EcoFlow IoT Developer Platform(開発者プラットフォーム)の通信方式を整理します。
以降の画像と説明文(翻訳済み)は、公式サイトからお借りしています。
アプリケーションは、各インターフェース・リクエストで使用されるaccessKeyとsecretKeyを介してアプリケーションとバインドされ、署名検証操作が行われる。
アプリケーションとサーバーはHTTPまたはMQTTプロトコルで通信でき、デバイスとサーバーはMQTTで通信できる。
EcoFlow IoT Developer Platform – introduction
- DeveloperApplication:
これから皆さんが作成する制御プログラムを指します。 - IoT Open Platform:
ここで紹介している開発者プラットフォーム自体を指します。 - EcoFlow Device:
ポータブル電源のことを指します。
説明の通り、皆さんが作成する制御プログラムと開発者プラットフォーム間は、HTTPかMQTTという通信手段(プロトコル)のどちらかを選べます。
私自身、MQTTはよくわからないので、以降はすべてHTTPで通信する方法をご紹介しています。
説明文から制御プログラムと開発者プラットフォームで通信をするためには、accessKeyとsecretKey、署名が必要なことがわかります。
(署名の作り方はこの後説明します)
あまり気にする必要はありませんが、開発者プラットフォームとポータブル電源間の通信にはMQTTという通信方式が使われているのですね。
HTTP通信プロセス
次に、皆さんがこれから作成する制御プログラムと開発者プラットフォームの間でどのように通信するのかをもう少し詳しく紹介します。
EcoFlow IoT Developer Platform – introduction
上記の通信プロセスのイメージだと①~③とあり、順を踏まないと行けないように思いますが、実際はそれぞれ別々(単独)で実行が可能です。
最初に一度だけ①の手順で、操作したいデバイス(ポータブル電源)が、自宅で利用しているポータブル電源と一致していることを念の為確認します。
その後は、そのポータブル電源のシリアルNoを指定して、②の設定変更と③の情報取得はいつでも任意のタイミングで行えます。
翻訳したものは以下になります。
説明を読んでいただくと分かる通り、①、②、③いずれも、要は開発者プラットフォーム(API)にリクエストする際には、アクセスキーと秘密キー使って署名を作り、それをAPIに渡せばOKということです。
じゃあその署名はどうやって作成するのか、次の手順で見ていきます。
署名の実装方法
署名の作り方は、以下のgeneralInfo(総合案内?)のページに記載があります。
HTTPリクエスト時の認証用に、アクセスキーとシークレットキーを使用した署名を作成します。
EcoFlow IoT Developer Platform – generalInfo
Step1~Step8までの手順で説明があり、少し難解です。
翻訳したものを載せます。
{
// General Parameter, expand to: name=demo1
"name" : "demo1" ,
// Array, expand to: ids[0]=1&ids[1]=2&ids[2]=3
"ids" :[1, 2, 3],
// Object, expand to: deviceInfo.id=1
"deviceInfo" :{
"id" :1
},
// Object Array, expand to: deviceList[0].id=1&deviceList[1].id=2
"deviceList" :[
{
"id" :1
},
{
"id" :2
}
]
}
Step1~Step3では、署名を作成するためのパラメータ文字列を作る部分の説明になります。
ルールは以下ですね。
・リクエストパラメータはキー(変数名)とバリュー(値)で構成されています。
キーとバリューは『=』で連結します。
例)param1=1
・リクエストパラメータが配列の場合にはキー名に[0]、[1]、[2]のように記載します。
例)array[0]=1
array[1]=2
array[2]=3
・リクエストパラメータがネスト(親子関係)されている場合にはドット(.)で繋ぎます。
例)parent.child=1
・複数のリクエストパラメータはキー名(変数名)で昇順に並び替える必要があります。
例)上の例だと以下の並びになります。 1.array[0]
2.array[1]
3.array[2]
4.param1
5.parent.child
・複数のリクエストパラメータは『&』で連結します。
例)上の例で並び替えた結果を連結すると、以下になります。
array[0]=1&array[1]=2&array[2]=3¶m1=1&parent.child=1
・accessKey、nonce、timestampは最後に連結します。
例)array[0]=1&array[1]=2&array[2]=3¶m1=1&parent.child=1&accessKey=***&nonce=…×tamp=…
※accessKey、nonce、timestampは並び替えの対象外ですので注意
nonceとtimestampがいきなり出てきていますが、以下になります。
nonce:
一度だけ有効な任意の乱数になります。
EcoFlow 開発者プラットフォームでは 6桁の整数文字列 を設定する仕様となっています。
timestamp:
名前の通りタイムスタンプ(現在日時)になります。
1970年1月1日00:00:00UTCからの経過ミリ秒数を設定します。
Step4~Step5は、実際に署名を作成する部分です。
シークレットキー(秘密鍵)はここで利用します。
今回紹介しているポータブル電源自動充電システムでは、Google Apps Script(GAS)でコーディングしますが、上記の例とは若干違うコードになります。
// HTTP Header
accessKey:ba2d7e9…
nonce:123456
timestamp:1671171709428
sign:85776ede686fe…
accessKey、nonce、timestampと作成した署名は、HTTPヘッダーに追加して開発者プラットフォーム(API)を呼び出します。
リクエストデータとして渡すわけではないというところが注意ポイントでしょうか。
// HTTP header content-type=application/json;charset=UTF-8
// HTTP body
{
"name": "...",
"begin_time": 123456789
}
// HTTP header content-type is none or other
// HTTP query string
https://${host}//iot-open/sign/device/quota/all?sn=123
基本的に、以下の理解でいいのかなと思います。
ポータブル電源の設定を変更するリクエストを送る場合にはJSON形式でデータを渡すので前者、リクエストボディ(HTTP body)にリクエストデータを設定する。
逆に、ポータブル電源のデータを取得する場合には後者、クエリ文字列(HTTP query string)にリクエストデータを設定する。
Step1~Step3で作成した署名用のパラメータ文字列とは違い、accessKey、nonce、timestamp(および署名)は、リクエストデータとしては渡しません。
// インプット(前提情報)
accessKey=Fp4SvIprYSDPXtYJidEtUAd1o
secretKey=WIbFEKre0s6sLnh4ei7SPUeYnptHG6V
nonce=345164
timestamp=1671171709428
// HTTP json Body(リクエストデータ)
{
"sn" : "123456789" ,
"params" :{
"cmdSet" :11,
"id" :24,
"eps" :0
}
}
// Step1~Step3に従って作成したリクエストパラメータ(key-value string)
params.cmdSet=11¶ms.eps=0¶ms.id=24&sn=123456789&accessKey=Fp4SvIprYSDPXtYJidEtUAd1o&nonce=345164×tamp=1671171709428
// Step4~Step5に従って作成した署名
// (生成した署名がこの値であれば、正しいです)
07c13b65e037faf3b153d51613638fa80003c4c38d2407379a7f52851af1473e
最後のStep8では、ご自身で作成したプログラムが間違っていないことを確認できるサンプルが載っています。
この後、実際にGoogle Apps Script(GAS)でコーディングしてみて、同じ結果(署名値)が取得できることを確認しましょう。
Google Apps Script(GAS)でコーディング
上記Step1~Step8を確認してもらって、署名作成の手順は理解いただけたかと思います。
ここからは実際にGASでプログラミングしてみてStep8と同じ結果(署名値)が作成できることを確認してみます。
準備
そもそもGoogle Apps Script(GAS)の利用が初めての方もいるかと思います。
とてもざっくりですが、プログラム(コード)を書くまでの手順を載せておきます。
① まずは以下からgoogleのページを開きます。
② googleページのアプリアイコンからスプレッドシートを起動します。
③ 空白のスプレッドシートを選択します。
④ スプレッドシートが起動するので、拡張機能から Apps Script をクリックします。
⑤ Apps Scriptの新しいプロジェクトとしてエディタが開きますので、これでプログラム(コード)を書くことが出来ます。
コード
では実際にコードを書いてみましょう。
最終的なコードは以下になります。
コードをそのままエディタにコピーペーストし、変更を保存(フロッピーのアイコンをクリック)してから実行してみてください。
function ecoflowSignTest() {
// ① 署名用パラメータ
let signParam = "params.cmdSet=11¶ms.eps=0¶ms.id=24&sn=123456789&accessKey=Fp4SvIprYSDPXtYJidEtUAd1o&nonce=345164×tamp=1671171709428";
// ② 暗号化
const secretKey = "WIbFEKre0s6sLnh4ei7SPUeYnptHG6V";
let encryptedValue = Utilities.computeHmacSha256Signature(signParam, secretKey);
// ③ 16進文字列変換
let signValue = encryptedValue.reduce(function(str,chr){
chr = (chr < 0 ? chr + 256 : chr).toString(16);
return str + (chr.length==1?'0':'') + chr;
},'');
// ログ出力
Logger.log("signValue=" + signValue);
}
上記ソースをGoogle Apps Script(GAS)で実行した結果が以下になります。
出力された値が、上述のStep8の値と同じであれば成功です。
コードの解説
// ① 署名用パラメータ
let signParam = "params.cmdSet=11¶ms.eps=0¶ms.id=24&sn=123456789&accessKey=Fp4SvIprYSDPXtYJidEtUAd1o&nonce=345164×tamp=1671171709428";
①のコードは、実装方法として解説したStep1~Step3の部分にあたります。
本来であればプログラムでこの署名用パラメータを作成すべきかもしれませんが、Step8のサンプルとして既に出来上がったパラメータの文字列が記載されていますので、今回はチュートリアルということもあり、そのまま利用させてもらっています。
// ② 暗号化
const secretKey = "WIbFEKre0s6sLnh4ei7SPUeYnptHG6V";
let encryptedValue = Utilities.computeHmacSha256Signature(signParam, secretKey);
実装方法として解説したStep4の部分にあたります。
Google Apps Script(GAS)では、便利な機能を集めたUtilitiesクラスが用意されており、今回はその中にあるcomputeHmacSha256Signatureという暗号化メソッドを利用します。
HmacSha256という暗号化方式で、秘密鍵を利用して署名用パラメータを暗号化しています。
暗号化した署名用パラメータ(署名値)は、バイト配列として返却されます。
// ③ 16進文字列変換
let signValue = encryptedValue.reduce(function(str,chr){
chr = (chr < 0 ? chr + 256 : chr).toString(16);
return str + (chr.length==1?'0':'') + chr;
},'');
実装方法として解説したStep5の部分にあたります。
ここで暗号化した値をバイト配列から文字列へ変換しています。
この部分の実装は難解ですので、一旦はお決まりとして「こういうものなんだなー」と思っていただければOKだと思います。
Step6に記載の通り、開発者プラットフォーム(API)にリクエストする際には、ここで作成した文字列(署名値)をHTTPヘッダーに設定することになります。
まとめ
お疲れ様でした。
これで開発者プラットフォーム(API)へリクエストするための署名は作成出来るようになりました。
次回からは実際にプログラムからEcoFlow IoT Developer Platform(開発者プラットフォーム)を利用する方法を記載していければと思っています。