2019/05/17

raspberrypi3のカメラを使ってメール応答型のスクリプト node.jsを活用 

やりたいこと
メールを受信すると、raspberrypi3のカメラで撮影し、
メールに添付して指定のメールアドレスに送信するスクリプト。
子供や愛犬の見守りなど目的は同じになるが、
玄関の施錠をしたか?を最寄りの駅で気になった場合に
万が一の為に戻りたくないので作成。
玄関が開き戸だったら、もっといろいろな仕掛けができたのだが・・・。


 補足
 メール部分は自分でサーバ建てるのが大変なので、
 Gmailを利用し、node.jsを使ってメールの送信を常に監視し、
 監視中にメールが来るとそれをトリガーにして
 カメラで撮影、メールを送信する。

 node.jsは『非同期スクリプト』であるため、
 fs等を使う場合は、同期させた処理を行う必要がある。
 ここで、嵌ってしまった・・・。
 カメラの処理に時間がかかり、ファイルが生成される前に
 メール送信のためのファイル加工が動作してしまい、
 エラーが返って来てしまうので、
 別々に動作確認が終わったら、promise 等で同期処理をさせる。
 
 前提
 ・raspberry-pi3のカメラモジュールを認識済み
 ・Gmailのアカウント取得、安全性の低いアプリの許可」を有効にしてある。
 ・nodeが動かせること

成果物◆◆
'use strict';
var inbox = require('inbox');
//var nodemailer = require('nodemailer');
//var iconv = require('iconv');
//var conv = new iconv.Iconv("UTF-8", "UTF-8");
const simpleParser = require('mailparser').simpleParser;
var client = inbox.createConnection(false, 'imap.gmail.com', {
    secureConnection: true
    ,auth: {
        user:'Gmailアカウント@gmail.com'
        ,pass:'Gmailパスワード'
    }
});

client.on('connect', function() {
    console.log('connected');
    client.openMailbox('INBOX', function(error) {
        if (error) throw error;
    });
});

client.connect();

client.on("new", function(message) {
    console.log('日時:' + message.date);
    console.log('送信者:' + message.from.name + '-' + message.from.address);
    console.log('タイトル:' + message.title);
var stream = client.createMessageStream(message.UID);
simpleParser(stream)
    .then(mail=> {
        console.log('本文(HTMLテキスト):' + mail.textAsHtml);
    })
    .catch(err=> {
        console.log(err);
    });

//node.jsはデフォルトで非同期処理を行うため、
//asyncを用いてカメラがsnap.jpgを生成するのを待ち
//Gmailの送信(snap.jpgの添付)、snap.jpgの削除を行っている
//pi-cameraでは上書き保存ができないため、メール送信後に削除しておく。

const async = require('async'); //同期処理のためasync呼び出す

async.series([
  function(callback) {
    console.log('カメラの処理開始');
    //pi-camera の動作ここから
    const PiCamera = require('pi-camera');
    const myCamera = new PiCamera({
      mode: 'photo',
      output: `${ __dirname }/snap.jpg`,
      width: 640,
      height: 480,
      nopreview: true,
    });
    myCamera.snap()
      .then((result) => {
        // Your picture was captured
      })
      .catch((error) => {
         // Handle your error
      });
   //pi-camera の動作ここまで
    //安全をみて7秒ディレイ
    setTimeout(callback, 7000);
  }, function(callback) {
    console.log('メールの返信開始');
    //Gmailで送信ここから
    var nodemailer = require('nodemailer')
    var fs = require('fs');
    // メッセージ内容
    var message = {
      from    : '送信元@gmail.com',
      to      : '送信先@gmail.com',
      subject : 'Reply to request',
      text    : 'Reply to request,Picture of the Pi-Camera !',
      attachments : [{
      filename: 'snap.jpg',
      content: fs.readFileSync('snap.jpg') //カメラのデータ生成待ち
    }]
    };
    var smtpConfig = {
      host: 'smtp.gmail.com',
      port: 465,
      secure: true, // SSL
      auth: {
        user : 'Gmailアカウント@gmail.com',
        pass : 'Gmailパスワード'
      }
    };
    var transporter = nodemailer.createTransport(smtpConfig);
    transporter.sendMail(message, function(err, response) {
      console.log(err || response);
    });
    //ファイル削除
    try {
      fs.unlinkSync('snap.jpg');
      console.log('snap.jpgを削除しました。');
    } catch (error) {
      throw error;
    }
  //Gmailで送信ここまで
    setTimeout(callback, 1000);
  }, function(callback) {
    return console.log('処理終了');
  }
], function(err, results) {
  if (err) {
    return console.log('err[' + err + ']');
  }
});

}); // client.connect() を閉じる。



コマンド自体はプロンプト状態なので停止はcrtl+Zにて行う。
sshで操作する場合は、forever等で動かさないと、ターミナル閉じた瞬間に
Killされるので注意が必要。

動作部分解説

◆メール受信トリガー部分
  node.jsのパッケージインストール
  $ npm install inbox
  $ npm install mailparser
まんま、@hideito2000様の掲載内容を使わせていただいた。
//ソース
client.on("new", function(message) {
console.log('日時:' + message.date);
console.log('送信者:' + message.from.name + '-' + message.from.address);
console.log('タイトル:' + message.title);
var stream = client.createMessageStream(message.UID);
simpleParser(stream)
.then(mail=> {
console.log('本文(HTMLテキスト):' + mail.textAsHtml);
})
.catch(err=> {
console.log(err);
});

//この部分にカメラ処理とメール送信を組み込む
});
//ソースここまで

◆pi-cameraのインストール
$ npm install pi-camera

作成者のページのサンプルをそのまま使う
//ソース
const PiCamera = require('pi-camera');
const myCamera = new PiCamera({
  mode: 'photo',
  output: `${ __dirname }/snap.jpg`,
  width: 640,
  height: 480,
  nopreview: true,
});
myCamera.snap()
  .then((result) => {
    // Your picture was captured
  })
  .catch((error) => {
     // Handle your error
  });
//ソースここまで
 
◆nodemailer でメールを送信させるので、インストール
$ npm i -D nodemailer

ソースは皆さまの内容を参照させていただいた。
ありがとうございます。
 //ソース
var mailer = require('nodemailer');
mailer.SMTP = {
    host: 'smtp.gmail.com',
    port: 465,
    secure: true, // SSL
    auth: {
      user : 'Gmailのid@gmail.com,
      pass : 'Gmailのパスワード'
    }
};
fs.readFile("./snap.jpg", function (err, data) {
    mailer.send_mail({
        sender: '送信元@gmail.com',
        to: '送信先@gmail.com',
        subject: 'snapshot-Ent!',
        body: 'Snapshot of the Entrance !',
        attachments: [{'filename': 'snap.jpg', 'content': picture}]
    }), function(err, success) {
        if (err) {
            // Handle error
        }

    }
});
//ソースここまで

 
◆ファイルの削除◆  
 try {
      fs.unlinkSync('snap.jpg');
      console.log('snap.jpgを削除しました。');
    } catch (error) {
      throw error;
    }

 fsは非同期だが、同じコマンドであれば記載の順に動くようだ。
駄目な場合は、 async の回数を増やせば良い。
 
◆同期処理◆

async.series([
  function(callback) {    
 console.log('処理開始');
このcallback間にメソッドを記載
    setTimeout(callback, 1000);
 }, function(callback) { console.log('処理2開始');
    setTimeout(callback, 1000);
    });
}, function(callback) {
    return console.log('処理終了');
  }
], 
 

期待した動きができたので、満足。

0 件のコメント:

コメントを投稿