見出し画像

子供の写真を撮り忘れていたらGoogle Homeに教えてもらうよ

背景

子供の成長は早いです。
「この成長を毎日写真に残したい!」と思うのですが、どうしても忘れてしまいます。
そこで子供の写真を撮り忘れていたら、Google Homeに教えてもらうことにしました。

概要

僕たち夫婦はスマホで撮った写真は自動でGoogleフォトにアップロードされるように設定しています。
Googleフォトにアップロードされた写真はどこにあるかというと、Google Driveにキレイにフォルダ分けされて保存されていました。

この写真ファイルをGoogle Apps Scriptから参照することで『今日写真撮ったか』を判断しています。
もしも今日はまだ写真を撮っていない場合、GASから家庭内にあるRaspberry Piを経由してGoogle Homeに「写真撮ってないよ!」と教えてもらうようにしています。

概要はこんな感じです。
すでにお気づきかもしれませんが、自動化できる写真はスマホで撮った写真のみです。
最近はスマホで写真を撮ることが増えていますし、僕の使い方ではこれでOKです。

Google Homeに喋らせる準備をする

Google Homeを使っている人にはおなじみのgoogle-home-notifierを使ってGoogle Homeに好きなことを喋ってもらう準備をします。

詳しい説明はこちらを見ながら行いました。
一部ngrokの仕様変更に伴い8時間以上で再起動が必要になる課題についてはこちらを参考に修正しました。

ngrokとは、localで動いているサーバーを、LANの外からアクセスできるようにできるツールです。ローカル環境を一時的に外部に公開できます。

実際のコードがこちらです。

var express = require('express');
var googlehome = require('./google-home-notifier');
var ngrok = require('ngrok');
var bodyParser = require('body-parser');
var app = express();
const serverPort = 8091; // default port
const token = 'XXX'; //追加

var deviceName = 'ribihome';
var ip = '192.168.1.10'; // default IP      //ここにIPを記載

var urlencodedParser = bodyParser.urlencoded({ extended: false });

app.post('/google-home-notifier', urlencodedParser, function (req, res) {

  if (!req.body) return res.sendStatus(400)
  console.log(req.body);

  var text = req.body.text;

  if (req.query.ip) {
     ip = req.query.ip;
  }

  var language = 'ja'; // default language code   //ここに「ja」を記載
  if (req.query.language) {
    language;
  }

  googlehome.ip(ip, language);
  googlehome.device(deviceName,language)       //ここに命令文を追加

  if (text){
    try {
      if (text.startsWith('http')){
        var mp3_url = text;
        googlehome.play(mp3_url, function(notifyRes) {
          console.log(notifyRes);
          res.send(deviceName + ' will play sound from url: ' + mp3_url + '\n');
        });
      } else {
        googlehome.notify(text, function(notifyRes) {
          console.log(notifyRes);
          res.send(deviceName + ' will say: ' + text + '\n');
        });
      }
    } catch(err) {
      console.log(err);
      res.sendStatus(500);
      res.send(err);
    }
  }else{
    res.send('Please GET "text=Hello Google Home"');
  }
})

app.get('/google-home-notifier', function (req, res) {

  console.log(req.query);

  var text = req.query.text;

  if (req.query.ip) {
     ip = req.query.ip;
  }

  var language = 'ja'; // default language code     //ここに「ja」を記載
  if (req.query.language) {
    language;
  }

  googlehome.ip(ip, language);
  googlehome.device(deviceName,language)        //ここに命令文を追加

  if (text) {
    try {
      if (text.startsWith('http')){
        var mp3_url = text;
        googlehome.play(mp3_url, function(notifyRes) {
          console.log(notifyRes);
          res.send(deviceName + ' will play sound from url: ' + mp3_url + '\n');
        });
      } else {
        googlehome.notify(text, function(notifyRes) {
          console.log(notifyRes);
          res.send(deviceName + ' will say: ' + text + '\n');
        });
      }
    } catch(err) {
      console.log(err);
      res.sendStatus(500);
      res.send(err);
    }
  }else{
    res.send('Please GET "text=Hello+Google+Home"');
  }
})

app.listen(serverPort, function () {
  ngrok.connect({authtoken: token, addr: serverPort}, function (err, url) {
    console.log('Endpoints:');
    console.log('    http://' + ip + ':' + serverPort + '/google-home-notifier');
    console.log('    ' + url + '/google-home-notifier');
    console.log('GET example:');
    console.log('curl -X GET ' + url + '/google-home-notifier?text=Hello+Google+Home');
        console.log('POST example:');
        console.log('curl -X POST -d "text=Hello Google Home" ' + url + '/google-home-notifier');
  });
})

コピペ後に変更する場所は、
token(ngrok登録後のAuthから取得)と
ip(Raspberry PiのIPアドレス)のみでいけるはずです。

試しに上記のJavaScriptをRaspberry Pi上で実行後、以下のコマンドを打ってみると、「ドルン!」という音の後にGoogle Homeが喋り始めます。

node example.js
curl -X POST -d "text=Hello Google Home" http://XXX.ngrok.io/google-home-notifier

これでこのURLにPOSTさえすれば好きな言葉をGoogle Homeに喋って貰えるようになります。
ここからは『今日写真撮った?』を判断する処理に入っていきます。

『今日の写真』はGoogle Driveから取得する

スマホで撮った写真はこちらの設定をすることで自動でクラウド上のGoogleフォトへとアップロードされるようになります。
「Googleフォトの写真はどんな感じで保存されているんだろう?」と思ってGoogle Driveを開いてみると、以下のような感じで『Googleフォト>年>月』という体系で保存されておりました。

『月フォルダ』の直下にその月に撮った写真が保存されていきますので、こちらを活用することにします。

『今日写真撮った?』はGoogle Apps Scriptで判断する

「Google Driveのファイルを参照するならGoogle Apps Scriptが親和性が高そう」ということでGoogle Apps Scriptに中継させます。
Google Driveの特定のフォルダを参照したい場合、以下の赤枠ようにURLの末尾を『FolderID』として利用できます。

ということで以下のようなコードを実装しました。

function myFunction() {
  var now = new Date();
  var nowYMD = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0);
  var nowYMD180000 = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 18, 0, 0);
  var nowYear = ('0000' + now.getFullYear()).slice(-4)
  var nowMonth = ('00' + (now.getMonth()+1)).slice(-2) // JavaScriptは0~11で月を取得するため+1します
  var photoFolderId = "XXX";
  var photoFolder = DriveApp.getFolderById(photoFolderId);

  // Google Apps Scriptは"トリガー:毎日特定時刻"がないので条件分岐で18:00に実行します
  if (nowYMD180000.getTime() >= now.getTime()){
    exit;
  }
  
  var takenPhotoFlag = false;
  try {
    var photoYearFolder = photoFolder.getFoldersByName(nowYear).next();
    var photoYearMonthFolder = photoYearFolder.getFoldersByName(nowMonth).next();
    
    var photos = photoYearMonthFolder.getFiles()
    while (photos.hasNext()){
      var photo = photos.next()
      var photoDate = photo.getLastUpdated()
      if (photoDate.getTime() >= nowYMD.getTime()){
        takenPhotoFlag = true;
        break;
      }
    }
  }
  catch (e) {
    // 年・月の変わり目はフォルダが存在しない可能性があるため例外処理します
  }
  
  if (takenPhotoFlag = false){
    sendRasPi("おやおや、今日は写真を撮っていないようですよ?");
  }
}

function sendRasPi(message){
   var payload =
   {
     "text" : message
   };

   var options =
   {
     "method" : "post",
     "payload" : payload
   };

   UrlFetchApp.fetch("http://XXX.ngrok.io/google-home-notifier", options);
}

処理としては以下の流れ。

1. Googleフォトフォルダから今月のフォルダ内にある写真一覧を抽出
2. 本日の日付の写真がない場合はRaspberry Piで立てているngrokにPostする

自分で書いたので結構お粗末だと思います。
特にtry catch文とかかなり無理やり感あります。
キレイにできる方法教えてくれると死ぬほど喜びます。

Google Apps Scriptでは設定でプログラムの定期実行をすることが可能です。
今回は15分のトリガーを発生させて、『18時以降になっても写真を撮っていなかったら、15分ごとにGoogle Homeに教えてもらう』という設定にしました。

感想

今回の方法だと厳密には『今日の子供の写真』ではなく『今日の写真』しか取得できていないので、子供の写真がなくても写真が1枚でも撮られていたらGoogle Homeは教えてくれなくなります。
実際はスマホの写真はほとんどが子供なので問題はないのですが、Googleフォトは子供の顔をすでに学習済みだし、もしかしたら子供の写真を検索できるのかもしれません。

本当は『子供の写真が撮られていなかったら子供の写真を撮る』っていうロボットを作りたかったのですが、僕のスキルでは遠く及びませんでした。
カメラが自動で動くような感じを想定しているので、ルンバにカメラを乗せたり、ロボホンを使ったりするような感じでしょうか。
もしくは『子供が毎日必ず座る場所に来たら写真を撮る』っていう方法もよさそうです。

何はともあれこれで子供の写真がより増えていきそうです。
365日カレンダーとか作ったら楽しそうですね。

この記事が気に入ったらサポートをしてみませんか?