Google Apps ScriptとNetatmoで職場環境改善【GAS】
頭脳労働する職場環境では、夏は暑さ、冬は寒さといった具合にそのパフォーマンスに大きな影響を及ぼす外的要因がゴロゴロしています。そういった外的要因、感覚ではなく、数値化・視覚化すると何をどう改善すべきか見えてきます。また、この中でとりわけ二酸化炭素濃度については、法令でも1000ppm以下と定められており、これを超えてくると、眠気や思考能力の低下を招きます。
温度、湿度、気圧、騒音、二酸化炭素濃度を計測できるのが今回使用するNetatmoと呼ばれる装置。親機1機に最大4機(室外、室内含む)の子機をぶら下げる事が出来、データはクラウドに蓄積。アプリでいつでもどこでも確認でき、APIでデータを呼び出す事も出来ます。今回このデータを呼出しスプレッドシートに書き込むまでを行ってみたいと思います。
※ちなみにNetatmoは1セットになっていますが、同梱されているのは室外用の子機なので、室内用の子機は別途購入が必要。最大3機まで接続可能です。
目次
今回使用する資料・ファイル
事前準備
ライブラリの追加
図:OAuth2ライブラリを追加している様子
リダイレクトURLを取得する
OAuth認証実行用情報のセットアップ
ここでの準備は、Netatmo自身のセットアップは完了済みとしてお話を進めます。APIを使ってデータを呼び出すので、Developer登録をしてゆく作業になります。すでにデータの可視化自体はウェザーステーションページからも確認が可能です。これぞ、Internet of Thingsの王道。
- Netatmo Developerに入ります。
- 右上のMy Accountからログインします。
- Create An Appをクリックして登録します。
- APIの名前をつけ、適当にメアドなども入力します。ロゴもつけられますよ。I accept...のチェックを入れて、SAVEボタンを押します。
- Technical Parameterのセクションでは、控えておいたリダイレクトURLを入れます。
- すでにこの段階で、Client IDとClient Secretが出来ているので、控えておきます。
- App StatusはActivate、そして、SAVEボタンを押します。
図;アプリ登録画面
ソースコード
GAS側ソースコード
OAuth認証用コード
//認証用各種変数
var tokenurl = "https://api.netatmo.com/oauth2/token";
var authurl = "https://api.netatmo.com/oauth2/authorize?";
var clientid = 'ここにクライアントIDを入力';
var clientsecret='ここにクライアントシークレットを入力';
var scope = "read_station";
function startoauth(){
//UIを取得する
var ui = SpreadsheetApp.getUi();
//認証済みかチェックする
var service = checkOAuth();
if (!service.hasAccess()) {
//認証画面を出力
var output = HtmlService.createHtmlOutputFromFile('template').setHeight(310).setWidth(500).setSandboxMode(HtmlService.SandboxMode.IFRAME);
ui.showModalDialog(output, 'OAuth2.0認証');
} else {
//認証済みなので終了する
ui.alert("すでに認証済みです。");
}
}
//アクセストークンURLを含んだHTMLを返す関数
function authpage(){
var service = checkOAuth();
var authorizationUrl = service.getAuthorizationUrl();
var html = "<center><b><a href='" + authorizationUrl + "' target='_blank' onclick='closeMe();'>アクセス承認</a></b></center>"
return html;
}
//認証チェック
function checkOAuth() {
return OAuth2.createService("Netatmo")
.setAuthorizationBaseUrl(authurl)
.setTokenUrl(tokenurl)
.setClientId(clientid)
.setClientSecret(clientsecret)
.setScope(scope)
.setCallbackFunction("authCallback") //認証を受けたら受け取る関数を指定する
.setPropertyStore(PropertiesService.getScriptProperties()) //スクリプトプロパティに保存する
.setParam("response_type", "code");
}
//認証コールバック
function authCallback(request) {
var service = checkOAuth();
var isAuthorized = service.handleCallback(request);
if (isAuthorized) {
return HtmlService.createHtmlOutput("認証に成功しました。ページを閉じてください。");
} else {
return HtmlService.createHtmlOutput("認証に失敗しました。");
}
}
//ログアウト
function reset() {
checkOAuth().reset();
SpreadsheetApp.getUi().alert("ログアウトしました。")
}
- Token取得用のエンドポイントは、https://api.netatmo.com/oauth2/tokenです。
- OAuth認証用URLは、https://api.netatmo.com/oauth2/authorize?です。
- 今回、スコープとしてはステーションのデータの読み取りのみを設定してるので、read_stationだけとなっています。
デバイスリストを返すコード
//Netatomoデバイスデータを取得して返す関数
function getNetatomolist() {
//スプレッドシートを取得する
var ui = SpreadsheetApp.getUi();
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("devicelist");
//シートデータをクリアする
ss.getRange("A2:C").clearContent();
//ステーションデータを取得する
var service = checkOAuth();
if (service.hasAccess()) {
var url = stationdata;
var response = UrlFetchApp.fetch(url, {
headers: {
Authorization: "Bearer " + service.getAccessToken()
},
method: "GET",
contentType: "application/json"
});
//ユーザ名を取得する
var results = JSON.parse(response.getContentText());
//親機を取得する
var array = [];
array.push(results['body']['devices'][0]['module_name']);
array.push(results['body']['devices'][0]['_id']);
array.push(JSON.stringify(results['body']['devices'][0]['data_type']));
ss.appendRow(array);
//デバイスリスト(子機)を取得する
var devlength;
devlength = results['body']['devices'][0]['modules'].length
for(var i = 0;i<devlength;i++){
//配列初期化
array = [];
//データの取得
array.push(results['body']['devices'][0]['modules'][i]['module_name']);
array.push(results['body']['devices'][0]['modules'][i]['_id']);
array.push(JSON.stringify(results['body']['devices'][0]['modules'][i]['data_type']));
ss.appendRow(array);
}
//終了メッセージ
ui.alert("デバイスリストの取得が完了しました。");
}else{
//エラーを返す(認証が実行されていない場合)
ui.alert("error");
}
}
- Netatmoのデータ取得エンドポイントURLはhttps://api.netatmo.com/api/getstationsdataとなります。リファレンスはこちら。
- 通常の外部の気象データも取得が可能で、その場合のエンドポイントURLはhttps://api.netatmo.com/api/getpublicdataとなります。リファレンスはこちら。
- Indoorの値(親機)と子機の値の取得方法はちょっと違うので注意!!
- 今回は、デバイス名、デバイスID(あとで利用します)、取得できるデータタイプ(温度や二酸化炭素濃度など)を取得し、スプレッドシートに書き込みしています。
データの塊を取得してスプレッドシートに記述するコード
今回このデータの取得では、スクリプトトリガーを利用して、1時間に1度データを取得するようセットしてみました。APIで取得できるデータは最大1024ポイントです(scaleの指定がmaxの場合)。もちろん期間指定(UNIXタイムスタンプ)も可能です。
//指定日時のUNIXタイムスタンプを取得する
function now2unixtime(date){
var unixtime = Math.floor((date.getTime()/1000)).toString();
return unixtime;
}
//UNIXタイムスタンプを日時に変換する関数
function get_date(_timestamp){
var _d = _timestamp?new Date(_timestamp * 1000):new Date();
var Y = _d.getFullYear();
var m = ("0" + (_d.getMonth() + 1)).slice(-2);
var d = ("0" + _d.getDate()).slice(-2);
var H = ("0" + _d.getHours()).slice(-2);
var i = ("0" + _d.getMinutes()).slice(-2);
var s = ("0" + _d.getSeconds()).slice(-2);
return( Y + '-' + m + '-' + d + ' ' + H + ':' + i + ':' + s );
}
//現時点からみて一度に取得できるデータを全て取得する
function getWeatherMax(){
//スプレッドシートを取得する
var ui = SpreadsheetApp.getUi();
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("netatmo");
//実行問い合わせ
var response = ui.alert('直近のデータを取得しますか?既存のデータは全て削除されます。', ui.ButtonSet.YES_NO_CANCEL);
switch(response){
case response.YES:
//作業を続行する
break;
case response.NO:
return 0;
break;
case response.CANCEL:
ui.alert("設定はキャンセルされました。");
return 0;
break;
}
//シートデータをクリアする
ss.getRange("A2:G").clearContent();
//デバイスリストを取得する
var devlist = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("devicelist").getRange("A2:C").getValues();
var devlength = devlist.length;
//ステーションデータを取得する
var service = checkOAuth();
if (service.hasAccess()) {
var url = netatmodata;
var access_token = service.getAccessToken();
var array = [];
//親機のデバイスIDを取得する
var parentid = devlist[0][1];
for(var i = 0;i<devlength;i++){
//devlistが空白ならば終了する
if(devlist[i][1] == ""){
break;
}
//devlistからデバイス値を取得する
var devnum = devlist[i][1];
var devname = devlist[i][0];
var tempdev = devlist[i][2];
var devtype = tempdev.replace( /\[/g , "" ) ;
devtype = devtype.replace( /\]/g , "" ) ;
devtype = devtype.replace( /\"/g , "" ) ;
//送信パラメータ
var params = {
'access_token': access_token,
'device_id': parentid, //70:ee:50:36:ff:88
'module_id': devnum,
'scale': '30min', //スケールを30分単位に設定
'date_begin' : now2unixtime(new Date("2018/11/08")),
'type': devtype //'Temperature,Humidity,CO2,Pressure,Noise'
};
//送信オプション
var options = {
'method': 'post',
'contentType': 'application/x-www-form-urlencoded;charset=UTF-8',
'payload': params
};
var response = UrlFetchApp.fetch(url, options).getContentText('UTF-8');
//一旦10秒スリープさせる(UrlFetch連続実行でエラーにならないようにするため)
Utilities.sleep(10000);
//レスポンスデータを取得する
var data = JSON.parse(response);
Logger.log(data);
var blength = data['body'].length;
//返り値を処理する
for(var j = 0;j<blength;j++){
//一時配列を用意
var tempArray = [];
//一時配列データに基本データを入れる
tempArray.push(devname); //デバイス名を入れる
tempArray.push(get_date(data["body"][j]["beg_time"])); //UNIXタイムを変換して入れる
//観測データを配列に入れる
//対応データの数を調べる
var dtcnt = data["body"][j]["value"][0].length;
for(var z = 0;z<dtcnt;z++){
tempArray.push(data["body"][j]["value"][0][z]);
}
//空の値をpushする
switch(dtcnt){
case 3:
tempArray.push("");
tempArray.push("");
break;
case 2:
tempArray.push("");
tempArray.push("");
tempArray.push("");
break;
}
//書き込み用配列にpushする
array.push(tempArray);
}
}
//配列データを書き込む
var lastColumn = array[0].length //列の数を求める
var lastRow = array.length; //行の数を取得する
ss.getRange(2,1,lastRow,7).setValues(array);
//終了メッセージ
ui.alert("観測データの取得が完了しました。");
}else{
//エラーを返す(認証が実行されていない場合)
ui.alert("error");
}
}
- Netatmoの観測データ取得エンドポイントURLはhttps://api.netatmo.com/api/getmeasureとなります。リファレンスはこちら。
- 取得できるデータタイプは、Temperature, CO2, Humidity, Noise, Pressureの5種類(親機)。子機はNoiseとPressureはありません。また室外用子機はTemperatureとHumidityのみです。
- 今回は30分単位、2018/11/08〜のデータとしてパラメータを構築しています。ただし、1リクエストに対して最大1024レコードまでしか取得できません。
- 日付はUNIXタイムで返ってきます。また日付の指定もUNIXタイムでなければなりません。其のための相互変換用関数を利用させていただいています。
- 複数のタイプの子機と親機が混在しているので、配列にて値を調整しています。
- UrlfetchAppは10秒間隔で発行しないと、エラーとなるため、途中でスリープを10秒間入れています。
認証の実行と結果
スプレッドシートのメニューより、「セッティング」⇒「OAuth認証実行」をクリックすることで、ログイン認証画面が出てきます。Netatomoのサイトに飛び、YESを押すことで、Access Token他が手に入るようになります。
図:OAuth2認証画面が出てきます。
スプレッドシートメニューより、「Netatmo実行」⇒「直近全データ取得」でデータを取得します。といっても、2018/11/08で指定しているので、そこから最大1024ポイント、30分単位のデータが書き込まれます。
図:毎日取得しておけば、全データが揃います。






