コンテンツへスキップ

2019年07月31日 めざましじゃんけん結果

個人の実験的な試行内容であり、めざましじゃんけんの結果を保証したり、全ての結果が記載を保証するものではありません。

2019年07月31日のめざましじゃんけんの結果をベストエフォートで公開します。

Goo(グー)
Choki(チョキ)
Pa(パー)

めざましテレビ|めざましじゃんけんーフジテレビ

めざましじゃんけん結果
回次結果
7月31日
(水曜日)
07時58分
07時35分
06時58分
05時58分

PHPのPEARライブラリーをインストール

開発自体は、できる限り手間をかけずに各種実装を進めているのですが、プログラム言語により各種フレームワークが準備されております。
最近の利用実態は分かりませんが、以前はPHPのPEARは、各所で利用されておりました。
Raspberry Piに導入した際の手順を公開します。

マニュアルでインストールしようとしたのですが、apt-getしたソフトで全く問題なかったので、簡単な手順となりました。
DBを例に、個別の機能をインストールする際の例も記載しております。

sudo apt-get install php-pear
sudo pear update-channels
sudo pear upgrade-all
sudo pear install DB

導入時点では、以下のパッケージがインストールされていました。

  • Archive_Tar 1.4.7 stable
  • Console_Getopt 1.4.2 stable
  • PEAR 1.10.9 stable
  • PEAR_Manpages 1.10.0 stable
  • Structures_Graph 1.1.1 stable
  • XML_Util 1.4.3 stable

以下、コマンド抜粋です。

@raspberrypi:~ $ sudo apt-get install php-pear
[sudo] パスワード:
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています
状態情報を読み取っています... 完了
php-pear はすでに最新バージョン (1:1.10.6+submodules+notgz-1.1) です。
php-pear は手動でインストールしたと設定されました。
アップグレード: 0 個、新規インストール: 0 個、削除: 0 個、保留: 0 個。

@raspberrypi:~ $ pear -h
Commands:
build Build an Extension From C Source
bundle Unpacks a Pecl Package
channel-add Add a Channel
channel-alias Specify an alias to a channel name
channel-delete Remove a Channel From the List
channel-discover Initialize a Channel from its server
channel-info Retrieve Information on a Channel
channel-login Connects and authenticates to remote channel server
channel-logout Logs out from the remote channel server
channel-update Update an Existing Channel
clear-cache Clear Web Services Cache
config-create Create a Default configuration file
config-get Show One Setting
config-help Show Information About Setting
config-set Change Setting
config-show Show All Settings
convert Convert a package.xml 1.0 to package.xml 2.0 format
cvsdiff Run a "cvs diff" for all files in a package
cvstag Set CVS Release Tag
download Download Package
download-all Downloads each available package from the default channel
info Display information about a package
install Install Package
list List Installed Packages In The Default Channel
list-all List All Packages
list-channels List Available Channels
list-files List Files In Installed Package
list-upgrades List Available Upgrades
login Connects and authenticates to remote server [Deprecated in favor of channel-login]
logout Logs out from the remote server [Deprecated in favor of channel-logout]
makerpm Builds an RPM spec file from a PEAR package
package Build Package
package-dependencies Show package dependencies
package-validate Validate Package Consistency
pickle Build PECL Package
remote-info Information About Remote Packages
remote-list List Remote Packages
run-scripts Run Post-Install Scripts bundled with a package
run-tests Run Regression Tests
search Search remote package database
shell-test Shell Script Test
sign Sign a package distribution file
svntag Set SVN Release Tag
uninstall Un-install Package
update-channels Update the Channel List
upgrade Upgrade Package
upgrade-all Upgrade All Packages [Deprecated in favor of calling upgrade with no parameters]
Usage: pear [options] command [command-options] <parameters>
Type "pear help options" to list all options.
Type "pear help shortcuts" to list all command shortcuts.
Type "pear help version" or "pear version" to list version information.
Type "pear help <command>" to get the help for the specified command.

@raspberrypi:~ $ sudo pear update-channels
Updating channel "doc.php.net"
could not create lock file: fopen(/usr/share/php/.lock): failed to open stream: Permission denied
Updating channel "pear.php.net"
could not create lock file: fopen(/usr/share/php/.lock): failed to open stream: Permission denied
Updating channel "pecl.php.net"
could not create lock file: fopen(/usr/share/php/.lock): failed to open stream: Permission denied
miki@raspberrypi:~ $ sudo pear update-channels
Updating channel "doc.php.net"
Update of Channel "doc.php.net" succeeded
Updating channel "pear.php.net"
Update of Channel "pear.php.net" succeeded
Updating channel "pecl.php.net"
Update of Channel "pecl.php.net" succeeded

@raspberrypi:~ $ sudo pear upgrade-all
Will upgrade channel://pear.php.net/console_getopt
Will upgrade channel://pear.php.net/archive_tar
Will upgrade channel://pear.php.net/pear
WARNING: "pear/Console_Getopt" is deprecated in favor of "pear/Console_GetoptPlus"
downloading Console_Getopt-1.4.2.tgz ...
Starting to download Console_Getopt-1.4.2.tgz (5,736 bytes)
.....done: 5,736 bytes
downloading Archive_Tar-1.4.7.tgz ...
Starting to download Archive_Tar-1.4.7.tgz (21,134 bytes)
...done: 21,134 bytes
downloading PEAR-1.10.9.tgz ...
Starting to download PEAR-1.10.9.tgz (291,931 bytes)
...done: 291,931 bytes
upgrade-all ok: channel://pear.php.net/Console_Getopt-1.4.2
upgrade-all ok: channel://pear.php.net/Archive_Tar-1.4.7

Warning: "continue" targeting switch is equivalent to "break". Did you mean to use "continue 2"? in PEAR/PackageFile/v1.php on line 1424
PHP Warning: "continue" targeting switch is equivalent to "break". Did you mean to use "continue 2"? in /usr/share/php/PEAR/PackageFile/v1.php on line 1424
upgrade-all ok: channel://pear.php.net/PEAR-1.10.9
PEAR: Optional feature webinstaller available (PEAR's web-based installer)
PEAR: Optional feature gtkinstaller available (PEAR's PHP-GTK-based installer)
PEAR: Optional feature gtk2installer available (PEAR's PHP-GTK2-based installer)
PEAR: To install optional features use "pear install pear/PEAR#featurename"

@raspberrypi:~ $ sudo pear list
Installed packages, channel pear.php.net:
=========================================
Package Version State
Archive_Tar 1.4.7 stable
Console_Getopt 1.4.2 stable
PEAR 1.10.9 stable
PEAR_Manpages 1.10.0 stable
Structures_Graph 1.1.1 stable
XML_Util 1.4.3 stable
miki@raspberrypi:~ $

miki@raspberrypi:~ $ php -v
PHP 7.3.4-2 (cli) (built: Apr 13 2019 19:05:48) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.4, Copyright (c) 1998-2018 Zend Technologies
with Zend OPcache v7.3.4-2, Copyright (c) 1999-2018, by Zend Technologies

@raspberrypi:~ $ sudo pear install DB
WARNING: "pear/DB" is deprecated in favor of "pear/MDB2"
downloading DB-1.9.3.tgz ...
Starting to download DB-1.9.3.tgz (132,290 bytes)
.............................done: 132,290 bytes
install ok: channel://pear.php.net/DB-1.9.3

東芝REGZAテレビをWEB APIで操作

テレビの状態が知りたいと思い、東芝のレグザ(REGZA)に何かI/F(インターフェース)がないのか、調べてみました。
残念ながら、テレビの状態は分かりませんでした。電源が付いていなければ、なにかのコマンドやネットワーク接続失敗などで、電源ONかOFFぐらいは分かるかなが現時点の結果です。操作系は、Nature RemoのAPIやNature Remo経由のIFTTTが利用しやすいですし、他デバイスと同じ方式で統一した操作が可能など優位点が多いです。東芝はテレビを頑張っているように見えるので、せっかくI/F(インターフェース)があるようなので、今後IFTTT対応をしてほしいです。Google home搭載モデルなどもありますし、今後に期待!
非公式と思われる情報であり、有用となり得る情報なので設定方法や確かめた内容を記事にしておきます。

利用にあたり、REGZA側に2つ設定を行いました。

  1. IPアドレスの指定(IPアドレスが分かれば、設定方法は問わないです)
  2. レグザAppコネクト設定

1.IPアドレスの指定

「(リモコン)設定ボタン」「初期設定」「ネットワーク設定」「IPアドレス設定」

デフォルト設定のIPアドレス自動取得を「しない」に設定変更しました。
その後、ネットワーク設定に合わせて、IPアドレスの設定を行いました。

2.レグザAppコネクト設定

「(リモコン)設定ボタン」「接続機器設定」「外部連携設定」「レグザAppコネクト設定」

API利用時に、ユーザー名とパスワードの認証が必要となります。
Digest認証ダイジェストにんしょう)が利用されているようで、プログラムからの利用時には、Digest認証に対応した方法で利用する必要があります。

東芝テレビ(レグザ:REGZA)のWEB API仕様

http://[REGZA IP]/remote/remote.htm?key=[command_key]

IE、Chrome、Firefoxなど一般ブラウザから動作確認できます。
戻り値は、テキスト出力として数字が返ってきました。
「0」成功 「0以外」失敗。
ブラウザで操作している際に、テレビの音量を上げようとすると「4」というエラー番号も見ることが出来ました。電源ボタンを押した直後に音量操作実施のタイミング。

テレビのIPアドレスが192.168.0.77の場合のコマンドサンプル

  • 音量を上げたいとき
    http://192.168.0.77/remote/remote.htm?key=40BF1A
  • チャンネル「8」ボタンを押す
    http://192.168.0.77/remote/remote.htm?key=40BF08
  • リモコンの「青」ボタンを押す
    http://192.168.0.77/remote/remote.htm?key=40BF73

[command_key]リスト

ボタン名[command_key]
140BF01
240BF02
340BF03
440BF04
540BF05
640BF06
740BF07
840BF08
940BF09
1040BF0A
1140BF0B
1240BF0C
入力切替40BF0F
消音40BF10
電源40BF12
音声切り替え40BF13
音量↑40BF1A
チャンネル↑40BF1B
画面表示40BF1C
音量↓40BF1E
チャンネル↓40BF1F
ブロードバンド40BF25
クイック40BF27
マルチ画面40BF29
画面サイズ40BF2B
戻る40BF3B
終了40BF3C
決定40BF3D
40BF3E
40BF3F
一時停止(静止)40BF50
40BF5B
40BF5F
CH番号40BF60
サーチ40BF60
ラジオ/データ40BF6D
番組表40BF6E
番組説明40BF71
40BF73
40BF74
40BF75
40BF76
ミニ番組表40BF77
地デジ40BF7A
地アナ40BF7B
BS40BF7C
CS40BF7D
録画40BF86
設定メニュー40BFD0
↑↑40BE20
↓↓40BE21
→→(30秒送り)40BE22
←←(10秒戻し)40BE23
早送りスキップ40BE26
戻るスキップ40BE27
録画リスト40BE28
停止40BE2B
巻き戻し40BE2C
再生40BE2D
早送り40BE2E
レグザメニュー40BE34
タイムシフト40BE35
番組検索40BE36
始めにジャンプ40BE47
イジェクト40BE93
ディスク40BE9E
dデータ43BC14
字幕43BC52

ブラウザで実行した際の画面イメージ

表示されるDigest認証画面

音量を上げるコマンド実行後の結果「0」(成功)

参考にさせていただいたサイトです

REGZAをネットワーク越しに操作する

フジテレビのめざましじゃんけん画像検出

フジテレビのめざましじゃんけんシステムGo-Live!!
本日分が無事に動いたので、早速公開!
めざましじゃんけん結果一覧はこちら

初めての技術習得が結構多く、開発極小がポリシーなのですが、時間かかりました。各種勉強から初めて3週間弱でしょうか。
7月13日の週末に、関連書籍とWEBカメラをアマゾンで購入していました。

Deep Learning(ディープラーニング、深層学習)DNN(ディープニューラルネットワーク)、AI(人工知能)ML(機械学習)を少し触ってみました。初めての技術ばかりで、ライフのデジタル化計画に有益な技術習得を行いました。

  • Python(初めて扱いました)
  • Python関係画像ライブラリ、OpenCVなど(オープンシーヴィ、Open Source Computer Vision Library)
  • Chainer (チェイナー) ディープラーニングフレームワーク
  • Darknet(ダークネット)YOLO(You Only Look Once)物体検出、オープンソースのニューラルネットフレームワーク

以下サンプル画像です。デジタル放送部分のみなので、著作権には配慮しているつもりです。問題のある際は、ご連絡下さい。大手なので、使用許諾にも時間がかかりそうなので、時間を見つけて画像の使用許諾を考えます。

8月からのブログネタは、このシステム構築に関わる技術内容とする予定です。
次の週末にでも記事を書き溜めます。
さて、次は、、、まだ未定。

文字列のエスケープ処理

PHPでWEB APIを利用していると、制御文字や改行文字を削除(エスケープ)したい場面が出てきます。

  1. 改行文字(x0A x0D)含む制御文字を削除したい場合
  2. 改行文字(x0A x0D)は残し制御文字を削除したい場合

1.改行文字を含む制御文字を削除したい場合

x0A Line Feed(改行)およびx0D Carriage Return(復帰)と制御文字を削除したい場合の、PHPコードです。

$string = preg_replace('/[\x00-\x1F\x7F]/','', $string);

改行文字含むコントロール文字は、POSIXキャラクタクラスにおいて [:cntrl:] で定義されており、以下のように記載することも可能です。

$string = preg_replace('/[[:cntrl:]]/', '', $string);

2.改行文字は残し制御文字を削除したい場合

x0A Line Feed(改行)およびx0D Carriage Return(復帰)と制御文字は残し、その他の制御文字を削除する場合の、PHPコードです。

$string = preg_replace('/[\x00-\x09\x0B\x0C\x0E-\x1F\x7F]/', '', $string);

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

Remove control characters from PHP string

[制御文字一覧]

16進略号名称CS
00NUL空白(Null)^@
01SOHヘッディング開始(Start of Heading)^A
02STXテキスト開始(Start of Text)^B
03ETXテキスト終結(End of Text)^C
04EOT伝送終了(End of Transmission)^D
05ENQ問い合わせ(Enquiry)^E
06ACK肯定応答(Acknowledge)^F
07BELベル(Bell)^G
08BS後退(Backspace)^H
09HT水平タブ(Horizontal Tabulation)^I
0ALF改行(Line Feed)^J
0BVT垂直タブ(Vertical Tabulation)^K
0CFF書式送り(Form Feed)^L
0DCR復帰(Carriage Return)^M
0ESOシフトアウト(Shift Out)^N
0FSIシフトイン(Shift In)^O
10DLE伝送制御拡張(Data Link Escape)^P
11DC1装置制御1(Device Control 1)^Q
12DC2装置制御2(Device Control 2)^R
13DC3装置制御3(Device Control 3)^S
14DC4装置制御4(Device Control 4)^T
15NAK否定応答(Negative Acknowledge)^U
16SYN同期信号(Synchronous Idle)^V
17ETB伝送ブロック終結(End of Transmission Block)^W
18CAN取り消し(Cancel)^X
19EM媒体終結(End of Medium)^Y
1ASUB置換(Substitute Character)^Z
1BESC拡張(Escape)^[
1CFSファイル分離(File Separator)^\
1DGSグループ分離(Group Separator)^]
1ERSレコード分離(Record Separator)^^
1FUSユニット分離(Unit Separator)^_
7FDEL抹消(Delete)^?

[POSIXキャラクタクラス]

クラス名意味
[:alnum:]アルファベットと(十進)数字
[:alpha:]アルファベット
[:blank:]空白文字(スペース、タブ等)
[:cntrl:]制御文字
[:digit:]十進数字
[:graph:]印字可能かつ表示可能な文字(スペースは印字可能だが表示可能ではない)
[:lower:]アルファベットの小文字
[:print:]印字可能なキャラクタ(=制御文字以外のキャラクタ)
[:punct:]句読点(通常の文字、数字、制御文字、スペースのいずれでもないキャラクター)
[:space:]スペース、タブ、改ページ
[:upper:]アルファベットの大文字
[:xdigit:]十六進数字

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"
}



Seleniumでブラウザ自動操作(スクレイピング)

Raspberry Piでない環境で利用したことのあるSleniumの導入を行いました。
各種WEBツールの自動操作(スクレイピング)などが出来ると、更に自宅IoTの幅が広がると思い、導入してみましたが、「Raspberry Pi 3 Model B+」のCPUが1.2GHz、4コアのBroadcom BCM2837から1.4GHz駆動4コアのBCM2837B0は、32bitでありchromedriverの導入方法が限られ、高い信頼性で動作する状態まで構築出来ておりません。とりあえず動作しますが、信頼性が低いです。(数回に1回は失敗します)

pi@raspberrypi:~ $ cat /proc/cpuinfo
processor	: 0
model name	: ARMv7 Processor rev 4 (v7l)
BogoMIPS	: 38.40
Features	: half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32
CPU implementer	: 0x41
CPU architecture: 7
CPU variant	: 0x0
CPU part	: 0xd03
CPU revision	: 4

processor	: 1
model name	: ARMv7 Processor rev 4 (v7l)
BogoMIPS	: 38.40
Features	: half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32
CPU implementer	: 0x41
CPU architecture: 7
CPU variant	: 0x0
CPU part	: 0xd03
CPU revision	: 4

processor	: 2
model name	: ARMv7 Processor rev 4 (v7l)
BogoMIPS	: 38.40
Features	: half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32
CPU implementer	: 0x41
CPU architecture: 7
CPU variant	: 0x0
CPU part	: 0xd03
CPU revision	: 4

processor	: 3
model name	: ARMv7 Processor rev 4 (v7l)
BogoMIPS	: 38.40
Features	: half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32
CPU implementer	: 0x41
CPU architecture: 7
CPU variant	: 0x0
CPU part	: 0xd03
CPU revision	: 4

Hardware	: BCM2835
Revision	: a020d3
Serial		: 00000000e63ad038
pi@raspberrypi:~ $ cat /etc/debian_version
10.0
pi@raspberrypi:~ $ uname -a
Linux raspberrypi 4.19.57-v7+ #1244 SMP Thu Jul 4 18:45:25 BST 2019 armv7l GNU/Linux

Pipでseleniumを導入

root@raspberrypi:~# pip install selenium
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Collecting selenium
Downloading https://files.pythonhosted.org/packages/80/d6/4294f0b4bce4de0abf13e17190289f9d0613b0a44e5dd6a7f5ca98459853/selenium-3.141.0-py2.py3-none-any.whl (904kB)
100% |████████████████████████████████| 911kB 306kB/s
Requirement already satisfied: urllib3 in /usr/lib/python2.7/dist-packages (from selenium) (1.24.1)
Installing collected packages: selenium
Successfully installed selenium-3.141.0
@raspberrypi:~ $ pip list |grep selenium
selenium 3.141.0

chromium-browser導入

sudo apt-get install chromium-browser

Chromedirever導入

  1. オフィシャルサイトよりダウンロード(一番推奨される方法)
    ただし、Linux版は64Bitしか提供されていない。https://sites.google.com/a/chromium.org/chromedriver/downloads
    よって、https://launchpad.net/ubuntu/trusty/+package/chromium-chromedriverよりダウンロードしたドライバを利用します。
    参考URL:Raspberry Piによるスクレイピング 環境構築(Selenium + Chromiumドライバ)
  2. apt sourceを利用したインストール
    参考URL:Raspberry pi3上でChromeのheadlessモードによる小型低電力&自動化環境への第一歩
  3. pipでのインストール
    参考URL:【Python】SeleniumでHeadless Chromeを使おう
  4. npmでのインストール
    sudo npm -g install chromedriver

2. apt sourceでのインストール

chromiumブラウザともバージョンが合い、まだ安定して動作しました。

sudo apt-get install chromium-chromedriver

3. pipでのインストール

あまり安定して動作しませんでした。

root@raspberrypi:~# pip install chromedriver
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Collecting chromedriver
Downloading https://files.pythonhosted.org/packages/c8/5e/1daf3c71852f5b8536e30b2afa1e6442c454e91947678ac1a37daba2d7f5/chromedriver-2.24.1-py2.py3-none-any.whl (14.3MB)
100% |████████████████████████████████| 14.3MB 16kB/s
Installing collected packages: chromedriver
Successfully installed chromedriver-2.24.1
miki@raspberrypi:~ $ pip list |grep chromedriver
chromedriver 2.24.1

Selenium Webdriverの動作確認

以下のスクリプトでheadlessモードでの動作検証を行いました。タイミングにより失敗するケースも有り、IoT家電のメインパスとして利用するにはもう少し試行錯誤が必要かなという状況です。ただし、APIがなくWEBのみに温度など取得したい情報があるツールなどの情報GET系には利用していこうかなと考えております。
それとは、別に設定変更など操作を行うことはもう少し、信頼のおける構築方法が見つかってから導入したいと考えております。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import time
from selenium import webdriver
from selenium.webdriver.chrome.options import Options

CHROME_BIN = "/usr/bin/chromium-browser"
CHROME_DRIVER = '/usr/lib/chromium-browser/chromedriver'

options = Options()
options.binary_location = CHROME_BIN
options.add_argument('--headless')

driver = webdriver.Chrome(CHROME_DRIVER, options=options)

driver.get("https://www.google.co.jp")

time.sleep(5)

html = driver.page_source
print(html)
driver.quit()

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カメラを使ってみる