楽に行こう!

のむさんチのライフログ

Nature Remoで実家のじいちゃんを見守り

じいちゃんは実家に一人

 去年、お母さんが死んでから実家はおじいさん一人。実家は高知でおれは松山。高速を使えば2時間で行けるとは言え、そうそう頻繁に行くのは難しい。加えて、最近おじいさんは足腰がだいぶ弱ってきて、平地を歩くのもずりずりとやっとの事。

 ついこの間も田んぼのタニシを取りよったら足滑らせて田んぼに落ちて、小一時間も上がれかったし、もう1年くらい前にはると思うけんど、夜中トイレに行こうとしてなぜか倒れて(気を失うくらい)、これもしばらく動けんかったみたい。

 こういうのを見ると、何かしらの形で見守りをせんと!と。お姉ちゃんが比較的近く(車で15分くらい)におるき、毎日晩飯のおかずを持っていくとかで見てくれよるけんど、四六時中って訳にもいかん。おれも遠いけんど何か出来んかと。。。

遠隔からの見守り、どんな方法がある?

 遠くから親を見守る方法は、ざっと調べてもだいぶある。

  • 介護支援サービスによる確認
  • 自治体の見守りサービスによる確認
  • ネット接続ポットの利用状況を確認
  • 電力消費量を推移を見て確認
  • ネットワークカメラで映像を確認
  • センサー付き専用機器による見守り

人による見守りが一番

 が、けんど、ウチのおじいさんは他人に何かを頼むとか、他人が家に来るとかってのがダメなタイプ。なので上位2つはボツなんよなぁ。本当は人による見守りが一番えいんやけどなぁ。介護認定も確か介護1か支援2かみたいやき、サービス利用できるのに、本人がイヤなんやき困ったもんや。

何かのついでに見守るサービスも

 次に自治体が提供する見守りサービスやけど、これ調べてみたけんど、ウチの実家周辺には無かった。ヤクルトとか、新聞とか配達のついでに見守るっていうのもあるみたいやけど、例えばヤクルトとかだとヤクルトと自治体が契約を結んでサービスを提供する、みたいな感じ。やき、ウチの実家周辺ではやってなかった。

今出来るのはIoTによる見守り

 となると、遠くのおれらが出来る見守りは、IoTを利用した見守り。これらを色々吟味してみた。

  • ポット        →コスト高いし、見守れる範囲が狭い
  • 電力消費量      →ウチの実家の電力会社はやってない
  • カメラ        →プライバシー的にいかんやろう
  • センサー付き専用機器 →コスト高い

 う~ん、どれも決定打に欠ける。と、色々検討しよったところ、ウチにはNature Remoがあるやん! Remoはネットワークにつながるし、温度、湿度、照度、人感の各種センサーが付いとる。特に人感センサー使えば遠隔で見守りできるんやない?

Nature Remoによる見守り

nature.global

 Remoは、クラウドファンディングに登場した時から利用しよって、今家には3台のRemoがあって、リビングに寝室、それにばあちゃんの部屋で稼働中。目的はエアコンやテレビ、照明といった赤外線対応の家電をスマホで操作するため。

期待の人感センサーを検討するも...

 このRemoを実家に設置してその状況をスマホで確認。特に人感センサーで動きを確認すれば、トラブルなく生活できよるよね、ってのが確認できるやろうと。スマホのRemoアプリだと、人感センサーをトリガーにして別の赤外線機器に指示を出すくらいしかできんけど、IFTTTを使えば通知も可能。

 が、しかし。人感センサーで「30分以上動きが検出されなかったら」というトリガーしか用意されてなかった。部屋にじいちゃんが入ってきて、30分動いてなかったら通知する、みたいな事はできるけんど、歳とるとあんまり動かんなるし、寝る事が多いきあんまり使えんよなぁ。

 じゃあ、Remoが提供するAPIを利用したらどうよ?と思うたけんど、人感センサーのデータを取得できるAPIは用意されてなかった(2019年6月現在)。残念(´・ω・`)

それでもRemoを活用するとしたら...

 人感センサーが使えんとは言え、Remoは比較的安価でネットワークにつながり、部屋の温度、湿度、照度がリモートで確認できる。ん、照度?

 そう、Remoの照度センサーを利用すれば、夕方から夜に部屋に入った&出た、夜中にトイレに入った&出たってのが確認できるんじゃ? 特にトイレは夜中に倒れた事があるき、これはよさそう!しかも、世の中には先人たちがRemoのデータをAPIで取得してグラフ化する、なんて事をやっていて、それを見るとグラフ化されれば視覚的にも確認しやすいみたい。

 という事で、おじいさんの見守りは、Remoの照度センサーを利用して「ゆるく見守る」ことからから始めてみろうかと。

Nature Remoによる見守りの構成

 さて、実際にRemoを使って見守りをするとして、どんな形をとるか?色々検討した結果、次を選択。

 これを図にすると次ような感じ。

f:id:n_nomusan:20190608190048p:plain
Remoによる見守りの構成

 実家にインターネットの環境は無いきSIMフリー対応の無線LANルーターを使う。Remoを実家の寝室とトイレに設置してSIMフリー対応の無線LANルーターに接続。そして、Google Apps Script(GAS)を利用して定期的(5分間隔)にRemoのCloud APIを叩きセンサーの情報を取得。それをMackerelに送信してグラフ化するという事。

Mackerelでセンサーの情報を可視化

 先にグラフ化した結果を見ると次のような感じで、温度・湿度・照度を視覚的に確認できる。

f:id:n_nomusan:20190608190820p:plain
実家の状況 (寝室)
f:id:n_nomusan:20190608190904p:plain
実家の状況 (トイレ)

 「実家の状況 (寝室)」のグラフを見ると、薄暗くなった18時くらいに部屋の明かりをつけて、23時くらいに就寝したっぽい事が分かる。そして、翌朝6時くらいから陽が出てきて明るくなった感じ。

 一方、同じ時間帯の「実家の状況 (トイレ)」のグラフを見てみると、照度のデータがポンっと突き出てる所が何ヶ所か。この様子から、この時間にトイレに入って出たことが分かる。もしこれで、上がった照度がずっと下がらないとかだと何かおかしい。

 Mackerelは、データを監視してアラームを上げる事ができるんで、後々そういうのも試してみたい。

料金は?

 これをやる上でちょっと心配だったのが通信量(料金)。5分間隔でAPIコールして通信するからどれくらいかかるのか? 毎月何千円も要るのはちょっとなぁ。。。

 が、実際数か月運用してみると、1日1MBで月30MB程度で収まってる事が分かった。

f:id:n_nomusan:20190609113158p:plain
料金

 これなら、mineoで一番安いデータプラン500MB(月額700円)でも余裕。余ったのは家族でシェアすれば無駄にならんしね。

Google Apps Script(GAS)のスクリプト

 これを実現するため、Google Apps Script(GAS)にコードを書く。やる事はだいたい次の通り。

 1. Google Apps Script(GAS)で新しいプロジェクトを作成
 2. APIトークンの取得
 3. GASのプロパティにAPIトークンを設定
 4. Remoからセンサーのデータを取得
 5. Remoのセンダーデータを分解
 6. 取り出したセンサーデータをMackerelに登録
 7. 定期実行するためのトリガーの設定


1. Google Apps Script(GAS)で新しいプロジェクトを作成

 次にGoogle Apps Script(GAS)でコードを書くわけやけど、従来はGoogleスプレッドシートから作成する方法なんかがあった。けど、もっと簡単にGoogle Apps Script(GAS)のコードを書くために「G Suite Developer Hub」ってのが提供開始されたき、これを利用するのが簡単。

 ここにアクセスしてGoogleアカウントでログインした後、左上のメニューから「新規スクリプト」を選択すると新しいスクリプト作成画面が現れる。

2. APIトークンの取得

 Remoからのデータ取得、Mackerelへのデータ登録、ともにAPIを利用するのでそれぞれのAPIトークンを取得する。APIトークンの取得方法は、Webサイトにたくさん情報が載っているので割愛。

3. GASのプロパティにAPIトークンを設定

 取得したAPIトークンをGASの「ファイル > プロジェクトのプロパティ > スクリプトのプロパティ」に設定しておく。今回は「NATURE_REMO_TOKEN」と「MACKEREL_API_KEY」という名前で設定。

 このプロパティは次のコードで取得できる。

    var prop = PropertiesService.getScriptProperties();
    var natureRemoApiToken = prop.getProperty('NATURE_REMO_TOKEN');
    var mackerelApiKey = prop.getProperty('MACKEREL_API_KEY');


4. Remoからセンサーのデータを取得

 取得したAPIトークンを組み合わせてRemoのCloud APIを呼び出し、JSON形式のデータを取得してそれをパースする。

function main() {
    // Remo からセンサーデータを取得
    var data = getNatureRemoData(natureRemoApiToken);
    var jsonData = JSON.parse(data);
}

//
// Nature Remo APIから部屋のデータを取得する
//
function getNatureRemoData(apiToken){
    url = 'https://api.nature.global/1/devices';
    var options = {"headers" : {"Authorization" : "Bearer " + apiToken}};
    res = UrlFetchApp.fetch(url,options);
    return res
}


5. Remoのセンダーデータを分解

 "1/devices"で取得できるのはRemoのアカウントに登録してある全てのRemoのセンサーデータ。なので、取得したJSON形式のデータから、機器ごとの温度、湿度、照度のデータを個別に取り出す。ちなみに、後で登録した機器ほど先の配列に格納されてくる。
 ついでに、Mackerelのサービスメトリックに登録するための形式に整形しておく。

function main() {
    var payloadParents = formatToPayloadParentsHouse(jsonData);
}

//
// Mackerel用にデータを整形する (実家用)
//
function formatToPayloadParentsHouse(jsonData){
    // 実家 (トイレ)
    var te0 = jsonData[0]['newest_events']['te']['val'];
    var hu0 = jsonData[0]['newest_events']['hu']['val'];
    var il0 = jsonData[0]['newest_events']['il']['val'];
    // 実家 (リビング)
    var te1 = jsonData[1]['newest_events']['te']['val'];
    var hu1 = jsonData[1]['newest_events']['hu']['val'];
    var il1 = jsonData[1]['newest_events']['il']['val'];

  var epoch = Date.now() / 1000;

  var payloadParents = [
    {
      'name' : 'room.0.temperature',
      'time' : epoch,
      'value' : Number(te0)},
    {
      'name' : 'room.0.humidity',
      'time' : epoch,
      'value' : Number(hu0)},
    {
      'name' : 'room.0.illumination',
      'time' : epoch,
      'value' : Number(il0)},
    {
      'name' : 'room.1.temperature',
      'time' : epoch,
      'value' : Number(te1)},
    {
      'name' : 'room.1.humidity',
      'time' : epoch,
      'value' : Number(hu1)},
    {
      'name' : 'room.1.illumination',
      'time' : epoch,
      'value' : Number(il1)},
  ];
  return payloadParents
}


6. 取り出したセンサーデータをMackerelに登録

 Mackrelでは、外部の任意のデータをグラフ化できる「サービスメトリック」というのがあるのでこれを利用してグラフ化する。
mackerel.io

 サービス名というのが、APIを通して登録した複数のメトリックデータを束ねる単位(1つの画面になる)。そしてデータは、メトリック名(name)、時間(time)、計測値(value)なので、例えば照度は次のように設定してみた(「5. Remoのセンダーデータを分解」)。

  • メトリック名  :room.0.illumination
  • 時間      :Date.now() / 1000
  • 計測値     :Number(il0)
function main() [
    // Mackerelのサービス名
    var serviceNameParents = 'Condition_Parents'; // 実家

    // Meckerel の別々のサービスにデータを送信
    postMackerelServiceMetric(mackerelApiKey, serviceNameParents, payloadParents);
}

//
// Mackerelに送る
//
function postMackerelServiceMetric(apiKey, serviceName, payload) {
    return UrlFetchApp.fetch(
        'https://mackerel.io/api/v0/services/' + serviceName + '/tsdb',
        {
            'contentType' : 'application/json',
            'method' : 'post',
            'headers' : {
                'X-Api-Key' : apiKey
            },
            'payload' : JSON.stringify(payload),
            'muteHttpExceptions' : true
        }
    );
}


7. 定期実行するためのトリガーの設定
 作成したスクリプトを定期実行するために、GASでプロジェクトの一覧の右端にあるメニューから「トリガー」を選択。表示されたトリガー一覧の画面で、右下の「+トリガーを追加」を選択。トリガー設定画面で次のように設定。

  • イベントのソースを選択       :時間手動型
  • 時間ベースのトリガーのタイプを選択 :分ベースのタイマー
  • 時間の間隔を選択(分)       :5分おき

 これで、5分起きにスクリプトを実行してくれる。

 以上。最後にコード全体を載せたいけど、全体は長いので「続きを読む」へ。

function main() {

    var prop = PropertiesService.getScriptProperties();
    var natureRemoApiToken = prop.getProperty('NATURE_REMO_TOKEN');
    var mackerelApiKey = prop.getProperty('MACKEREL_API_KEY');

    // Mackerelのサービス名
    var serviceNameMyHouse = 'Condition_MyHouse'; // 自宅
    var serviceNameParents = 'Condition_Parents'; // 実家
   
    // Remo からセンサーデータを取得
    var data = getNatureRemoData(natureRemoApiToken);
    var jsonData = JSON.parse(data);
  
    // 自宅と実家、別々にJSONデータをパース
    var payloadMyHouse = formatToPayloadMyHouse(jsonData);
    var payloadParents = formatToPayloadParentsHouse(jsonData);
  
    // Meckerel の別々のサービスにデータを送信
    postMackerelServiceMetric(mackerelApiKey, serviceNameMyHouse, payloadMyHouse);
    postMackerelServiceMetric(mackerelApiKey, serviceNameParents, payloadParents);
}

//
// Nature Remo APIから部屋のデータを取得する
//
function getNatureRemoData(apiToken){
  url = 'https://api.nature.global/1/devices';
  var options = {"headers" : {"Authorization" : "Bearer "+ apiToken}};
  res = UrlFetchApp.fetch(url,options);
  return res
}

//
// Mackerel用にデータを整形する (実家用)
//
function formatToPayloadParentsHouse(jsonData){
  // 実家 (トイレ)
  var te0 = jsonData[0]['newest_events']['te']['val'];
  var hu0 = jsonData[0]['newest_events']['hu']['val'];
  var il0 = jsonData[0]['newest_events']['il']['val'];
  // 実家 (リビング)
  var te1 = jsonData[1]['newest_events']['te']['val'];
  var hu1 = jsonData[1]['newest_events']['hu']['val'];
  var il1 = jsonData[1]['newest_events']['il']['val'];

  var epoch = Date.now() / 1000;

  var payloadParents = [
    {
      'name' : 'room.0.temperature',
      'time' : epoch,
      'value' : Number(te0)},
    {
      'name' : 'room.0.humidity',
      'time' : epoch,
      'value' : Number(hu0)},
    {
      'name' : 'room.0.illumination',
      'time' : epoch,
      'value' : Number(il0)},
    {
      'name' : 'room.1.temperature',
      'time' : epoch,
      'value' : Number(te1)},
    {
      'name' : 'room.1.humidity',
      'time' : epoch,
      'value' : Number(hu1)},
    {
      'name' : 'room.1.illumination',
      'time' : epoch,
      'value' : Number(il1)},
  ];
  return payloadParents
}

//
// Mackerel用にデータを整形する (自宅用)
//
function formatToPayloadMyHouse(jsonData){
  // 自宅 (ばあちゃん)
  var te2 = jsonData[2]['newest_events']['te']['val'];
  var hu2 = jsonData[2]['newest_events']['hu']['val'];
  var il2 = jsonData[2]['newest_events']['il']['val'];
  // 自宅 (寝室)
  var te3 = jsonData[3]['newest_events']['te']['val'];
  var hu3 = jsonData[3]['newest_events']['hu']['val'];
  var il3 = jsonData[3]['newest_events']['il']['val'];
  // 自宅 (リビング)
  var te4 = jsonData[4]['newest_events']['te']['val'];
  var hu4 = jsonData[4]['newest_events']['hu']['val'];
  var il4 = jsonData[4]['newest_events']['il']['val'];

  var epoch = Date.now() / 1000;
  
  var payloadMyHouse = [
    {
      'name' : 'room.0.temperature',
      'time' : epoch,
      'value' : Number(te2)},
    {
      'name' : 'room.0.humidity',
      'time' : epoch,
      'value' : Number(hu2)},
    {
      'name' : 'room.0.illumination',
      'time' : epoch,
      'value' : Number(il2)},
    {
      'name' : 'room.1.temperature',
      'time' : epoch,
      'value' : Number(te3)},
    {
      'name' : 'room.1.humidity',
      'time' : epoch,
      'value' : Number(hu3)},
    {
      'name' : 'room.1.illumination',
      'time' : epoch,
      'value' : Number(il3)},
    {
      'name' : 'room.2.temperature',
      'time' : epoch,
      'value' : Number(te4)},
    {
      'name' : 'room.2.humidity',
      'time' : epoch,
      'value' : Number(hu4)},
    {
      'name' : 'room.2.illumination',
      'time' : epoch,
      'value' : Number(il4)},
  ];
  return payloadMyHouse 
}

//
// Mackerelに送る
//
function postMackerelServiceMetric(apiKey, serviceName, payload) {
  return UrlFetchApp.fetch(
    'https://mackerel.io/api/v0/services/' + serviceName + '/tsdb',
    {
    'contentType' : 'application/json',
    'method' : 'post',
    'headers' : {
      'X-Api-Key' : apiKey
    },
    'payload' : JSON.stringify(payload),
    'muteHttpExceptions' : true
    }
  );
}