はじめに

自宅のスマートスピーカー(Google Home mini もしくは Echo Dot)に「おはよう」と言った時に日が出ていない時間帯の時だけ部屋のシーリングライトを点灯できたら便利だなと思い作ってみました。 今回は Google Home mini を対象に作成しています。 今回の作成にあたりGoogle Home 、 IFTTT 、 Firebase 、 Node.js 、 irMagician を使ってシーリングライトを音声操作するを参考にさせていただきました。

今回のハード構成と役割

  • Google Home mini(音声入力受付)
  • Raspberry Pi Zero W(CLI アプリで irMagician 操作)
  • irMagician(シーリングライトに赤外線信号を送る)

使用したサービス

  • IFTTT
  • Firebase
  • OpenWeatherMap

大まかな仕様

  1. Google Home mini に「 OK 、 Google  おはよう」と話しかけると IFTTT が「おはよう」の箇所を拾う。
  2. IFTTT が「おはよう」というキーワードを拾った時に Firebase の Realtime Database に「 goodmorning 」というテキストを PUT する。
  3. Raspberry Pi Zero W で動作している CLI アプリ(Node.js)は Firebase の Realtime Database に「 goodmorning 」が書き込まれると OpenWeatherMap に現在の気象情報(日の出、日の入り時刻も含む)の問い合わせを行う。
  4. OpenWeatherMap から JSON が返ってくるのでパーサーで解析し、雲の量が50%以上の時は照明をつける為のシェル(irMagician 操作)を実行する。
  5. 雲の量が50%未満の時は現在時刻が日の出より前か日の入りより後であれば照明をつける為のシェル(irMagician 操作)を実行する。

イメージ:概略図

Raspberry Pi Zero W について

今回、私は Raspberry Pi Zero W スターターキットを購入しました。 付属の MicroSD に Raspbian が入っていたり、 HDMI 、 USB の変換ケーブル、電源がセットになっているのでおすすめです。 又、 Raspberry Pi Zero W は USB のポートが一つしかないので USB ハブも必須かと思います。 初期設定についてはRaspberry Pi Zero と Zero W セットアップ 完全ガイドを参考にしました。 特にタイムゾーン、キーボードの設定は必須です。

Node.js のバージョンについて

Raspbian の Node.js はバージョンが古いので上げる必要があります。私は Node-RED をアップグレードしたらついでにバージョンも上がりました。   ちなみに Raspbian には既に Node-RED が入っていますが、 Raspberry Pi Zero W の場合は下記のマニュアルインストールでないとアップグレードはできないようです。

bash <(curl -sL https://raw.githubusercontent.com/node-red/raspbian-deb-package/master/resources/update-nodejs-and-nodered)

irMagician の設定

今回、シーリングライトの操作は大宮技研製の学習型赤外線リモコン irMagician を使用しています。
irMagician と Raspberry Pi Zero W は USB で接続します。下記2点を満たしていると irMagician が使用できる状態になっています。

  • irMagician の右ランプが点滅(点灯は NG)している。
  • Raspberry Pi Zero W の dev ディレクトリ配下に ttyACM0がある。
  • 又、シェルから irMagician を操作したい為、@tamaki さんが作成された CLI ツールとしても使える「 irmagician 」を使用させていただきました。 Node モジュール「 irmagician 」を作りました。

    irmagician のインストールと serialport のリビルド

    今回は CLI として使用したいのでコンソールで下記を実行してインストールしました。 ディレクトリの権限等の都合上 sudo ではなくスーパーユーザーで実行しています。

    $su
    $npm -g install irmagician
    

    スーパーユーザーのまま、下記を実行して serialport をリビルドしました。

    $cd /usr/local/lib
    $npm rebuild serialport --build-from-source
    
    リビルドが終わったのでスーパーユーザーを終了しました。

    $exit
    

    irMagician でリモコンのキャプチャを取り、 json ファイルに出力する。

    コンソールで下記を実行し、「…」が表示されたらシーリングライトのリモコンを irMagician に向けて照明 ON ボタンを押します。

    $irMagician capture
    

    コンソールで下記を実行し、キャプチャした内容を json ファイルに出力する。 今回は lightsw.json という名前で出力していますが、名前は何でも構いません。

    $irMagician dump lightsw.json
    

    Firebase の Database 設定

    Google Home mini と Raspberry Pi Zero W の情報の橋渡しをしてくれる Database を登録、設定します。 私は下記の手順で設定しました。

    1. Firebaseにアクセスする。
    2. 画面右上のコンソールへ移動をクリックし、 Google アカウントでログインする。
    3. プロジェクトを追加をクリックする。
    4. プロジェクト名と国/地域を指定してプロジェクトを作成ボタンをクリックする。
    5. Database の画面に遷移するので Realtime Database のスタートガイドボタンをクリックする。
    6. テストモードで開始を選択し、有効にするをクリックする。   ※テストモードで開始した場合、 Database を誰でも変更可能になる為、プロジェクト名が漏洩すると家のシーリングライトを他人から操作されてしまう恐れがあります。注意してください。
    7. 上部にあるデータタブをクリックし、プロジェクト名の横にある+ボタンをクリックする。
    8. 1段下に名前と値の行ができるので名前に smarthome と入力して値の横にある+ボタンをクリックする。
    9. さらに一段下に名前と値の行ができるので名前に word 、値に""と入力して追加ボタンをクリックする。

    私の Database の内容は下記のような構成となりました。

    プロジェクト名
      └ smarthome
         └ word:""
    

    Firebase と接続する為の Node.js モジュールをインストール

    コンソールで下記を実行してインストールしました。

    $npm -g install firebase
    

    IFTTT の設定

    Google Home mini(Google Assistant)と Firebase の連携部分を作成します。 今回は単純なキーワード(goodmorning) だけ受け取れれば良いので、 Say a simple phrase を使いました。 下記は私が実際に行った手順です。 What do you want to say?はトリガーとなる言葉で What do you want the Assistant to say in response?はその言葉を受け取った時に Google Home mini が発声する言葉です。

    1. IFTTTにアクセスする。
    2. Google Home mini に登録されている google アカウントで Sign in する。
    3. 画面上の My Applets をクリックする。 イメージ:IFTTT1
    4. New Applet をクリックする。 イメージ:IFTTT2
    5. +this のところをクリックする。 イメージ:IFTTT3
    6. Google Assistant をクリックする。 イメージ:IFTTT4
    7. Say a simple phrase をクリックする。 イメージ:IFTTT5
    8. What do you want to say?におはようと入力する。
    9. What do you want the Assistant to say in response?に「おはようございます」と入力する。
    10. Language を Japanese にセットする。
    11. Create trigger をクリックする。 イメージ:IFTTT6
    12. +that のところをクリックする。 イメージ:IFTTT7
    13. Webhooks をクリックする。 イメージ:IFTTT8
    14. Make a web request をクリックする。 イメージ:IFTTT9
    15. URL に下記を入力する
      Firebase の databaseURL/smarthome/word.json
      
      ※ Firebase の databaseURL は Firebase のプロジェクトのトップページにあるウェブアプリに Firebase を追加をクリックすると表示されます。
    16. Method を PUT にセットする。
    17. Content Type を text/plain にセットする。
    18. Body に"goodmorning"を入力する。
    19. Create action をクリックする。 イメージ:IFTTT10
    20. Finish をクリックする。

    この段階で Googe Home mini に「 OK Google おはよう」と話しかけてると Firebase の Database の word の値に"goodmorning"が入り、下記のようになります。

    プロジェクト名
      └ smarthome
         └ word:"goodmorning"
    

    OpenWeatherMap のアカウント作成

    弊社では天気予報 APIを提供していますが、今回はOpenWeatherMap を使用させていただきました。 OpenWeatherMap は無料で使用できるのですが、アカウントを作成して API キーを取得する必要があります。 尚、パラメータ等についてはOpenWeatherMap で気象情報をゲットしよう | SG Labsを参考にさせていただきました。

    1. OpenWeatherMapにアクセスする。
    2. 画面右上にある Sigh up からアカウント登録する。
    3. アカウント作成後に my page に遷移するので API Keys タブから API キーが参照できる。

    CLI アプリ作成

    Node.js で動作するアプリを index.js というファイル名で作成しました。

    var firebase = require("firebase");
    
    // 下記には Firebase のプロジェクトのトップページにある
    // ウェブアプリに Firebase を追加をクリックすると表示される内容を入力
    var config = {
        apiKey: “ xxxx",
        authDomain: “ xxxxx",
        databaseURL: “ xxxx",
        projectId: “ xxxxxx",
        storageBucket: “ xxxxxxx",
        messagingSenderId: “ xxxxxx"
    };
    firebase.initializeApp(config);
    
    //firebase の変更監視
    const path = "/smarthome";
    const key = "word";
    const db = firebase.database();
    db.ref(path).on("value", function(changedSnapshot) {
      // smarthome/word の内容が変更になった
      const value = changedSnapshot.child(key).val();
      if (value=="goodmorning") {
    
        // smarthome/word の値に"goodmorning"が入ったので
        // OpenWeatherMap に気象情報を問い合わせ
        // xxxx は OpenWeatherMap の API キーを記入
        let http = require('http');
        const URL = "http://api.openweathermap.org/data/2.5/weather?id=1850147&units=metric&appid=xxxx";
    
        http.get(URL, (res) => {
          let body = '';
          res.on('data', (chunk) => {
            body += chunk; // API で取得した情報を連結
          });
    
          res.on('end', (res) => {
            // API から最後まで情報を取得したので判定開始
            let light_flg = false; // true の時にシーリングライトをつける
            res = JSON.parse(body); // API で取得した JSON をパース
            if( res.clouds.all >= 50 ){
              // 雲量が50%以上なのでシーリングライトをつける
              light_flg = true;
            }else{
              // API で取得できる日の出、日の入り時間は10桁(ms 秒を加味しない)で
              // new Date().getTime()で取得できる時間は13桁なので桁を合わせて比較する
              let now_time = new Date().getTime() / 1000 ;
              if( now_time < res.sys.sunrise || now_time > res.sys.sunset ){
                // 日の出前もしくは日の入り後なのでシーリングライトをつける
                light_flg = true;
              }
            }
            if(light_flg==true){
              // ここで子プロセスを作ってシェルスクリプトからシーリングライトを操作
              const exec = require('child_process').exec;
              exec("sh light_on.sh");
            }
          });
        }).on('error', (e) => {
           console.log(e.message); //気象情報取得エラー時
        });
    
        // firebase の smarthome/word の値をリセットする
        db.ref(path).set({[key]: ""});
    
      }
    });
    

    シーリングライトを操作するシェルスクリプト lighton.sh の内容は下記2行のみです。

    irMagician write lightsw.json
    irMagician play
    

    irMagcian の操作でシェルスクリプトを利用した理由

    irmagician は Node.js モジュールなので index.js 内に irMagician.write("lightsw.json")と irMagcian.play()を呼び出すことで操作することもできるのですが、今回は操作する対象がシーリングライトのみ且つ、 write と play の同期をとるのが面倒だったのでシェルスクリプトを利用しました。 シーリングライトと一緒にテレビの電源も入れたいといった時には、かえってシェルスクリプトだと、同期がとりずらいので irMagician.write("lightsw.json")と irMagcian.play()を同期させて処理させるやり方が良いかと思います。

    CLI アプリの実行

    コンソールで下記を入力すると待ち受け状態になるので、この状態で Google Home mini に 「 OK Google  おはよう」と話しかけると「おはようございます」と返答があり、シーリングライトをつける条件を満たしている場合はシーリングライトが ON になりました。 ただし、 ssh で実行させた場合にコネクションが切れると待ち受け状態が終わってしまうので、永続化したい場合はforeverを使用すると良いかと思います。

    node index.js
    

    感想

    現状ではまだまだ改造しがいがあると感じています。 Firebase の Database の smarthome/word が"goodmorning"に書き変われば今回作ったアプリが動作するので、 IFTTT の+this の部分で Amazon Alexa を指定すれば Echo でも動作させることができますし、 IFTTT を使わずに Actions on Google 等で書き換えても動作させることができると思います。 又、 CLI アプリ自体もその他 API を組み合わせて条件を複雑にしたりしても面白いかと思います。今回は使用できませんでしたが、天気予報 APIでは洗濯指数や傘指数といった豊富なオプション情報が用意されています。商用利用を検討されている方は是非ご一考いただければと思います。