前回までで、EcoFlow IoT Developer Platform(開発者プラットフォーム)を利用してポータブル電源のリアルタイム情報が取得できるところまでを行いました。
今回はプログラム上から開発者プラットフォームを利用して、自宅にあるポータブル電源の充電設定を変更する方法について記載します。
プログラム上から任意のタイミングでポータブル電源の充電ON/OFFや充電速度の変更が行えるようになるので、ポータブル電源自動入電システムのメインとなる部分かと思います。
ちなみにポータブル電源自動充電システムのシステム構成とメリット・デメリットについては、以下の記事で紹介しています。
EcoFlow IoT Developer Platform(開発者プラットフォーム)とは
EcoFlow IoT Developer Platform(開発者プラットフォーム)はざっくり言うと、プログラム上からポータブル電源の情報を取得したり、ポータブル電源の設定を変更する操作を行えるようにするためのサービスです。
開発者プラットフォームを利用するためには、開発者になるための申請を行い、アクセスキーとシークレットキーを発行する必要があります。
まだの方は以下の記事をご覧ください。
開発者プラットフォームのAPIにリクエストするための署名の作り方については以下の記事で詳しく記載しています。
デバイス(ポータブル電源)の設定変更
本記事で紹介するAPI(device/quota)を利用することで、デバイス(ポータブル電源)の設定をプログラム上から変更することが出来ます。
ポータブル電源の充電のON/OFFや充電速度の変更、その他にもディスプレイ設定やサイレントモードの設定など、様々な設定の変更ができます。
公式ドキュメント
まずは公式ドキュメントのページを確認してみましょう。
ポータブル電源の設定を変更するためのリクエスト(コマンド)は沢山用意さされていますが、共通の仕様としては以下となっています。
Setting device’s function
EcoFlow IoT Developer Platform – generalInfo
Key | Value |
Method | PUT |
URL | https://${host}/iot-open/sign/device/quota |
Content-Type | application/json;charset=UTF-8 |
リクエスト(コマンド)ごとのリクエストボディー部の使用については、ポータブル電源の機種ごとにドキュメントページが用意されており、そちらに記載があります。
以下はDelta2Maxのドキュメントになります。
HTTP communication mode
EcoFlow IoT Developer Platform – delta2max
Set & Get Quota
本記事で紹介するAC充電の設定を変更するリクエストの仕様は以下の通りとなっています。
リクエストボディー
名称 | 型 | 必須 | 説明 |
id | int | ◯ | 固定値:123 |
version | String | ◯ | 固定値:”1.0″ |
sn | String | ◯ | シリアルナンバー |
moduleType | int | ◯ | 固定値:3(INV) |
operateType | String | ◯ | 固定値:”acChgCfg” |
params | ー | ◯ | |
fastChgWatts | int | ◯ | AC急速充電(X-Stream急速充電)の最大充電電力 |
slowChgWatts | int | ◯ | AC低速充電(カスタム充電)の最大充電電力 |
chgPauseFlag | int | ◯ | 0: AC充電ON / 1: AC充電OFF(一時停止) |
リクエストボディーのサンプルコードは以下です。
{
"id": 123,
"version": "1.0",
"sn": "R351ZFB4HF6L0030",
"moduleType": 3,
"operateType": "acChgCfg",
"params": {
"fastChgWatts": 2400,
"slowChgWatts": 400,
"chgPauseFlag": 0
}
}
Google Apps Script(GAS)でコーディング
では実際にコードを書いてみましょう。
最終的なコードは以下になります。
function setAcCharge() {
// ① リクエストURL
const host = "https://api-e.ecoflow.com";
const requestUrl = host + "/iot-open/sign/device/quota";
// ② アクセスキー、シークレットキーとシリアルナンバー
const accessKey = "※開発者プラットフォームで発行したアクセスキー";
const secretKey = "※開発者プラットフォームで発行したシークレットキー";
const serialNo = "※ポータブル電源のシリアルナンバー";
// ③ nonceとタイムスタンプを生成
let nonce = String(Math.floor(Math.random() * 900000) + 100000);
let timestamp = String((new Date).getTime());
// ④ リクエストボディー値
const id = 123;
const version = "1.0";
const moduleType = 3;
const operateType = "acChgCfg";
let fastChgWatts = 1800; // X-Stream急速充電の充電スピードを設定
let slowChgWatts = 400; // カスタム充電の充電スピードを設定
let chgPauseFlag = 0; // AC充電をONにする場合は0、一時停止する場合は1を設定
// ⑤ 署名用パラメータ
let signParam = "id=" + id
+ "&moduleType=" + moduleType
+ "&operateType=" + operateType
+ "¶ms.chgPauseFlag=" + chgPauseFlag
+ "¶ms.fastChgWatts=" + fastChgWatts
+ "¶ms.slowChgWatts=" + slowChgWatts
+ "&sn=" + serialNo
+ "&version=" + version
+ "&accessKey=" + accessKey
+ "&nonce=" + nonce
+ "×tamp=" + timestamp;
// ⑥ 暗号化して16進文字列に変換
let signature = Utilities.computeHmacSha256Signature(signParam, secretKey);
let sign = signature.reduce(function(str,chr){
chr = (chr < 0 ? chr + 256 : chr).toString(16);
return str + (chr.length==1?'0':'') + chr;
},'');
// ⑦ リクエストヘッダー
let requestHeaders = {
'accessKey': accessKey
,'timestamp': timestamp
,'nonce': nonce
,'sign':sign
}
// ⑧ リクエストデータ
let data = {
"id":id,
"version":version,
"sn":serialNo,
"moduleType":moduleType,
"operateType":operateType,
"params":{
"fastChgWatts":fastChgWatts,
"slowChgWatts":slowChgWatts,
"chgPauseFlag":chgPauseFlag
}
};
let payload = JSON.stringify(data);
// ⑨ リクエストオプション
let requestOptions = {
"method" : "PUT",
"contentType": "application/json",
"headers" : requestHeaders,
"payload": payload,
"muteHttpExceptions" : true
}
// ⑩ API実行(開発者プラットフォームへのリクエスト)
let response = UrlFetchApp.fetch(requestUrl, requestOptions);
// ⑪ 結果(レスポンス)を取得
let responseCode = response.getResponseCode();
let responseText = response.getContentText();
// ⑫ ログ出力
Logger.log("responseCode=" + responseCode);
Logger.log("responseText=" + responseText);
}
コードの解説
コードの内容をそれぞれ解説していきます。
// ① リクエストURL
const host = "https://api-e.ecoflow.com";
const requestUrl = host + "/iot-open/sign/device/quota";
EcoFlow IoT Developer Platform(開発者プラットフォーム)にリクエストするためのURLを定義しています。
開発者プラットフォーム(API)のホスト名はhttps://api-e.ecoflow.comです。
そこに今回取得したいデバイス情報取得のURL:/iot-open/sign/device/quotaを組み合わせて、最終的なURLを作っています。
https://api-e.ecoflow.com/iot-open/sign/device/quota
// ② アクセスキー、シークレットキーとシリアルナンバー
const accessKey = "※開発者プラットフォームで発行したアクセスキー";
const secretKey = "※開発者プラットフォームで発行したシークレットキー";
const serialNo = "※ポータブル電源のシリアルナンバー";
EcoFlow IoT Developer Platform(開発者プラットフォーム)から発行した、ご自身のアクセスキーとシークレットキー および ポータブル電源のシリアルナンバーをここに設定してください。
今回はわかりやすいようにアクセスキーとシークレットキー、シリアルナンバーを直接ソースコードに記載していますが、本来はセキュリティを考慮し、ソースコードと別で管理することをお勧めします。
Google Apps Script(GAS)のプロパティサービス等の利用を検討してください。
まだアクセスキーとシークレットキーを発行していないようであれば、以下の記事を参考に発行してください。
ポータブル電源のシリアルナンバーはスマホアプリから確認するか、開発者プラットフォームからデバイスリストを取得することで確認ができます。
// ③ nonceとタイムスタンプを生成
let nonce = String(Math.floor(Math.random() * 900000) + 100000);
let timestamp = String((new Date).getTime());
③ではnonceとtimestampの作成を行っています。
順に見ていきましょう。
- Code1Math.random()
Math.random()では 0~1未満 の乱数が生成されます。
- Code2Math.floor(Math.random() * 900000)
上記Code1で採番した乱数(0~1未満)に 900000 を掛けることで、0~900000未満 の値となります。
Math.floor()では小数点以下を切り捨てています。
結果手として、ここでは 0~899999 の乱数が作り出されます。 - Code3Math.floor(Math.random() * 900000) + 100000
上記Code2で出来た 0~899999 の乱数に対して、100000を足しています。
これで値としては 100000~999999 となり nonceとして欲しい6桁の整数となります。 - Code4String(Math.floor(Math.random() * 900000) + 100000)
最後にString()で文字列に変換してnonceは完成です。
nonceの採番(生成)方法については、必ずしも上記の実装である必要はありません。
ようは6桁数整数文字列が作成できれば良いので、他のやり方でも構いません。
- Code1new Date
システム日時(現在日時)で日付オブジェクトを作成しています。
- Code2(new Date).getTime()
上記Code1で作成した日付オブジェクトに対してgetTime()することで、システム日時(現在日時)のミリ秒を取得しています。
- Code3String((new Date).getTime())
最後にString()で文字列に変換してtimestampは完成です。
- Qnonceとは?
- A
nonceとは、ノンス/ナンスと呼ばれます。
API呼び出し時に一度だけ有効な任意の値(乱数)になります。
開発者プラットフォームではnonceとして 6桁の整数文字列 が必要です。
セキュリティ保護のためにAPIにリスエストする都度、新たな乱数を採番します。
- Qtimestampとは?
- A
名前の通りタイムスタンプ(現在日時)になります。
1970年1月1日00:00:00UTCからの経過ミリ秒数を設定します。
// ④ リクエストボディー値
const id = 123;
const version = "1.0";
const moduleType = 3;
const operateType = "acChgCfg";
let fastChgWatts = 1800; // X-Stream急速充電の充電スピードを設定
let slowChgWatts = 400; // カスタム充電の充電スピードを設定
let chgPauseFlag = 0; // AC充電をONにする場合は0、一時停止する場合は1を設定
④では設定変更API(device/quota)を利用してAC充電設定を変更するためのリクエストボディーの値を設定しています。
constで定義している部分は固定値(変更は不要)です。
- fastChgWatts
X-Stream急速充電の充電スピードになります。
基本的に充電スピードの設定は、低速充電(カスタム充電)に対して行います。
こちらの設定値は変更不要となります。 - slowChgWatts
低速充電(カスタム充電)の充電スピードになります。
こちらの設定を任意の値に変更することで、充電速度の変更が行えます。
スマホアプリ上からだと200w~1600wまでの設定範囲となっていますが、
APIから設定する場合には範囲外(例えば100wなど)の設定も可能なようです。 - chgPauseFlag
充電のON/OFFを切り替えることが出来ます。
AC充電をONにする場合は0、一時停止する場合は1を設定します。
基本的に充電スピードの設定は、低速充電(カスタム充電)に対して行います。
以下の画像(ポータブル電源のマニュアル)に従い、充電設定を「カスタム充電」に変更してください。
// ⑤ 署名用パラメータ
let signParam = "id=" + id
+ "&moduleType=" + moduleType
+ "&operateType=" + operateType
+ "¶ms.chgPauseFlag=" + chgPauseFlag
+ "¶ms.fastChgWatts=" + fastChgWatts
+ "¶ms.slowChgWatts=" + slowChgWatts
+ "&sn=" + serialNo
+ "&version=" + version
+ "&accessKey=" + accessKey
+ "&nonce=" + nonce
+ "×tamp=" + timestamp;
⑤では署名生成用のパラメータ文字列を作成しています。
文字列の作成ルールは以下の記事にて紹介しています。
// ⑥ 暗号化して16進文字列に変換
let signature = Utilities.computeHmacSha256Signature(signParam, secretKey);
let sign = signature.reduce(function(str,chr){
chr = (chr < 0 ? chr + 256 : chr).toString(16);
return str + (chr.length==1?'0':'') + chr;
},'');
ここでは署名用パラメータを暗号化して、署名(署名値)を作成しています。
以前の記事でも記載していますが、この部分の実装は難解ですので、一旦はお決まりとして「こういうものなんだなー」と思っていただければOKだと思います。
// ⑦ リクエストヘッダー
let requestHeaders = {
'accessKey': accessKey
,'timestamp': timestamp
,'nonce': nonce
,'sign':sign
}
EcoFlow IoT Developer Platform(開発者プラットフォーム)にリクエストするためのHTTPヘッダーを作成しています。
開発者プラットフォームではHTTPヘッダーに以下を設定してAPIを呼び出すルール(仕様)になっています。
- アクセスキー(accesskey)
- タイムスタンプ(timestamp)
- ナンス/ノンス(nonce)
- 署名(sign)
// ⑧ リクエストデータ
let data = {
"id":id,
"version":version,
"sn":serialNo,
"moduleType":moduleType,
"operateType":operateType,
"params":{
"fastChgWatts":fastChgWatts,
"slowChgWatts":slowChgWatts,
"chgPauseFlag":chgPauseFlag
}
};
let payload = JSON.stringify(data);
こちらはAPIへリクエストするためのデータ部の定義になります。
JSONという形式の文字列として定義し、次の「⑨リクエストオプション」の1つとして指定します。
// ⑨ リクエストオプション
let requestOptions = {
"method" : "PUT",
"contentType": "application/json",
"headers" : requestHeaders,
"payload": payload,
"muteHttpExceptions" : true
}
こちらはリクエストのオプション設定になります。
- method
今までの記事で紹介したデバイス情報の取得などと違い、
設定を変更する場合にはPUTを指定します。 - headers
⑦で作成したHTTPヘッダーを指定します。 - payload
⑧で作成したリクエストデータを指定します。
他、以下の指定はおまけ程度です(とりあえず無くても動くと思います)
- muteHttpExceptions
trueを指定することで、HTTPリクエスト時のエラー(例外)を抑制するオプションです。
※すべての例外を抑制できるわけではないという事と、例外が発生しないだけでエラー処理が不要になる訳では無い(本来は適切にエラー処理が必要)というところは注意が必要です。
// ⑩ API実行(開発者プラットフォームへのリクエスト)
let response = UrlFetchApp.fetch(requestUrl, requestOptions);
ここでは実際にAPI(開発者プラットフォームへのリクエスト)の実行を行っています。
ここまでで作成したリクエストURLとリクエスト用のオプションを指定して、UrlFetchApp.fetch()を呼び出すことで、実際の通信が行われ、結果のレスポンスがresponse(変数)に格納されます。
// ⑪ 結果(レスポンス)を取得
let responseCode = response.getResponseCode();
let responseText = response.getContentText();
ここではHTTPステータスとAPIの実行結果レスポンスをそれぞれ変数に格納しています。
// ⑫ ログ出力
Logger.log("responseCode=" + responseCode);
Logger.log("responseText=" + responseText);
HTTPステータスとAPIの実行結果をログに出力しています。
コード実行
コーディングを行ったら、変更を保存(フロッピーのアイコンをクリック)してから実行してみてください。
以下のような結果がレスポンスとして返ってくると思います。
正しくAPIが実行できていれば、スマートフォンのエコフローアプリに変更した設定が反映されているはずです。
充電のON/OFFを切り替えた場合には、ポータブル電源のディスプレイ(LCD)で充電状態(青いグルグル)が動き出したり止まったりすることも確認できると思います。
- Q実行時に「認証が必要です」「このアプリはGoogleで確認されていません」といったセキュティの警告が表示される場合は?
- A
Google Apps Script(GAS)では、プログラムの初回実行時などに「認証が必要です」や「このアプリはGoogleで確認されていません」といったセキュリティの警告が出ることがあります。
今回のソースコードでは、外部サービス(EcoFlow開発者プラットフォーム)に接続しようとしているため、その外部サービスに接続して本当に大丈夫ですか?安全ですか?とGoogleから確認されている状態です。
あなた自身がソースコードを理解し、外部サービス(EcoFlow開発者プラットフォーム)に接続して問題ないと判断したのであれば以下の手順に従って、プログラムの実行を承認(許可)してください。
脅すわけではありませんが、これはセキュリティ警告です。
記事に記載してあるソースコードを鵜呑みにせず、必ずご自身で理解し、問題ないことを確認の上で『承認(許可)』の操作をお願いします。
まとめ
お疲れ様でした。
これでプログラム上から任意のタイミングでポータブル電源の充電設定を変更することが出来るようになりました。
以前の記事で紹介したポータブル電源のリアルタイム情報取得と組み合わせることで、Google Apps Script(GAS)で定期的にポータブル電源の情報を取得し、その結果に応じてAC充電のON/OFF、充電スピードの変更するような制御が可能になるはずです。
ポータブル電源の自動充電システムを構築することが出来ますよ。
次回は簡易HEMS(Nature Remo E Lite)を利用して、家庭の消費電力量(余剰電力量)を取得する方法を記載していければと思っています。
余剰電力(売電する電気)がある場合だけ充電をNOにするような制御が行えるようになりますので、お楽しみに。