コンテンツへスキップ

めざましじゃんけん 画像検出システム

めざましじゃんけん結果

テレビ画像よりフジテレビ、めざましテレビ内で実施されるめざましじゃんけんの結果を蓄積します。めざましじゃんけんは、デジタル放送のコンテンツであり、B-CAS(ビーキャス)カードを用いた受信装置により、データ放送を画面表示させ、リアルタイムに参加する必要があります。番組録画しても、データ放送のコンテンツは録画することは出来ません。

  1. 環境準備(インストール)

  2. Darknet/YOLOで学習モデル準備

  3. システム実装と結果公開

  4. プログラム関連技術

本システム実装に関連した、プログラム関係のTips。

    • PHP
    • Python
  1. 【機械学習導入】ニューラルネットワークChainerフレームワーク

    • 導入方法
    • 機械学習
    • Out of Memoryへの対応
    • 学習モデルのRaspberry Piでの実行

SendGridのInbound Email Parseを利用

SendGridは、メール配信などで利用されている方は多いのかもしれませんが、メール受信トリガーにWEB API経由でPOSTしてくれるサービスも提供されているようです。メール受信時のWEB APIプッシュの実現方法を探している際に見つけました。
グローバルで、企業向けにサービスを展開されているので、サービス品質などに間違いはなさそうです。
IFTTTでメール受信をしっかりとトリガー出来ていれば本サービスを利用することも無かったかもしれませんが、利用してみてPubSubというような使い方が出来そうなのでメインで利用する予定です。一般向け製品では通知手段がメールというサービスが多いです。

SendGrid | メールを成功の原動力に。

DNS設定に関しては、利用しているVALUE-DOMAIN(バリュードメイン)を例に説明を行います。

概要

sendgridで扱うEmail専用のサブドメインを準備します。サブドメイン宛のメールはすべてSendGridのメールサーバーに送信されるように設定し、SendGridでは、メール受信時のアクション(アクセスするWEB APIのURL)を設定します。あとは、受信を受けたWEB APIがメールの件名や差出人などにより実施したい処理を実施します。
現時点では、モーションセンサーの通知などをメール受信し、各種情報のデータ化としてデーターベースへの蓄積を行っております。

  1. SendGridサービス登録
  2. SendGridサービスで利用ドメインの認証
  3. SendGridでメール受信時の動作設定(メールをPOSTするWEB APIのアドレスを設定)
  4. 自ドメインのSendGridへのメール転送設定(DNS設定)

1. SendGridサービス登録

SendGridのサイトよりアカウントの新規登録を行います。
メールアドレスを登録し、簡単なアンケートや情報入力を行い、本登録を待ちます。人の手でサービス品質を保っているようで、以下メールが来て1日程度登録を待ちました。
実際、住所登録に不備があり、住所修正を行い、本登録が終了しました

SendGridにご登録いただき、誠にありがとうございます。

ログイン用のユーザ名につきましては、ご登録内容の確認およびアカウントのご利用準備が整い次第、別途ご連絡いたします(通常、翌2営業日以内)。
※ユーザ名はメールアドレスではございません。

なお、ご登録内容について確認のご連絡をさせていただく場合がございますので、あらかじめご了承ください。

サービス登録後の、利用者向けポータルのトップページ。

2. SendGridサービスで利用ドメインの認証

SendGrid-Inbound Email Parse Webhookを利用すると、利用条件としてドメインの認証が必要と、ドメイン認証の画面に誘導されます。以下の画面が、まずドメインの認証が必要とダイアログが出ています。

ドメインの認証方法は、利用しているドメインサーバのDNS設定にDNSレコードの追加を行います。SendGridで指定されたDNSレコードを追加する必要があります。SendGrid側でDNS変更を確認し、DNS変更の確認が完了すれば、ドメインの認証が終了します。
ドメイン認証は、SendGridのDomain Authentication(Authenticate Your Domain)のダイアログに従い、Install DNS Recourdsまで進み、登録するDNS情報を入手して下さい。

取得した追加するDNSレコードを自分のDNSサーバに追加します。
今回は、利用しているVALUE-DOMAIN(バリュードメイン)での設定例を例示して起きます。
ポイントは、DNS設定には時間を要するので、正しく設定出来た状態で、しばらく待ちましょう。数時間で反映されると思いますが、各種サーバなどの設定に依存します。



追加したDNSレコードが確認され、ドメイン認証が完了される際に表示されるWEB画面です。「Verify Your Domain」It worked!と表示されました。

日本法人 公式サイトの関連するQAです。
独自ドメイン利用(Sender Authentication)および設定時のDNSレコードについて、詳しく教えてください

3. SendGridでメール受信時の動作設定(メールをPOSTするWEB APIのアドレスを設定)

ドメイン認証が完了すると SendGridでSendGrid-Inbound Email Parse Webhook(メール受信時とWeb APIのマッピング)設定となります。
SendGridで受信するメールアドレスのドメインおよびWeb APIのURLを設定します。
オプションで、SendGrid側でのSpamチェック有無と受信するPOSTデータを指定します。

4. 自ドメインのSendGridへのメール転送設定(DNS設定)

最後に、SendGridへ転送したいメールアドレスを自分のDNSサーバに設定します。
この設定が終了すると、DNS情報がインターネットに反映され次第、受信メールのSendGridへ転送、SendGridでメール受信時にWEB APIへのメールデータPOSTが開始されます。
必ず、WEB API側の準備後に、DNSレコードの追加を行って下さい。(最低限WEB受信可能な状態)
以下の例では、MXレコードを追加し、対象のサブドメインをSendGridのメールサーバーに転送されるように指定します。

Inbound Email Parse WebhookのPOST情報

HEADERSThe raw headers of the email.
DKIMA string containing the verification results of any DKIM and domain keys signatures in the message.
CONTENT-IDSA string containing the number of attachments.
TOEmail recipient field, as taken from the message headers.
HTMLHTML body of email. If not set, email did not have an HTML body.
FROMEmail sender, as taken from the message headers.
SENDER_IPA string of the sender’s ip address.
SPAM_REPORTSpam Assassin’s spam report.
ENVELOPEA string containing the SMTP envelope. This will have 2 variables: to, which is a single-element array containing the address that we received the email to, and from, which is the return path for the message.
ATTACHMENTSNumber of attachments included in email.
SUBJECTEmail Subject.
SPAM_SCORESpam Assassin’s rating for whether or not this is spam.
ATTACHMENT-INFOA JSON map where the keys are named attachment{X}. Each attachment key points to a JSON object containing three fields, filenametype, and content-id. The filename field is the name of the file (if it was provided). The type field is the media type of the file. X is the total number of attachments. For example, if the number of attachments is 0, there will be no attachment files. If the number of attachments is 3, parameters attachment1, attachment2, and attachment3 will have file uploads.
CHARSETSA string containing the character sets of the fields extracted from the message.
SPFThe results of the Sender Policy Framework verification of the message sender and receiving IP address.

 

[Date] array(16) {
  ["headers"]=>
  string(1970) "Received: by mx0047p1mdw1.sendgrid.net with SMTP id 6WCVv7KAWn Wed, 27 Jul 2016 20:53:06 +0000 (UTC)
Received: from mail-io0-f169.google.com (mail-io0-f169.google.com [209.85.223.169]) by mx0047p1mdw1.sendgrid.net (Postfix) with ESMTPS id AA9FFA817F2 for <example@example.comom>; Wed, 27 Jul 2016 20:53:06 +0000 (UTC)
Received: by mail-io0-f169.google.com with SMTP id b62so81593819iod.3 for <example@example.comom>; Wed, 27 Jul 2016 13:53:06 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sendgrid.com; s=ga1; h=mime-version:from:date:message-id:subject:to; bh=DpB1CYYeumytcPF3q0Upvx3Sq/oF4ZblEwnuVzFwqGI=; b=GH5YTxjt6r4HoTa+94w6ZGQszFQSgegF+Jlv69YV76OLycJI4Gxdwfh6Wlqfez5yID 5dsWuqaVJZQyMq/Dy/c2gHSqVo60BKG56YrynYeSrMPy8abE/6/muPilYxDoPoEyIr/c UXH5rhOKjmJ7nICKu1o99Tfl0cXyCskE7ERW0=
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:from:date:message-id:subject:to; bh=DpB1CYYeumytcPF3q0Upvx3Sq/oF4ZblEwnuVzFwqGI=; b=Sq6LVHbmywBdt3sTBn19U8VOmelfoJltz8IcnvcETZsYwk96RBxN+RKMN5fOZSKw4j 15HrgdIFfyDmp67YK0ygvOITlTvZ6XY5I0PtnvDtAQt79kS3tKjI3QKJoEp/ZjIjSzlL KG7agl6cxFgBbIN0yHWBOvy3O+ZXY8tZdom1yOvULjmjW1U9JkdOs+aJ6zq4qhZX/RM/ tIgLB461eJ5V95iQDDc5Ibj9Cvy4vJfXLQRO0nLVQAT2Yz58tkEO1bDZpWOPAyUNneIL yhIWp+SpbuqhMA68mq0krG1PjmWalUbpVcGJIGuOKB9mQFFo/MqdrUCjvYnyo1jPLPeX psdQ==
X-Gm-Message-State: AEkoousvdxmDoxLlTUYJ1AOmCGJv77xRBBlfKv6YrthH0M2NueMwlOxUD6t8nidE9uonXbdJ/DQy/chmHUnN//a4
X-Received: by 10.107.6.101 with SMTP id 98mr38024553iog.41.1469652785829; Wed, 27 Jul 2016 13:53:05 -0700 (PDT)
MIME-Version: 1.0
Received: by 10.107.48.17 with HTTP; Wed, 27 Jul 2016 13:53:05 -0700 (PDT)
From: Sender Name <example@example.com>
Date: Wed, 27 Jul 2016 14:53:05 -0600
Message-ID: <CAN_P_JMvV7ZpAQhOnDienypLrJmuhN=LQWweu4yScw4jQyXY2w@mail.gmail.com>
Subject: Different File Types
To: example@example.comom
Content-Type: multipart/mixed; boundary=001a113f8ad03e85160538a4343c
"
  ["dkim"]=>
  string(22) "{@sendgrid.com : pass}"
  ["content-ids"]=>
  string(37) "{"ii_1562e2169c132d83":"attachment1"}"
  ["to"]=>
  string(26) "example@example.comom"
  ["html"]=>
  string(479) "<div dir="ltr">Here&#39;s an email with multiple attachments<div><br></div><div><img src="cid:ii_1562e2169c132d83" alt="Inline image 1" width="455" height="544"><br clear="all"><div><br></div>-- <br><div class="gmail_signature" data-smartmail="gmail_signature"><div dir="ltr"><img src="https://sendgrid.com/brand/sg-logo-email.png" width="96" height="17"><br><div><br></div></div></div>
</div></div>
"
  ["from"]=>
  string(33) "Sender Name example@example.com"
  ["text"]=>
  string(139) "Here's an email with multiple attachments
"
  ["sender_ip"]=>
  string(14) "209.85.223.169"
  ["spam_report"]=>
  string(844) "Spam detection software, running on the system "mx0047p1mdw1.sendgrid.net", has
identified this incoming email as possible spam. The original message
has been attached to this so you can view it (if it isn't spam) or label
similar future email. If you have any questions, see
@@CONTACT_ADDRESS@@ for details.

Content preview:  Here's an email with multiple attachments [image: Inline image
   1] -- [...]

Content analysis details:   (2.6 points, 5.0 required)

 pts rule name              description
---- ---------------------- --------------------------------------------------
 0.8 HTML_IMAGE_RATIO_02    BODY: HTML has a low ratio of text to image area
 0.0 HTML_MESSAGE           BODY: HTML included in message
 1.8 HTML_IMAGE_ONLY_08     BODY: HTML: images with 400-800 bytes of words
 0.0 T_MIME_NO_TEXT         No text body parts

"
  ["envelope"]=>
  string(66) "{"to":["example@example.comom"],"from":"example@example.com"}"
  ["attachments"]=>
  string(1) "2"
  ["subject"]=>
  string(20) "Different File Types"
  ["spam_score"]=>
  string(5) "2.597"
  ["attachment-info"]=>
  string(287) "{"attachment2":{"filename":"DockMcWordface.docx","name":"DockMcWordface.docx","type":"application/vnd.openxmlformats-officedocument.wordprocessingml.document"},"attachment1":{"filename":"MG_2359.jpg","name":"_MG_2359.jpg","type":"image/jpeg","content-id":"ii_1562e2169c132d83"}}"
  ["charsets"]=>
  string(77) "{"to":"UTF-8","html":"UTF-8","subject":"UTF-8","from":"UTF-8","text":"UTF-8"}"
  ["SPF"]=>
  string(4) "pass"
}



SendGridのInbound Email Parseを利用

SendGridは、メール配信などで利用されている方は多いのかもしれませんが、メール受信トリガーにWEB API経由でPOSTしてくれるサービスも提供されているようです。メール受信時のWEB APIプッシュの実現方法を探している際に見つけました。
グローバルで、企業向けにサービスを展開されているので、サービス品質などに間違いはなさそうです。
IFTTTでメール受信をしっかりとトリガー出来ていれば本サービスを利用することも無かったかもしれませんが、利用してみてPubSubというような使い方が出来そうなのでメインで利用する予定です。一般向け製品では通知手段がメールというサービスが多いです。

SendGrid | メールを成功の原動力に。

前提条件

  • PHPが動作するWEBサーバがセットアップされている
  • WEBサーバの公開設定が終わっている

SendGridのInbound Email Parse向けPHP APIサンプル

WEBサーバーにPOST受信可能なPHPファイルを配置し、SendGridがメール受信したさいのWEBリクエストを受信するPHP APIを準備します。

以下は、サンプルとなります。受信後に実施したい処理を利用形態に合わせて実装して下さい。本ページの各種ユースケースもサンプルになると思います。ユースケース一覧 V1

  • メール受信時にDBにレコード登録(モーションセンサ感知時にメール通知用)
  • ホームセキュリティ通知をDBにレコード登録(活動ログとして)
  • 各種速報メール受信時のLINE通知など

以下サンプルコードとなります。

<?php
/*************************************************************************
 * sendgrid-api (MAIN)
 * Home Tools for private. Using IFTTT and Google Home etc
 *
 * PHP 5 or later
 *
 * @category  Home IoT
 * @author    Miki
 * @url       https://www.miki-ie.com/
 * @copyright 2019 (c) MIKI-IE All rights Reserved.
 * @license   https://opensource.org/licenses/mit-license.html MIT License
 * @version   1.0
*************************************************************************/
//ログのファイル名
define("SENDGRID_API_LOG_NAME","sendgrid");
//DB
define("DB_HOST","@IP_Adress@:@Port@");
define("DB_USER","@User@");
define("DB_PASS","@Password@");
define("DB_DBNAME","@DB_Name@");
define("DB_TABLENAME1","@TableName1@");
define("DB_TABLENAME2","@TableName2@");


function addDBRecord($table, $datetime, $value1) {
    $mysqli = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_DBNAME);

    if (mysqli_connect_errno()) {
        logger("Connect failed: ".mysqli_connect_error(),"ERROR");
        exit();
	}
	
	if (!$mysqli->set_charset("utf8")) {
        logger("Error loading character set utf8: ".$mysqli->error,"ERROR");
		exit();
	}

	switch ($table) {
		case DB_TABLENAME1:
			// SQL(INSERT)を作成
			$sql = "INSERT INTO $table (
				DATETIME, COUNT
				) VALUES (
				'$datetime', $value1
				)";
			break;
		//addDBRecord(DB_TABLENAME2, $datetime_text, $state_text);
		case DB_TABLENAME2:
		// SQL(INSERT)を作成
		$str = mb_convert_encoding($value1, "UTF-8");
		$sql = "INSERT INTO $table (
			DATETIME2, STATE
			) VALUES (
				'$datetime', N'$str'
			)";
		break;
		default:
			logger("Internal DB Tabel Name Error. table:{$table}","ERROR");
	}

	if (!$mysqli->query($sql)) {
        logger("SQL query error Errormessage: ".$mysqli->error,"ERROR");
	}

	$mysqli->close();
}

function logger($text, $level) {
	$datetime = date('Y-m-d H:i:s');
	$date = date('Ym');
	$file_name = __DIR__ . "/log/".SENDGRID_API_LOG_NAME."-{$date}.log";
	$text = "{$datetime} [{$level}] {$text}" . PHP_EOL;
	echo $text;
	if(!(file_exists($file_name))){
		touch($file_name);
		chmod($file_name, 0777);
	}
	return error_log(print_r($text, TRUE), 3, $file_name);
}

$post_from = $_POST['from'];
$post_charsets = $_POST['charsets'];
$post_charsets_array = json_decode($post_charsets, true);
$post_charsets_all = print_r($post_charsets_array, true);
$post_encode_subject = mb_convert_encoding($_POST['subject'], "UTF-8", $post_charsets_array["subject"]);
$post_encode_subject = preg_replace('/[\x00-\x1F\x7F]/', '', $post_encode_subject);
$post_encode_text = mb_convert_encoding($_POST['text'], "UTF-8", $post_charsets_array["text"]);
$post_encode_text = preg_replace('/[\x00-\x1F\x7F]/', '', $post_encode_text);
logger("mb_convert_encoding, post_subject : {$post_encode_subject}","DEBUG");
logger("mb_convert_encoding, post_text : {$post_encode_text}","DEBUG");

$text_substr = mb_substr($post_text, 0, 15);
logger("Start sendgrid: key={$key} , from={$post_from} , subject={$post_subject} , text={$text_substr}","INFO");

if(isset($_POST['from'])) {
	switch ($post_from) {
	case 'aaa1@bbb.ccc': //メール受信内容に合わせて個別処理。以下はメール本文から文字列切り出しを実施
		$start = mb_strpos($post_text, "時刻:");
		$datetime_text = mb_substr($post_text, $start + 4, 19,"utf-8");
		logger("Start CAM-A7DE datetime : {$datetime_text}","INFO");
		addDBRecord(DB_TABLENAME1, $datetime_text, 1);
		break;
	case 'aaa2@bbb.ccc': //メール受信内容に合わせて個別処理。以下はメール本文から文字列切り出しを実施
		$datetime_text = mb_substr($post_text, 0, 16,"utf-8");
		$temp_text = mb_substr($post_text, 18);
		$end_point = mb_strpos($temp_text, "。");
		$state_text = mb_substr($temp_text, 0, $end_point,"utf-8");
		logger("end_point : {$end_point} ,state : {$state_text}","DEBUG");
		logger("Start CSP-Security datetime : {$datetime_text} state : {$state_text}","INFO");
		addDBRecord(DB_TABLENAME2, $datetime_text, $state_text);
		break;
	default:
		logger("This is private API. (in Default)","ERROR");
	}
}else{
	logger("This is private API. (in else)","ERROR");
}

?>

Inbound Email Parse WebhookのPOST情報

HEADERSThe raw headers of the email.
DKIMA string containing the verification results of any DKIM and domain keys signatures in the message.
CONTENT-IDSA string containing the number of attachments.
TOEmail recipient field, as taken from the message headers.
HTMLHTML body of email. If not set, email did not have an HTML body.
FROMEmail sender, as taken from the message headers.
SENDER_IPA string of the sender’s ip address.
SPAM_REPORTSpam Assassin’s spam report.
ENVELOPEA string containing the SMTP envelope. This will have 2 variables: to, which is a single-element array containing the address that we received the email to, and from, which is the return path for the message.
ATTACHMENTSNumber of attachments included in email.
SUBJECTEmail Subject.
SPAM_SCORESpam Assassin’s rating for whether or not this is spam.
ATTACHMENT-INFOA JSON map where the keys are named attachment{X}. Each attachment key points to a JSON object containing three fields, filenametype, and content-id. The filename field is the name of the file (if it was provided). The type field is the media type of the file. X is the total number of attachments. For example, if the number of attachments is 0, there will be no attachment files. If the number of attachments is 3, parameters attachment1, attachment2, and attachment3 will have file uploads.
CHARSETSA string containing the character sets of the fields extracted from the message.
SPFThe results of the Sender Policy Framework verification of the message sender and receiving IP address.

 

[Date] array(16) {
  ["headers"]=>
  string(1970) "Received: by mx0047p1mdw1.sendgrid.net with SMTP id 6WCVv7KAWn Wed, 27 Jul 2016 20:53:06 +0000 (UTC)
Received: from mail-io0-f169.google.com (mail-io0-f169.google.com [209.85.223.169]) by mx0047p1mdw1.sendgrid.net (Postfix) with ESMTPS id AA9FFA817F2 for <example@example.comom>; Wed, 27 Jul 2016 20:53:06 +0000 (UTC)
Received: by mail-io0-f169.google.com with SMTP id b62so81593819iod.3 for <example@example.comom>; Wed, 27 Jul 2016 13:53:06 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sendgrid.com; s=ga1; h=mime-version:from:date:message-id:subject:to; bh=DpB1CYYeumytcPF3q0Upvx3Sq/oF4ZblEwnuVzFwqGI=; b=GH5YTxjt6r4HoTa+94w6ZGQszFQSgegF+Jlv69YV76OLycJI4Gxdwfh6Wlqfez5yID 5dsWuqaVJZQyMq/Dy/c2gHSqVo60BKG56YrynYeSrMPy8abE/6/muPilYxDoPoEyIr/c UXH5rhOKjmJ7nICKu1o99Tfl0cXyCskE7ERW0=
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:from:date:message-id:subject:to; bh=DpB1CYYeumytcPF3q0Upvx3Sq/oF4ZblEwnuVzFwqGI=; b=Sq6LVHbmywBdt3sTBn19U8VOmelfoJltz8IcnvcETZsYwk96RBxN+RKMN5fOZSKw4j 15HrgdIFfyDmp67YK0ygvOITlTvZ6XY5I0PtnvDtAQt79kS3tKjI3QKJoEp/ZjIjSzlL KG7agl6cxFgBbIN0yHWBOvy3O+ZXY8tZdom1yOvULjmjW1U9JkdOs+aJ6zq4qhZX/RM/ tIgLB461eJ5V95iQDDc5Ibj9Cvy4vJfXLQRO0nLVQAT2Yz58tkEO1bDZpWOPAyUNneIL yhIWp+SpbuqhMA68mq0krG1PjmWalUbpVcGJIGuOKB9mQFFo/MqdrUCjvYnyo1jPLPeX psdQ==
X-Gm-Message-State: AEkoousvdxmDoxLlTUYJ1AOmCGJv77xRBBlfKv6YrthH0M2NueMwlOxUD6t8nidE9uonXbdJ/DQy/chmHUnN//a4
X-Received: by 10.107.6.101 with SMTP id 98mr38024553iog.41.1469652785829; Wed, 27 Jul 2016 13:53:05 -0700 (PDT)
MIME-Version: 1.0
Received: by 10.107.48.17 with HTTP; Wed, 27 Jul 2016 13:53:05 -0700 (PDT)
From: Sender Name <example@example.com>
Date: Wed, 27 Jul 2016 14:53:05 -0600
Message-ID: <CAN_P_JMvV7ZpAQhOnDienypLrJmuhN=LQWweu4yScw4jQyXY2w@mail.gmail.com>
Subject: Different File Types
To: example@example.comom
Content-Type: multipart/mixed; boundary=001a113f8ad03e85160538a4343c
"
  ["dkim"]=>
  string(22) "{@sendgrid.com : pass}"
  ["content-ids"]=>
  string(37) "{"ii_1562e2169c132d83":"attachment1"}"
  ["to"]=>
  string(26) "example@example.comom"
  ["html"]=>
  string(479) "<div dir="ltr">Here&#39;s an email with multiple attachments<div><br></div><div><img src="cid:ii_1562e2169c132d83" alt="Inline image 1" width="455" height="544"><br clear="all"><div><br></div>-- <br><div class="gmail_signature" data-smartmail="gmail_signature"><div dir="ltr"><img src="https://sendgrid.com/brand/sg-logo-email.png" width="96" height="17"><br><div><br></div></div></div>
</div></div>
"
  ["from"]=>
  string(33) "Sender Name example@example.com"
  ["text"]=>
  string(139) "Here's an email with multiple attachments
"
  ["sender_ip"]=>
  string(14) "209.85.223.169"
  ["spam_report"]=>
  string(844) "Spam detection software, running on the system "mx0047p1mdw1.sendgrid.net", has
identified this incoming email as possible spam. The original message
has been attached to this so you can view it (if it isn't spam) or label
similar future email. If you have any questions, see
@@CONTACT_ADDRESS@@ for details.

Content preview:  Here's an email with multiple attachments [image: Inline image
   1] -- [...]

Content analysis details:   (2.6 points, 5.0 required)

 pts rule name              description
---- ---------------------- --------------------------------------------------
 0.8 HTML_IMAGE_RATIO_02    BODY: HTML has a low ratio of text to image area
 0.0 HTML_MESSAGE           BODY: HTML included in message
 1.8 HTML_IMAGE_ONLY_08     BODY: HTML: images with 400-800 bytes of words
 0.0 T_MIME_NO_TEXT         No text body parts

"
  ["envelope"]=>
  string(66) "{"to":["example@example.comom"],"from":"example@example.com"}"
  ["attachments"]=>
  string(1) "2"
  ["subject"]=>
  string(20) "Different File Types"
  ["spam_score"]=>
  string(5) "2.597"
  ["attachment-info"]=>
  string(287) "{"attachment2":{"filename":"DockMcWordface.docx","name":"DockMcWordface.docx","type":"application/vnd.openxmlformats-officedocument.wordprocessingml.document"},"attachment1":{"filename":"MG_2359.jpg","name":"_MG_2359.jpg","type":"image/jpeg","content-id":"ii_1562e2169c132d83"}}"
  ["charsets"]=>
  string(77) "{"to":"UTF-8","html":"UTF-8","subject":"UTF-8","from":"UTF-8","text":"UTF-8"}"
  ["SPF"]=>
  string(4) "pass"
}



Python3とPip導入

SynologyのパッケージセンターよりPython3を導入することが出来ます。

Python3をインストール

Pipをインストール

以下、Pipのコマンドラインインストールです。必要ファイルをダウンロードし、そのままインストールを実行します。

@Synology:~$ sudo curl -k https://bootstrap.pypa.io/get-pip.py | sudo python3
Password:
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 1669k  100 1669k    0     0  2403k      0 --:--:-- --:--:-- --:--:-- 2405k
Collecting pip
  Downloading https://files.pythonhosted.org/packages/5c/e0/be401c003291b56efc55aeba6a80ab790d3d4cece2778288d65323009420/pip-19.1.1-py2.py3-none-any.whl (1.4MB)
     |████████████████████████████████| 1.4MB 1.3MB/s
Collecting setuptools
  Downloading https://files.pythonhosted.org/packages/ec/51/f45cea425fd5cb0b0380f5b0f048ebc1da5b417e48d304838c02d6288a1e/setuptools-41.0.1-py2.py3-none-any.whl (575kB)
     |████████████████████████████████| 583kB 7.2MB/s
Collecting wheel
  Downloading https://files.pythonhosted.org/packages/bb/10/44230dd6bf3563b8f227dbf344c908d412ad2ff48066476672f3a72e174e/wheel-0.33.4-py2.py3-none-any.whl
Installing collected packages: pip, setuptools, wheel
  WARNING: The script wheel is installed in '/volume1/@appstore/py3k/usr/local/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
Successfully installed pip-19.1.1 setuptools-41.0.1 wheel-0.33.4

インストール後に、PATH指定と導入したpipのバージョン確認を実施。

@Synology:~$ export PATH="$PATH:/volume1/@appstore/py3k/usr/local/bin"
@Synology:~$ pip -V
pip 19.1.1 from /volume1/@appstore/py3k/usr/local/lib/python3.5/site-packages/pip (python 3.5)

Pipの動作確認も兼ねて、Seleniumをインストール

@Synology:~$ sudo pip install selenium
Collecting selenium
Downloading https://files.pythonhosted.org/packages/80/d6/4294f0b4bce4de0abf13e17190289f9d0613b0a44e5dd6a7f5ca98459853/selenium-3.141.0-py2.py3-none-any.whl (904kB)
|████████████████████████████████| 911kB 1.2MB/s
Collecting urllib3 (from selenium)
Downloading https://files.pythonhosted.org/packages/e6/60/247f23a7121ae632d62811ba7f273d0e58972d75e58a94d329d51550a47d/urllib3-1.25.3-py2.py3-none-any.whl (150kB)
|████████████████████████████████| 153kB 6.3MB/s
Installing collected packages: urllib3, selenium
Successfully installed selenium-3.141.0 urllib3-1.25.3

参考にさせて頂いたサイト

SynologyでPython! pipが使えるようにする

SynologyにWEBカメラ導入

Raspberry PiにWEBカメラを接続しました。
設置場所の自由度と、設置時のスマートさを優先させて、Raspberry Pi標準のカメラモジュールでなく、市販されているWEBカメラを利用することにしました。

UVC(USB Video Class)に対応した製品が、Raspberry認識時にトラブルも少ないと思います。UVCとは、USB Implementers Forumにより定義されたUSBのデバイスクラスとなります。

BUFFALO 200万画素WEBカメラ 広角120°マイク内蔵 ブラック BSW200MBK

価格comやアマゾンでのランキングも高く、120度の広角など購入時点だと、同価格帯の類似製品よりは魅力的な製品でした。

カメラ接続とguvcviewインストール

接続後には、Bus001に接続したカメラがUSBデバイスとして認識されました。

@raspberrypi:~ $ lsusb
Bus 001 Device 004: ID 0458:708c KYE Systems Corp. (Mouse Systems) Genius WideCam F100
Bus 001 Device 005: ID 0424:7800 Standard Microsystems Corp.
Bus 001 Device 003: ID 0424:2514 Standard Microsystems Corp. USB 2.0 Hub
Bus 001 Device 002: ID 0424:2514 Standard Microsystems Corp. USB 2.0 Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

続いて、「guvcview」をインストールします。

@raspberrypi:~ $ sudo apt-get install
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています
状態情報を読み取っています... 完了
以下のパッケージが自動でインストールされましたが、もう必要とされていません:
libboost-system1.62.0 libboost-thread1.62.0 libreoffice-gtk2
これを削除するには 'sudo apt autoremove' を利用してください。
以下の追加パッケージがインストールされます:
libgsl23 libgslcblas0 libguvcview-2.0-2 libportaudio2 libwebcam0 uvcdynctrl
uvcdynctrl-data
提案パッケージ:
gsl-ref-psdoc | gsl-doc-pdf | gsl-doc-info | gsl-ref-html
以下のパッケージが新たにインストールされます:
guvcview libgsl23 libgslcblas0 libguvcview-2.0-2 libportaudio2 libwebcam0
uvcdynctrl uvcdynctrl-data
アップグレード: 0 個、新規インストール: 8 個、削除: 0 個、保留: 0 個。
1,132 kB のアーカイブを取得する必要があります。
この操作後に追加で 3,663 kB のディスク容量が消費されます。
続行しますか? [Y/n] y
取得:1 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian buster/main armhf libgslcblas0 armhf 2.5+dfsg-6 [79.0 kB]
取得:2 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian buster/main armhf libgsl23 armhf 2.5+dfsg-6 [704 kB]
取得:3 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian buster/main armhf libportaudio2 armhf 19.6.0-1 [56.9 kB]
取得:4 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian buster/main armhf libguvcview-2.0-2 armhf 2.0.6+debian-1 [107 kB]
取得:5 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian buster/main armhf guvcview armhf 2.0.6+debian-1 [137 kB]
取得:6 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian buster/main armhf libwebcam0 armhf 0.2.4-1.1 [22.1 kB]
取得:7 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian buster/main armhf uvcdynctrl-data all 0.2.4-1.1 [9,916 B]
取得:8 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian buster/main armhf uvcdynctrl armhf 0.2.4-1.1 [16.4 kB]
1,132 kB を 5秒 で取得しました (221 kB/s)
以前に未選択のパッケージ libgslcblas0:armhf を選択しています。
(データベースを読み込んでいます ... 現在 151093 個のファイルとディレクトリがインストールされています。)
.../0-libgslcblas0_2.5+dfsg-6_armhf.deb を展開する準備をしています ...
libgslcblas0:armhf (2.5+dfsg-6) を展開しています...
以前に未選択のパッケージ libgsl23:armhf を選択しています。
.../1-libgsl23_2.5+dfsg-6_armhf.deb を展開する準備をしています ...
libgsl23:armhf (2.5+dfsg-6) を展開しています...
以前に未選択のパッケージ libportaudio2:armhf を選択しています。
.../2-libportaudio2_19.6.0-1_armhf.deb を展開する準備をしています ...
libportaudio2:armhf (19.6.0-1) を展開しています...
以前に未選択のパッケージ libguvcview-2.0-2:armhf を選択しています。
.../3-libguvcview-2.0-2_2.0.6+debian-1_armhf.deb を展開する準備をしています ...
libguvcview-2.0-2:armhf (2.0.6+debian-1) を展開しています...
以前に未選択のパッケージ guvcview を選択しています。
.../4-guvcview_2.0.6+debian-1_armhf.deb を展開する準備をしています ...
guvcview (2.0.6+debian-1) を展開しています...
以前に未選択のパッケージ libwebcam0 を選択しています。
.../5-libwebcam0_0.2.4-1.1_armhf.deb を展開する準備をしています ...
libwebcam0 (0.2.4-1.1) を展開しています...
以前に未選択のパッケージ uvcdynctrl-data を選択しています。
.../6-uvcdynctrl-data_0.2.4-1.1_all.deb を展開する準備をしています ...
uvcdynctrl-data (0.2.4-1.1) を展開しています...
以前に未選択のパッケージ uvcdynctrl を選択しています。
.../7-uvcdynctrl_0.2.4-1.1_armhf.deb を展開する準備をしています ...
uvcdynctrl (0.2.4-1.1) を展開しています...
libportaudio2:armhf (19.6.0-1) を設定しています ...
libgslcblas0:armhf (2.5+dfsg-6) を設定しています ...
uvcdynctrl-data (0.2.4-1.1) を設定しています ...
libgsl23:armhf (2.5+dfsg-6) を設定しています ...
libwebcam0 (0.2.4-1.1) を設定しています ...
libguvcview-2.0-2:armhf (2.0.6+debian-1) を設定しています ...
uvcdynctrl (0.2.4-1.1) を設定しています ...
guvcview (2.0.6+debian-1) を設定しています ...
mime-support (3.62) のトリガを処理しています ...
gnome-menus (3.31.4-3) のトリガを処理しています ...
libc-bin (2.28-10+rpi1) のトリガを処理しています ...
man-db (2.8.5-2) のトリガを処理しています ...
desktop-file-utils (0.23-4) のトリガを処理しています ...

guvcviewを起動

コマンドライン、またはGUIよりguvcviewを起動します。

コマンドラインからのguvcview起動

@raspberrypi:~ $ guvcview &
[1] 2258

GUIからのguvcview起動

起動後のサンプル画面

参考にさせて頂いたサイト

RaspberryPiでUSB接続のWebカメラを使ってみる

メールサーバー(Mail Plus)でSMTP構築

25番ポートブロック(Outbound Port25 Blocking)が設定されていたり、GmailのSMTP利用基準が厳しくなったりと、何かとIoT家電などからメール送信する際に独自のメール送信SMTPサーバが利用したい場面が出てきます。今回は、SynologyにSMTPメール送信サーバを構築します。Synologyの通知機能で用いるメールサーバも今回構築するSMTPメールサーバ経由に設定します。
また、迷惑メール防止で、自宅サーバのメール送信もIP逆引きが確認されたりと厳しくなっているので、Synologyでリレーメール設定(SMTPリレー)を行い、利用しているプロバイダのSMTPサーバ経由(サブミッションポート:Port 587、SMTP-Auth)でメールを設定します。
ちなみに、メールのメインの受信サーバおよびIMAP/POPサーバは、サブドメイン無制限のレンタルサーバを運用している環境があるので、MXレコードを利用している外部のサーバにしております。自宅のネットワーク環境+Synologyでメールサーバを本番運用するまでには至っておりません。
外部サーバがない方も、個人的には、メール転送を利用して、Gmailに転送し、Gmailから独自ドメインのメールも送信出来るように設定し運用することをおすすめします。
[ITmedia]Gmailで差出人を別のメールアドレスに変更して送信する

メールサーバ(Mail Plus)をインストール

「パッケージセンター」よりMail Serverをインストールします。

SMTPサーバを設定

メールサーバーの設定画面を開きます

メールサーバーインストール後、設定画面を開きます。TLS / SSLプロファイルレベルの警告が出ます。

SMTPの設定画面を開き、SMTPサーバ設定を入力します

SMTPを有効にするにチェックを入れます。

SMTP認証を有効にするにチェックを入れ、LAN接続の認証を無視するにチェックを入れます(利用する環境に合わせて設定下さい)。
ホスト名(FQDN)に、利用するメールのドメイン名を入力します。
ローカルのみで利用するので、ポートは、SMTP標準の25を設定します。

SMTPリレー設定を行います

SMTP設定画面より「SMTPリレー」設定画面を開きます。
SMTPリレー設定画面では、プロバイダのSMTPサーバ、ポートとアカウント情報を入力して、SMTPリレー設定を行います。

Synologyの通知設定のメールサーバを変更

「コントロールパネル」「通知」を開きます

「コントロールパネル」「通知」の「電子メール」タブを設定します

電子メール通知を有効にするにチェックを入れます。
通知先メールアドレスを受信者のEメールアドレスに設定します。
SMTPサーバに「localhost」を設定し、SMTPポートに「25」を設定します。

送信者の電子メールに、SMTPサーバで設定したFQDNで利用可能なメールアドレスをしてします。

すべて設定後、「テストメールの送信」でテストメールが受信者のEメールアドレスで設定したアドレスで受信できるか確認して下さい。

 

Raspberry Piのバックアップ

SDカードのみの運用、他のコンポーネントの信頼性も不明瞭なので、とりあえずまるごとSynologyへバックアップを行い、故障などが起きた際に備えます。

Synologyでrsync利用設定

「コントロールパネル」「ファイルサービス」「rsync」タブの「rsyncサービスを有効にする」にチェックを入れます。。設定変更時には「適用」ボタンを押して設定を有効化する必要があります。

Raspberry Piのバックアップ設定(Rsync)

バックアップ対象

現在のボリューム構成は、以下となります。(「df -h」コマンド結果)
/ ルートフォルダ全体をバックアップ対象とし、バックアップから除外するファイルリストをファイルで準備します。「/rsync-exclude.txt」として準備しました。

@raspberrypi:~# df -h
ファイルシス サイズ 使用 残り 使用% マウント位置
/dev/root 116G 5.8G 105G 6% /
devtmpfs 459M 0 459M 0% /dev
tmpfs 464M 0 464M 0% /dev/shm
tmpfs 464M 14M 450M 3% /run
tmpfs 5.0M 4.0K 5.0M 1% /run/lock
tmpfs 464M 0 464M 0% /sys/fs/cgroup
/dev/mmcblk0p6 253M 40M 213M 16% /boot
tmpfs 93M 0 93M 0% /run/user/1000
tmpfs 93M 0 93M 0% /run/user/1001
@raspberrypi:~# sudo vi /rsync-exclude.txt
@raspberrypi:~#
@raspberrypi:~# sudo cat /rsync-exclude.txt
/proc/*
/sys/*
/dev/*
/boot/*
/tmp/*
/run/*
/mnt/*
/var/tmp/*
/var/log/*
/media/*

rsyncコマンド

今回は、以下のコマンドを利用することにしました。

バックアップ先 /mnt/synology/raspberry_backup (フォルダは事前に作成して置きます)

sudo rsync -aEv --delete-during --exclude-from=/rsync-exclude.txt / /mnt/synology/raspberry_backup/ --log-file=/home/rsync.log
rsync  version 3.1.3  protocol version 31
Copyright (C) 1996-2018 by Andrew Tridgell, Wayne Davison, and others.
Web site: http://rsync.samba.org/
Capabilities:
    64-bit files, 64-bit inums, 32-bit timestamps, 64-bit long ints,
    socketpairs, hardlinks, symlinks, IPv6, batchfiles, inplace,
    append, ACLs, xattrs, iconv, symtimes, prealloc

rsync comes with ABSOLUTELY NO WARRANTY.  This is free software, and you
are welcome to redistribute it under certain conditions.  See the GNU
General Public Licence for details.

rsync is a file transfer program capable of efficient remote update
via a fast differencing algorithm.

Usage: rsync [OPTION]... SRC [SRC]... DEST
  or   rsync [OPTION]... SRC [SRC]... [USER@]HOST:DEST
  or   rsync [OPTION]... SRC [SRC]... [USER@]HOST::DEST
  or   rsync [OPTION]... SRC [SRC]... rsync://[USER@]HOST[:PORT]/DEST
  or   rsync [OPTION]... [USER@]HOST:SRC [DEST]
  or   rsync [OPTION]... [USER@]HOST::SRC [DEST]
  or   rsync [OPTION]... rsync://[USER@]HOST[:PORT]/SRC [DEST]
The ':' usages connect via remote shell, while '::' & 'rsync://' usages connect
to an rsync daemon, and require SRC or DEST to start with a module name.

Options
 -v, --verbose               increase verbosity
     --info=FLAGS            fine-grained informational verbosity
     --debug=FLAGS           fine-grained debug verbosity
     --msgs2stderr           special output handling for debugging
 -q, --quiet                 suppress non-error messages
     --no-motd               suppress daemon-mode MOTD (see manpage caveat)
 -c, --checksum              skip based on checksum, not mod-time & size
 -a, --archive               archive mode; equals -rlptgoD (no -H,-A,-X)
     --no-OPTION             turn off an implied OPTION (e.g. --no-D)
 -r, --recursive             recurse into directories
 -R, --relative              use relative path names
     --no-implied-dirs       don't send implied dirs with --relative
 -b, --backup                make backups (see --suffix & --backup-dir)
     --backup-dir=DIR        make backups into hierarchy based in DIR
     --suffix=SUFFIX         set backup suffix (default ~ w/o --backup-dir)
 -u, --update                skip files that are newer on the receiver
     --inplace               update destination files in-place (SEE MAN PAGE)
     --append                append data onto shorter files
     --append-verify         like --append, but with old data in file checksum
 -d, --dirs                  transfer directories without recursing
 -l, --links                 copy symlinks as symlinks
 -L, --copy-links            transform symlink into referent file/dir
     --copy-unsafe-links     only "unsafe" symlinks are transformed
     --safe-links            ignore symlinks that point outside the source tree
     --munge-links           munge symlinks to make them safer (but unusable)
 -k, --copy-dirlinks         transform symlink to a dir into referent dir
 -K, --keep-dirlinks         treat symlinked dir on receiver as dir
 -H, --hard-links            preserve hard links
 -p, --perms                 preserve permissions
 -E, --executability         preserve the file's executability
     --chmod=CHMOD           affect file and/or directory permissions
 -A, --acls                  preserve ACLs (implies --perms)
 -X, --xattrs                preserve extended attributes
 -o, --owner                 preserve owner (super-user only)
 -g, --group                 preserve group
     --devices               preserve device files (super-user only)
     --copy-devices          copy device contents as regular file
     --specials              preserve special files
 -D                          same as --devices --specials
 -t, --times                 preserve modification times
 -O, --omit-dir-times        omit directories from --times
 -J, --omit-link-times       omit symlinks from --times
     --super                 receiver attempts super-user activities
     --fake-super            store/recover privileged attrs using xattrs
 -S, --sparse                turn sequences of nulls into sparse blocks
     --preallocate           allocate dest files before writing them
 -n, --dry-run               perform a trial run with no changes made
 -W, --whole-file            copy files whole (without delta-xfer algorithm)
     --checksum-choice=STR   choose the checksum algorithms
 -x, --one-file-system       don't cross filesystem boundaries
 -B, --block-size=SIZE       force a fixed checksum block-size
 -e, --rsh=COMMAND           specify the remote shell to use
     --rsync-path=PROGRAM    specify the rsync to run on the remote machine
     --existing              skip creating new files on receiver
     --ignore-existing       skip updating files that already exist on receiver
     --remove-source-files   sender removes synchronized files (non-dirs)
     --del                   an alias for --delete-during
     --delete                delete extraneous files from destination dirs
     --delete-before         receiver deletes before transfer, not during
     --delete-during         receiver deletes during the transfer
     --delete-delay          find deletions during, delete after
     --delete-after          receiver deletes after transfer, not during
     --delete-excluded       also delete excluded files from destination dirs
     --ignore-missing-args   ignore missing source args without error
     --delete-missing-args   delete missing source args from destination
     --ignore-errors         delete even if there are I/O errors
     --force                 force deletion of directories even if not empty
     --max-delete=NUM        don't delete more than NUM files
     --max-size=SIZE         don't transfer any file larger than SIZE
     --min-size=SIZE         don't transfer any file smaller than SIZE
     --partial               keep partially transferred files
     --partial-dir=DIR       put a partially transferred file into DIR
     --delay-updates         put all updated files into place at transfer's end
 -m, --prune-empty-dirs      prune empty directory chains from the file-list
     --numeric-ids           don't map uid/gid values by user/group name
     --usermap=STRING        custom username mapping
     --groupmap=STRING       custom groupname mapping
     --chown=USER:GROUP      simple username/groupname mapping
     --timeout=SECONDS       set I/O timeout in seconds
     --contimeout=SECONDS    set daemon connection timeout in seconds
 -I, --ignore-times          don't skip files that match in size and mod-time
 -M, --remote-option=OPTION  send OPTION to the remote side only
     --size-only             skip files that match in size
 -@, --modify-window=NUM     set the accuracy for mod-time comparisons
 -T, --temp-dir=DIR          create temporary files in directory DIR
 -y, --fuzzy                 find similar file for basis if no dest file
     --compare-dest=DIR      also compare destination files relative to DIR
     --copy-dest=DIR         ... and include copies of unchanged files
     --link-dest=DIR         hardlink to files in DIR when unchanged
 -z, --compress              compress file data during the transfer
     --compress-level=NUM    explicitly set compression level
     --skip-compress=LIST    skip compressing files with a suffix in LIST
 -C, --cvs-exclude           auto-ignore files the same way CVS does
 -f, --filter=RULE           add a file-filtering RULE
 -F                          same as --filter='dir-merge /.rsync-filter'
                             repeated: --filter='- .rsync-filter'
     --exclude=PATTERN       exclude files matching PATTERN
     --exclude-from=FILE     read exclude patterns from FILE
     --include=PATTERN       don't exclude files matching PATTERN
     --include-from=FILE     read include patterns from FILE
     --files-from=FILE       read list of source-file names from FILE
 -0, --from0                 all *-from/filter files are delimited by 0s
 -s, --protect-args          no space-splitting; only wildcard special-chars
     --address=ADDRESS       bind address for outgoing socket to daemon
     --port=PORT             specify double-colon alternate port number
     --sockopts=OPTIONS      specify custom TCP options
     --blocking-io           use blocking I/O for the remote shell
     --stats                 give some file-transfer stats
 -8, --8-bit-output          leave high-bit chars unescaped in output
 -h, --human-readable        output numbers in a human-readable format
     --progress              show progress during transfer
 -P                          same as --partial --progress
 -i, --itemize-changes       output a change-summary for all updates
     --out-format=FORMAT     output updates using the specified FORMAT
     --log-file=FILE         log what we're doing to the specified FILE
     --log-file-format=FMT   log updates using the specified FMT
     --password-file=FILE    read daemon-access password from FILE
     --list-only             list the files instead of copying them
     --bwlimit=RATE          limit socket I/O bandwidth
     --stop-at=y-m-dTh:m     Stop rsync at year-month-dayThour:minute
     --time-limit=MINS       Stop rsync after MINS minutes have elapsed
     --outbuf=N|L|B          set output buffering to None, Line, or Block
     --write-batch=FILE      write a batched update to FILE
     --only-write-batch=FILE like --write-batch but w/o updating destination
     --read-batch=FILE       read a batched update from FILE
     --protocol=NUM          force an older protocol version to be used
     --iconv=CONVERT_SPEC    request charset conversion of filenames
     --checksum-seed=NUM     set block/file checksum seed (advanced)
     --noatime               do not alter atime when opening source files
 -4, --ipv4                  prefer IPv4
 -6, --ipv6                  prefer IPv6
     --version               print version number
(-h) --help                  show this help (-h is --help only if used alone)

Use "rsync --daemon --help" to see the daemon-mode command-line options.
Please see the rsync(1) and rsyncd.conf(5) man pages for full documentation.
See http://rsync.samba.org/ for updates, bug reports, and answers

バックアップ実行コマンドと定期実行

バックアップコマンド(rsyncコマンド)

sudo rsync -aEv --delete-during --exclude-from=/rsync-exclude.txt / /mnt/synology/raspberry_backup/ --log-file=/home/rsync.log

crontabを編集

以下のcronジョブを登録します。
午前3時にrysyncコマンドを実行するように登録しました。
「sudo crontab -e」で編集します。

@raspberrypi:~ $ sudo crontab -e
0 3 * * * sudo rsync -a --delete-during --exclude-from=/rsync-exclude.txt / /mnt/synology/raspberry_backup/ --log-file=/home/rsync.log

[参考]cron コマンド

crontabファイルで、cron コマンドの実行を記述する行は、6つのフィールドで形成されています。(システムの crontabファイル(/etc/crontab)は、7つ(分、時、日、月、曜日、ユーザ名、コマンド))、コマンドの実行時間をさまざまな形式で指定することが出来ます。
初めてcrontabファイルを編集した場合には、何も書かれていませんので、以下のフォーマットで記述します。また、各フィールドでは、 '*' を使用することが可能です。

 分 時 日 月 曜日 コマンド
0~59
0~23
1~31
1~12 or jan~dec
曜日0~7 [0,7は日曜日] or sun~sat
コマンド有効なコマンドを記述します。空白を含むことも可能ですが、標準のBourne Shellの書式に従って記述します。

 

参考URL

Raspberry Piから外部ボリューム(NASをNFSマウント)利用

Raspberry Piからも信頼性の高いボリュームを利用したい、より大きなサイズのボリュームを利用したい、Raspberry Piの内容を外部にバックアップしたいなど、外部NASを利用したい利用シーンはたくさんあると思います。
今回は、WEBサーバなど各種サーバー機能のメインマシンであるSynologyのボリュームをNFSマウントし、利用する方法を記事にしておきます。

SynologyでNFS利用に向けた設定

SynologyでNFSを利用可能にする設定

「コントロールパネル」「ファイルサービス」「SMB/AFP/NFS」タブの「NFS」セッションの「NFSを有効にする」にチェックを入れて下さい。チェック後は、「適応」ボタンを押し、設定を反映して下さい。

SynologyでNFS利用設定1

共有フォルダにNFS権限付与

Synologyで作成した共有フォルダに対して、NFS権限の付与を行います。
「コントロールパネル」「共有フォルダ」対象の共有フォルダを選択して「編集」、「NFS権限」「編集」でNFSルールを指定します。
以下の例では、Raspberry Pi1台向け(IPアドレス:192.168.0.200)にNFS権限を付与しています。

SynologyでNFS利用設定2

共有フォルダのNFS権限画面に表示されている「マウントパス」は、実際のマウント時に必要となるので、内容のメモをお願いします。
マウント時のパス:<Synology_IPアドレス>:<マウントパス>
192.168.0.100:/volume1/raspberry

SynologyでNFS利用設定3

Raspberry PiからNASボリュームMount

Raspberry PiからmountコマンドでNASボリュームをNFSマウント

@raspberrypi:~ $ sudo apt-get install nfs-common
[sudo] password for:
nfs-common is already the newest version (1:1.3.4-2.5).
0 upgraded, 0 newly installed, 0 to remove and 26 not upgraded.

@raspberrypi:~ $ sudo showmount -e 192.168.0.100
Export list for 192.168.0.100:
/volume1/raspberry 192.168.0.200

@raspberrypi:~ $ sudo mkdir /mnt/synology

@raspberrypi:~ $ sudo mount -t nfs 192.168.0.100:/volume1/raspberry /mnt/synology

Raspberry Pi起動・再起動時のNFS自動マウント

/etc/fstabにNFSボリュームのマウント情報を記載

@raspberrypi:~ $ cat /etc/fstab
proc /proc proc defaults 0 0
/dev/mmcblk0p6 /boot vfat defaults 0 2
/dev/mmcblk0p7 / ext4 defaults,noatime 0 1
# a swapfile is not a swap partition, no line here
# use dphys-swapfile swap[on|off] for that
192.168.0.100:/volume1/raspberry /mnt/synology nfs defaults,_netdev 0 0

Raspberry PiのBootオプション変更(NASマウントタイミング変更)

Raspberry PiのBoot Option「Wait for Network at Boot Choose whether to wait for network」を変更します。「raspi-config」を用いて、設定を行います。

@raspberrypi:~ $ sudo raspi-config
  1. 3 Boot Options Configure options for start-up を選択
  2. B2 Wait for Network at Boot Chose whether to wait for network connection を選択
  3. <Yes> を選択
  4. <Ok> を選択

/etc/rc.localに3秒Sleep後マウント

上記の設定後も、起動・再起動時の自動マウントが実行されませんでした。
シスログ(/var/log/syslog)を確認したところ、やはり起動時のマウント時にNASへのネットワークマウントが失敗しておりました。
mount.nfs: Network is unreachable

Jul 9 06:36:22 raspberrypi rpc.mountd[473]: Version 1.3.3 starting
Jul 9 06:36:23 raspberrypi mount[489]: mount.nfs: Network is unreachable
Jul 9 06:36:23 raspberrypi systemd[1]: mnt-synology.mount: Mount process exited, code=exited, status=32/n/a
Jul 9 06:36:23 raspberrypi systemd[1]: mnt-synology.mount: Failed with result 'exit-code'.
Jul 9 06:36:23 raspberrypi systemd[1]: Failed to mount /mnt/synology.

よって、/etc/rc.localに3秒のスリープとマウントコマンドを追加しました。
sleep 3
sudo mount -t nfs 192.168.0.100:/volume1/raspberry /mnt/synology

#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.

# Print the IP address
_IP=$(hostname -I) || true
if [ "$_IP" ]; then
  printf "My IP address is %s\n" "$_IP"
fi

#After Sleep 3, then execute a mount command
sleep 3
sudo mount -t nfs 192.168.0.100:/volume1/raspberry /mnt/synology

exit 0

その他ブートシーケンスに関係すると
(/etc/rc.localにスリープおよびマウントを入れた際には試す必要ないです)

systemd-networkd-wait-onlineを利用し、確実にネットワークの起動後にサービスを起動が出来るようにwaitが入るようです。
systemd-networkdとsystemd-networkd-wait-onlineをenableにします。

@raspberrypi:~ $ sudo systemctl enable systemd-networkd
Created symlink /etc/systemd/system/dbus-org.freedesktop.network1.service → /lib/systemd/system/systemd-networkd.service.
Created symlink /etc/systemd/system/multi-user.target.wants/systemd-networkd.service → /lib/systemd/system/systemd-networkd.service.
Created symlink /etc/systemd/system/sockets.target.wants/systemd-networkd.socket → /lib/systemd/system/systemd-networkd.socket.
Created symlink /etc/systemd/system/network-online.target.wants/systemd-networkd-wait-online.service → /lib/systemd/system/systemd-networkd-wait-online.service.
@raspberrypi:~ $ sudo systemctl enable systemd-networkd-wait-online

雨が降り出す前に通知①Google Homeスピーカーとライン通知

Use Case(ユースケース)

約2−3週間利用してみてのベストプラクティスです。

YahooのYOLP(地図)気象情報APIを利用し、緯度経度で指定した地点の10分間隔の天気予報を定期的にチェックし、トリガーとします。(日本ならではの細かなWEBサービスを利用します)
天候が「Rain(雨)」に変化した際に、Webhooks経由でトリガーを受け、LINEに通知および、トリガー発生時間(日中)を判定しGoogle Homeスピーカーで雨が降り出す旨のアナウンスを実行します。Google Homeスピーカーは就寝時間などには音声を出させたくないので、時間判定を入れました。

前提条件

  • Yahoo! JAPANのアプリケーションID Yahoo!JAPANアプリケーションの管理
    アプリケーションIDはYahoo! JAPAN IDをお持ちの方ならどなたでも登録できます。アプリケーションIDは、各リクエスト送信時に必要で、開発者自身ではなく、アプリケーションを特定します。
  • (Raspberry Piの)Google Home Notifier導入が終わっている「google-home-notifier」導入
  • (Raspberry Piの)PHPが動作するWEBサーバがセットアップされている
  • (Raspberry Piの)WEBサーバの公開設定が終わっている
  • IFTTTサービスの利用登録が実施済みである
  • IFTTTサービスにおいて、Webhooksが利用可能である IFTTT(イフト)でWebhooksの利用
  • IFTTTサービスにおいて、LINE Notifyの利用設定を実施している

全体の流れ

  1. Weather.phpファイルの準備
  2. Weather.phpの定期実行設定、雨を検知した時点でWEB APIへトリガー
  3. WEB APIよりLINEへメッセージ送信
  4. Google Homeスピーカーでアナウンス(Google Home Notifier経由)

Weather.phpファイルの準備

こちらのページを参照して下さい。

Weather.phpの定期実行

Synologyの「コントロールパネル」「タスクスケジューラー」で10分毎の実行で、以下のコマンドラインを登録して、定期実行しております。

/usr/local/bin/php72 /var/services/homes/user/weather.php

トリガー

Weather.phpよりRaspberry PiのWEB APIへ直接トリガーされます。3つのパラメータを設定します。

    • APIKEY=apikey
    • KEY=Weather
    • text="$text"
APIKEY=apikey&KEY=Weather&text="$text"

アクション

以下が、準備したサンプルのPHPスクリプトとなります。

//@HOME_API_LOG_NAME@ ログファイル名、書き込み権限が必要です
//@HOME_API_KEY@ POST受信時の簡易的なAPI-KEYのトークン確認を行います
//@IFTTT_POST_API_KEY@ IFTTTのWebhooks用API-KEY
//@GOOGLE_HOME_1@ http://192.168.0.200:9081 などgoogle home notifier向けのURL
//@GOOGLE_HOME_2@ google home notifier向けのURL
//@GOOGLE_HOME_3@ google home notifier向けのURL

//各種設定
//ログのファイル名
define("HOME_API_LOG_NAME","@HOME_API_LOG_NAME@");
//home-api-key
define("HOME_API_KEY","@HOME_API_KEY@");
//IFTTT用webhookパラメータ
define("IFTTT_POST_API_KEY","/with/key/@IFTTT_POST_API_KEY@");
define("IFTTT_POST_API_BASE","https://maker.ifttt.com/trigger/");
//IFTTT用puchLINE
define("IFTTT_LINE_KEY","pushLINE");

//google-home-notifier
define("GOOGLE_HOME_1",'@GOOGLE_HOME_1@');
define("GOOGLE_HOME_2",'@GOOGLE_HOME_2@');
define("GOOGLE_HOME_3",'@GOOGLE_HOME_3@');

function checkTime($startTime, $endTime) {
	$currentTime = date('H:i');
	if(strtotime($startTime) <= strtotime($currentTime) and strtotime($currentTime) <= strtotime($endTime)) {
		return true;
	}else{
		return false;
	}
}

function pushLINE($value1, $value2) {
	logger("Start pushLINE value1={$value1},value2={$value2}","INFO");
	$url = IFTTT_POST_API_BASE.IFTTT_LINE_KEY.IFTTT_POST_API_KEY;
	$data = array(
		'value1' => $value1,
		'value2' => $value2
	);
	$curl = curl_init();
	curl_setopt($curl, CURLOPT_URL, $url);
	curl_setopt($curl, CURLOPT_POST, TRUE);
	curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($data)); // jsonデータを送信
	curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
	curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
	$response = curl_exec($curl);
	$result = json_decode($response, true);
	curl_close($curl);
	return $result;
}

function announce($api_url, $text) {
	logger("Start announce target={$api_url},text={$text}","INFO");
	$message = 'text='.$text;
	$data = array(
		'text' => "$text"
	);
	$path = '/google-home-notifier';
	$url = $api_url.$path;
	$curl = curl_init();
	curl_setopt($curl, CURLOPT_URL, $url);
	curl_setopt($curl, CURLOPT_POST, TRUE);
	curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); // 証明書の検証を行わない
	curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);  // curl_execの結果を文字列で返す
	curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($data)); // jsonデータを送信
	$response = curl_exec($curl);
	$result = json_decode($response, true);
	curl_close($curl);
	return $result;
}

function logger($text, $level) {
	$datetime = date('Y-m-d H:i:s');
	$date = date('Ym');
	$file_name = __DIR__ . "/log/log-home-{$date}.log";
	$text = "{$datetime} [{$level}] {$text}" . PHP_EOL;
	echo $text;
	if(!(file_exists($file_name))){
		touch($file_name);
		chmod($file_name, 0777);
	}
	return error_log(print_r($text, TRUE), 3, $file_name);
}

logger("Start API","INFO");

if(isset($_POST['KEY']) && strcmp($_POST['APIKEY'], HOME_API_KEY) == 0) {
	logger("KEY : ".$_POST['KEY'],"INFO");
	logger("TEXT : ".$_POST['TEXT'],"INFO");
	$text = $_POST['TEXT'];
	switch ($_POST['KEY']) {
	case 'Weather':
		logger("Start Weather","INFO");
		//Rain alert
		pushLine('【天気情報】',$text);
		if(checkTime('7:00','19:00')) {
			announce(GOOGLE_HOME_2, $text);
			announce(GOOGLE_HOME_3, $text);
		}
		if(checkTime('6:00','23:00')) {
			announce(GOOGLE_HOME_1, $text);
		}
		break;
	default:
		logger("This is private API. (in Default)","ERROR");
	}
}else{
	logger("This is private API. (in else)","ERROR");
}

アクション1

LINE送信のアクションを設定します。すでに、他ユースケースなどでIFTTT側にLINE送信のレシピを導入されている方は、アクション2の定義に進んで下さい。

アクション1:トリガー

アクセスキーなどの初期設定値は、IFTTTより取得して下さい。

IFTTT(イフト)でWebhooksの利用

//IFTTT用webhookパラメータ
define("IFTTT_POST_API_KEY","/with/key/アクセスキー");
define("IFTTT_POST_API_BASE","https://maker.ifttt.com/trigger/");
//IFTTT用puchLINE
define("IFTTT_LINE_KEY","pushLINE");

function pushLINE($value1, $value2) {
	$url = IFTTT_POST_API_BASE.IFTTT_LINE_KEY.IFTTT_POST_API_KEY;
	$data = array(
		'value1' => $value1,
		'value2' => $value2
	);
	$curl = curl_init();
	curl_setopt($curl, CURLOPT_URL, $url);
	curl_setopt($curl, CURLOPT_POST, TRUE);
	curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($data)); // jsonデータを送信
	curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
	curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
	$response = curl_exec($curl);
	$result = json_decode($response, true);
	curl_close($curl);
	return $result;
}

アクション1:IFTTTでのトリガー


Webhooksを{event}:pushLINEで設定します。

アクション1:アクション


LINE送信のアクションを定義します。
Recipientでラインの送付先を指定します。すでに作成しているLINEのグループにも送信することが出来ます。
Message部分は、自由に変更出来ます。今回は、PHPより2つの引数を渡しているので、2つの引数をMessageに入れております。pushLine('【テスト】',$text);

アクション2

Google Home Notifier経由で、Google Homeからアナウンスを流します。「google-home-notifier」導入

//@GOOGLE_HOME_1@ http://192.168.0.200:9081 などgoogle home notifier向けのURL
define("GOOGLE_HOME_1",'@GOOGLE_HOME_1@');

function announce($api_url, $text) {
	logger("Start announce target={$api_url},text={$text}","INFO");
	$message = 'text='.$text;
	$data = array(
		'text' => "$text"
	);
	$path = '/google-home-notifier';
	$url = $api_url.$path;
	$curl = curl_init();
	curl_setopt($curl, CURLOPT_URL, $url);
	curl_setopt($curl, CURLOPT_POST, TRUE);
	curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); // 証明書の検証を行わない
	curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);  // curl_execの結果を文字列で返す
	curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($data)); // jsonデータを送信
	$response = curl_exec($curl);
	$result = json_decode($response, true);
	curl_close($curl);
	return $result;
}

announce(GOOGLE_HOME_1, $text);

時間判定について checkTime関数

対象時間の開始と終了を指定して、true, falseを戻り値とする関数を準備しました。
日付を跨る設定などは考慮しておりません。

checkTime('6:00','23:00')とすれば、朝6時から夜23時まで「True」となります。
このチェック関数を用いて、Google Homeのアナウンス対象時間か否かを確認しています。

function checkTime($startTime, $endTime) {
	$currentTime = date('H:i');
	if(strtotime($startTime) <= strtotime($currentTime) and strtotime($currentTime) <= strtotime($endTime)) {
		return true;
	}else{
		return false;
	}
}

 

前提条件

  • Synologyのサーバの公開設定が終わっている
    • ルーターのNAT / Port Foward / Virtual Serverの設定が出来ており、インタネットからサーバーにアクセスできる

DDNS(Dynamic DNS)ダイナミックDNS/動的DNS

インターネットを利用する際に必要となる、パブリックIPアドレス(インターネットにおける世界で一意なアドレス)。
ドメイン(miki-ie.comなど)は、人にもわかりやすい表札や屋号のようなもの。
DNS(Domain Name Serivce)は、ドメインとIPアドレスの割り当てを行います。

DDNSとは、IPアドレスが頻繁に変わるホストに固定的にドメイン名を割り当て、アドレス変更に即座に追随してDNS情報を更新するシステム。 また、その仕組みを利用して提供される動的なDNSサービス。

企業向けなどには固定IPアドレスが利用され、DNS(Domain Name System)運用においてもIPアドレスとドメイン名を固定的に長期間結びつけた運用となる。
個人宅など、一般のインターネットプロバイダ(ISP)を利用した際は、インターネットプロバイダより、一時的なIPアドレスが割り当てられる。

よって、通常の自宅環境でドメインを運用した際は、利用するIPアドレスに合わせてDNSレコードを変更する必要があります。
www.miki-ie.com(不変に利用を続けたい) ⇔ Public IPアドレス(ISP接続毎に変わる)

独自ドメイン取得

DDNS向けに無料でドメインを貸してくれるWEBサービスもありますが、永続的な無料での利用などが約束されているわけでもなく、WEBサイトなどを運営する際は、独自ドメインを利用するのも悪くないと考えています。

VALUE-DOMAIN(バリュードメイン)

  • 500種類以上の独自ドメインを取り扱い。
  • 自動更新設定、一括ドメイン取得・更新など運用がしやすい
  • ネームサーバー/DNSレコード設定/Whois代行などドメイン運用の基本機能も標準装備
  • 「バリューサーバー」「コアサーバー」「XREA」などのホスティングとの連携が容易



VALUE-DOMAINでのDDNS利用時のIP更新方法

VALUE-DOMAINで本設定を行う際は、VALUE-DOMAINのポータルサイトよりIP更新時に用いるドメインのパスワードを取得する必要があります。
参考URL:https://www.value-domain.com/ddns.php?action=howto

ツールなどからのアクセス先(HTTP GET/POSTリクエスト)

https://dyn.value-domain.com/cgi-bin/dyn.fcg?d=ドメイン名&p=パスワード&h=ホスト名&i=IPアドレス

パラメータ説明
d更新するドメインを指定します。
例:value-domain.com
p更新するドメインのパスワードを指定します。
例: 1234
h更新するドメインのホスト名を指定します。DNSレコード編集画面のホスト名と全く同じ仕様です。
例: *(全ホスト)、www、指定なしはホスト名なし
i更新するドメインのIPアドレスを指定します。指定しない場合は、自動的に接続者のIPアドレスが設定されます。
aaaaレコードが存在し、IPアドレスがIPv6フォーマットの場合、aaaaレコードのホストがアップデートされます。

SynologyへVALUE-DOMAINのDDNS更新を設定

SynologyへVALUE-DOMAIN向けのプロバイダ登録を実施

「コントロールパネル」「外部アクセス」「DDNS」「カスタマイズ」よりDDNSプロバイダを新規登録します。
Query URLへは、以下を登録し、一部はSynologyの設定画面で設定する設定値を利用するために、準備されている変数を利用します。

  • ユーザー名:__USERNAME__ d=ドメイン名
  •  パスワード:__PASSWORD__ p=パスワード
  •  ホスト名:__HOSTNAME__ 利用しない
  • 外部アドレス:__MYIP__ i=IPアドレス

※ ホスト名(h=ホスト名)は、「 *(全ホスト)」を固定指定。

 

https://dyn.value-domain.com/cgi-bin/dyn.fcg?d=__USERNAME__&p=__PASSWORD__&h=*&i=__MYIP__

SynologyへVALUE-DOMAIN向けDDNS更新を設定

「コントロールパネル」「外部アクセス」「DDNS」「追加」より作成したサービス・プロバイダVALUE-DOMAINに必要な情報をINPUTします。
※ ホスト名は何を設定しても、実際には「*」となります。このフォームに「*」のしては不可能な仕様となっておりました。