野次馬エンジニア道

野次馬な気持ちでプログラミングをあれこれと綴ります

Google Home + Raspberry PiでHome Automation入門

昨年の半額セールで購入したGoogle Home Mini。まずは家のリビングのTVとエアコンと照明をオンオフできるような機能を自分で作ってみる*1

準備

赤外線の学習リモコンの類やIoT対応した照明など、機器は色々あるが、レビューを見て評判のよさそうな以下をセレクト。飽きたら別の用途に使ってもよいので汎用性の高そうなRaspberry Pi 3 の Model Bを購入。OSはRaspbianにして付属のマニュアルに沿ってインストール。インストールにはUSBとマウスが必要。

ビット・トレード・ワン USB赤外線リモコンアドバンス ADIR01P

ビット・トレード・ワン USB赤外線リモコンアドバンス ADIR01P

とりあえず全部つなぐ

赤外線モジュールはUSBのmini-USBという一昔前の端子であることに注意。大きい100円ショップで辛うじて売られているものを救出して今回は利用。

f:id:notta55:20180105222934j:plain

コマンドラインツールのビルド

の記事を参考にする。公式にサポートされているのはWindows環境用のC#の実装のようだが、それをlinux上で動作するように ポートしている模様。下記からダウンロードしてセルフビルドしてmake installしておく。

そのままだと実行にはroot権限が必要となるため追加で設定*2。刺した状態でlsusbをしてVendorとidProductをメモして

#sudo vi /etc/udev/rules.d/bittrade.rules
SUBSYSTEM=="usb", ATTRS{idVendor}=="22ea", ATTRS{idProduct}=="003a", MODE="0666"

こんな感じで記述してsudo udevadm control --reload-rulesで設定を読み込み。

IFTTTの設定

ifttt.com

こちらも記事が多く見つかるが、単純なon/offであれば、"Say a phrase with a text ingredient"を選んでWebHookを設定すればOK。 f:id:notta55:20180105231409p:plain

Dynamic DNSの設定

WebHookを設定する際に環境的にグローバルIPとルータのDMZの設定などをする必要がある場合は追加で設定。 Raspberry Pi 3でもDDClientが利用可能*3。 自分の環境ではeth0はローカルのIPになってしまうのでグローバルのIPを設定。

# sudo vi /etc/ddclient.conf
#use=if, if=wlan0
use=web, web=checkip.dyndns.org
ssl=yes

/etc/init.d/ddclient restartで正しく更新されるかテスト。

WebHookの実装

学習したリモコンデータはコマンドラインツールでローカルにファイルで出力されるのでそれをそのまま使ってしまう(手抜き)。 ポイントとしては、IFTTTで設定されたTextの処理。

  • スペースが入っている場合があるのでtrimしてしまう

  • シノニム的な言い換え('電気','ライト','明かり')に備えてマップを持ってしまう

var fs = require('fs');
var express = require("express");
var bodyParser = require("body-parser");
var exec = require('child_process').exec;
var app = express();

app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

var server = app.listen(3000, function(){
    console.log("Node.js is listening to PORT:" + server.address().port);
});

var targetNameMap = {
    'light' : ['電気','ライト','明かり'],
    'tv' : ['TV','テレビ'],
    'aircon' : ['エアコン','暖房','冷房']
};

function execBTO(path){
    exec('/usr/local/bin/bto_advanced_USBIR_cmd -d `cat '+__dirname+'/'+path+'`',
        function (error, stdout, stderr) {
            if(stdout){
                console.log('stdout: ' + stdout);
            }
            if(stderr){
                console.log('stderr: ' + stderr);
            }
            if (error !== null) {
                console.log('Exec error: ' + error);
            }
    });
}

function processAction(target, cmd){
    Object.keys(targetNameMap).forEach(function(target_){
        if( targetNameMap[target_].indexOf(target) === -1 && target_ !== target){
            return;
        }
        var path = 'data/'+target_+'/'+cmd;
        fs.access(__dirname+'/'+path, (fs.constants || fs).R_OK, function(error){
            if (error) {
                if (error.code === "ENOENT") {
                    console.log('ENOENT error '+__dirname+'/'+path);
                }
            }
            execBTO(path);
        });
    });
}

app.post("/api/action", function(req, res, next){
    if( !req.body.target || !req.body.cmd ) {
        res.json({status:'NG'});
        return;
    }
    processAction(req.body.target.trim(), req.body.cmd);
    res.json({status:'OK'});
});

あとはNodejsの起動スクリプトに入れてforeverを使って常駐化すると良い。

動作確認

  • OK Google リビングのエアコンをつけて
  • OK Google リビングのTVをつけて
  • OK Google リビングの電気をつけて

といった感じで赤外線モジュール経由でコントロールが可能。

所感

一通り動作したので、Raspberry Pi Zero Wへ乗せ変えて暫く運用予定*4。が、やはりインテリア的な見た目やメンテナンスを考えると

Nature Remo

Nature Remo

が欲しい。室内に置くものなので機能観点だけでは割り切れないということなのだろう。

*1:本当はNature Remoが欲しい

*2:c++ - libusb cannot open USB device, permission isse. NetBeans/Ubuntu - Stack Overflow

*3:Dynamic DNSの例 https://support.google.com/domains/answer/6147083?hl=ja

*4:抽選らしいので当たるといいですが