2018年11月22日木曜日

ラズパイでLet's Encryptの証明書更新に失敗

ラズパイを自宅用の各種サーバー(DNS、メール、ウェブ他)として使っています。httpsのサーバー証明書の発行・更新にはLet's Encriptを使っています。今日たまたま証明書を更新しようとすると下記のエラーに陥りました。何度やっても失敗します。


matsu@rpi001 ~/letsencript/certbot $ sudo ./certbot-auto renew 
Bootstrapping dependencies for Debian-based OSes... (you can skip this with --no-
bootstrap)
Hit http://archive.raspberrypi.org jessie InRelease                            
Hit http://mirrordirector.raspbian.org jessie InRelease                        
Hit http://archive.raspberrypi.org jessie/main Sources
Hit http://mirrordirector.raspbian.org jessie/main armhf Packages
Hit http://archive.raspberrypi.org jessie/ui Sources                           
Hit http://archive.raspberrypi.org jessie/main armhf Packages                  
Hit http://mirrordirector.raspbian.org jessie/contrib armhf Packages           
Hit http://archive.raspberrypi.org jessie/ui armhf Packages                    
Hit http://mirrordirector.raspbian.org jessie/non-free armhf Packages          
Hit http://mirrordirector.raspbian.org jessie/rpi armhf Packages               
Ign http://archive.raspberrypi.org jessie/main Translation-en_US               
Ign http://archive.raspberrypi.org jessie/main Translation-en                  
Ign http://archive.raspberrypi.org jessie/ui Translation-en_US                 
Ign http://archive.raspberrypi.org jessie/ui Translation-en                    
Ign http://mirrordirector.raspbian.org jessie/contrib Translation-en_US        
Ign http://mirrordirector.raspbian.org jessie/contrib Translation-en           
Ign http://mirrordirector.raspbian.org jessie/main Translation-en_US
Ign http://mirrordirector.raspbian.org jessie/main Translation-en
Ign http://mirrordirector.raspbian.org jessie/non-free Translation-en_US
Ign http://mirrordirector.raspbian.org jessie/non-free Translation-en
Ign http://mirrordirector.raspbian.org jessie/rpi Translation-en_US
Ign http://mirrordirector.raspbian.org jessie/rpi Translation-en
Reading package lists... Done
Reading package lists... Done
Building dependency tree       
Reading state information... Done
augeas-lenses is already the newest version.
ca-certificates is already the newest version.
gcc is already the newest version.
libaugeas0 is already the newest version.
libffi-dev is already the newest version.
libssl-dev is already the newest version.
openssl is already the newest version.
python is already the newest version.
python-dev is already the newest version.
python-virtualenv is already the newest version.
virtualenv is already the newest version.
0 upgraded, 0 newly installed, 0 to remove and 433 not upgraded.
Creating virtual environment...
Installing Python packages...
Had a problem while installing Python packages.

pip prints the following errors: 
=====================================================
Collecting argparse==1.4.0 (from -r /tmp/tmp.e1u7wLIvLq/letsencrypt-auto-requirements.txt (line 11))
  Downloading https://files.pythonhosted.org/packages/f2/94/3af39d34be01a24a6e65433d19e107099374224905f1e0cc6bbe1fd22a2f/argparse-1.4.0-py2.py3-none-any.whl
Collecting pycparser==2.14 (from -r /tmp/tmp.e1u7wLIvLq/letsencrypt-auto-require
(中略)

Certbot has problem setting up the virtual environment.

Based on your pip output, the problem can likely be fixed by 
increasing the available memory.

Consult https://certbot.eff.org/docs/install.html#problems-with-python-virtual-environment
for possible solutions.
You may also find some support resources at https://certbot.eff.org/support/ .

エラーメッセージにもあるように、純粋にメモリが足りない模様。確かにsyslogを見ると、OOMキラー(メモリ不足のときに動作中のプロセスが終了させられる仕組み)も発動していました。Let's Encriptは毎回自分自身をアップデートするので、アップデートのせいでメモリを多く使うようになったのか、とこの時は思いました。

OOMキラーで必要なdaemonが停止させられた可能性もあるためまずはリブート。物理メモリは簡単には増やせないので、swapを100Mから2GBに増やしました。その上で実行したところ、正常動作して期待通りサーバー証明書が更新されました。まずはめでたし。

しかし、実行中でtopコマンドでメモリの状況を観測していたのですが、swapを使った形跡がありません。あれ?おかしいなと思い、リブート前のtopの結果(偶然ターミナルログに残っていた)とリブート後のtopの結果を比較してみました。

リブート前

top - 11:11:55 up 233 days, 36 min,  4 users,  load average: 5.59, 10.66, 6.33
Tasks: 383 total,   1 running, 382 sleeping,   0 stopped,   0 zombie
%Cpu(s):  1.3 us,  0.6 sy,  0.0 ni, 98.1 id,  0.0 wa,  0.0 hi,  0.1 si,  0.0 st
KiB Mem:    948120 total,   732324 used,   215796 free,     9424 buffers
KiB Swap:   102396 total,   102396 used,        0 free.    76364 cached Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND     
11277 dovecot   20   0   81700  79644   1556 S   0.0  8.4   6:18.62 auth        
 8018 bind      20   0  119980  32256    316 S   0.0  3.4 380:41.05 named       
32603 matsu     20   0   15716  11432   6224 S   0.7  1.2   0:00.99 getmail     
21817 news      20   0   14740   7236      4 S   0.0  0.8   0:01.26 controlchan 
11746 matsu     20   0   16748   6188      4 S   0.0  0.7   0:01.74 getmail     
31888 matsu     20   0   16748   6188      4 S   0.0  0.7   0:01.84 getmail     
 2736 matsu     20   0   16748   6184      4 S   0.0  0.7   0:01.81 getmail     
 4670 matsu     20   0   15716   5252      4 S   0.0  0.6   0:01.43 getmail     
29979 matsu     20   0   15716   5228      4 S   0.0  0.6   0:01.10 getmail     
22534 matsu     20   0   15716   5212      4 S   0.0  0.5   0:00.98 getmail     
 1193 matsu     20   0   15716   5208      4 S   0.0  0.5   0:00.56 getmail     
 1559 matsu     20   0   15716   5208      4 S   0.0  0.5   0:00.57 getmail     
 2525 matsu     20   0   15716   5208      4 S   0.0  0.5   0:01.00 getmail     
 2652 matsu     20   0   15716   5208      4 S   0.0  0.5   0:00.68 getmail     
 3074 matsu     20   0   15716   5208      4 S   0.0  0.5   0:00.57 getmail   

リブート後

top - 12:30:47 up  1:17,  5 users,  load average: 0.06, 0.47, 1.37
Tasks: 123 total,   1 running, 122 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.4 us,  0.2 sy,  0.0 ni, 99.3 id,  0.0 wa,  0.0 hi,  0.1 si,  0.0 st
KiB Mem:    948120 total,   626872 used,   321248 free,    33892 buffers
KiB Swap:  2097148 total,        0 used,  2097148 free.   468300 cached Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND     
  517 bind      20   0   90112  32568   6024 S   0.0  3.4   0:17.99 named       
 4600 matsu     20   0   15716  11636   6428 S   0.7  1.2   0:00.76 getmail     
  671 news      20   0   14740  10820   3588 S   0.0  1.1   0:01.42 controlchan 
 4502 www-data  20   0  237040   8148   4560 S   0.0  0.9   0:00.57 apache2     
  802 root      20   0   11888   7956   5444 S   0.0  0.8   0:03.49 apache2     
  664 news      20   0   14056   7884   4040 S   0.0  0.8   0:00.06 innd        
 4501 www-data  20   0  235672   5836   2408 S   0.0  0.6   0:00.14 apache2     
 4590 postfix   20   0    7516   5668   5108 S   0.0  0.6   0:00.07 smtpd       
 4243 postfix   20   0    7516   5652   5072 S   0.0  0.6   0:00.09 smtpd       
 1251 root      20   0   11636   5300   4624 S   0.0  0.6   0:00.29 sshd        
  630 root      20   0   11636   5268   4588 S   0.0  0.6   0:00.34 sshd        
 2561 root      20   0   11636   5224   4544 S   0.0  0.6   0:00.30 sshd        
 2506 root      20   0   11636   5208   4528 S   0.0  0.5   0:00.32 sshd        
 1466 root      20   0   11636   5148   4468 S   0.0  0.5   0:00.30 sshd        
 4500 www-data  20   0   11384   4772   2264 S   0.0  0.5   0:00.00 apache2     
 1264 matsu     20   0    6848   4500   2956 S   0.0  0.5   0:01.12 bash        
 2519 matsu     20   0    6848   4496   2956 S   0.0  0.5   0:00.55 bash    
どうやらリブート前は、dovecot(POPサーバー)のプロセスがメモリリークか何かでメモリを消費していた模様。メモリ消費量は80MBほどですが、1GBしかメモリがないラズパイでは、この差が効いたようです。リブート前は233日ほど表面的には問題なく動き続けていましたが、たまにはリブートした方が良いのかもしれません。






2018年10月6日土曜日

BUFFALOの無線LANルーターWSR-2533DHP2-CBの導入記

BUFFALOの無線LANルーターを自宅に導入したので、感想など。

導入のきっかけ

これまで自宅で使用していた無線LANルーター(Logtec社LAN-WH300AN-DGR)が頻繁に再起動を繰り返すようになってしまいました。時間帯によって数分に1回再起動したり、数時間なんともなかったり。設定も使い方も変わらないのに状況が変化するため、インターネット側からのパケットや無線電波のような、何か外乱が引き金になっていることが疑わしい。ファームウェアを最新にしても効果が無く、無線の5GHz帯を止めて2.4GHz帯だけにするなど、設定をいろいろ変えてみても効果がなかったため、買い替えを決断。

機種選定

一般家庭にしては接続台数が多い(思いつくだけでもMac2台、PC4台、スマートフォン4台、タブレット1台、ラズパイ5台、仮想マシン4台、テレビ1台、録画機1台、プリンター1台、Google Home1台)です。有線と無線半々くらい。今後増えることも想定して、ストリーム数は多めのもの。

ポート開放でインターネットから接続させたいマシンがあります。それ自身は、最近のルーターならどれも問題ないはずですが、LAN側のクライアントからもグローバルアドレスで接続できた方が何かと便利なので、NAT-Loopback機能(ヘアピンNAT)は欲しいところです。Logtec社LAN-WH300AN-DGRを選択したときも、NAT-Loopbackが使えることを確認してから購入しました。

NAT-Loopback機能の有無はどのメーカーもまともに公表していないので困りました。BUFFALOの比較的最近の機種は対応しているらしいことがわかっていたので、まずはBUFFALOで検討。WSR-2533DHP2を候補にした上で、NAT-Loopback機能の有無をBUFFALOのサポートチャットで問い合わせてみました。

すると、「WSR-2533DHP2のNAT-Loopback機能の有無は、サポート窓口には情報が無いので、回答には数日要する」とのこと。「WSR-2533DHP2はWSR-2533DHPの後継機種。WSR-2533DHPはNAT-Loopback機能あり。」という情報と、「(上位シリーズの)WXRシリーズにはNAT-Loopback機能あり。」という情報をもらいました。数日我慢するのもいやだったので、NAT-Loopback機能ありに賭けてWSR-2533DHP2を購入することに決定し、量販店に走って購入。(ダメだったときのプランは別に考えていましたが長くなるので省略)


設定

設定はごく普通に、まずは無線LANルーターにPCを1台だけ接続して、Webブラウザで接続して行いました。
  • インターネットプロバイダにPPPoEで接続する設定
  • LAN側IPアドレスの変更
  • DHCPで割り当てるIPアドレスの範囲指定
  • DHCPで固定IPを割り当てたい複数のマシンについて、MACアドレスとIPアドレスの対応の設定
  • 無線のSSIDとパスワードの設定(以前のものをそのまま)
  • ポート転送の設定
全て設定完了したところで、旧ルーターとすげ替え。


NAT-Loopback機能

結論はNAT-Loopback機能ありでした。(ラッキー)
一通り設定したあと、LAN側からssh XXX.XXX.XXX.XXX (グローバルIP)で、sshのポート開放しているマシンに接続できることを確認。sshサーバーからは、無線LANルーターのLAN側のIPアドレスから接続されたように見えました。リプレース前と同じ挙動です。BUFFALOも、ちゃんとNAT-Loopback機能については製品紹介に載せればいいのに。

DHCPサーバー機能で気になったこと

DHCPで固定IPを割り当てるときに、DHCPで割り当てるIPアドレスの範囲内でしか固定IPを選べないようになっていました。これまではDHCPの固定IPアドレスは192.168.20.50〜99の範囲にして、動的に割り当てるIPアドレスは192.168.20.100〜199の範囲というように、混ざらないように別の範囲にしていたのですが、普通にはこの状態に設定できないことになります。ちょっと困ったなと思いましたが、一旦DHCPの割り当てる範囲を192.168.20.50〜149にしておいて、固定IPをその範囲で指定。固定IPの設定が終わってから、DHCPの割り当て範囲を192.168.20.100〜199に変更するという手順を踏めば、混ざらない設定ができました。

ポート開放設定

ポート開放は1つづつ設定することしかできません。例えば10080〜10082の連続する3つのポートを同じホストに転送する場合も、1つづつしかできないため、10080,10081,10082の 3つのポート開放エントリを作成する必要があります。また、TCPとUDPもそれぞれ別個に登録する必要があります。大した手間ではないとはいえ、まとめて設定できた方が良いです。

その他感想

まだ2日しかたっていませんが、再起動することもなく、安定して動いているようです。


2018年9月28日金曜日

google_home_speechのエラー AttributeError: 'NoneType' object has no attribute 'group'

ラズパイマガジン2018年8月号の記事で紹介した、Google Homeにテキストを読み上げさせるプログラム、google_home_speechが、私の環境では9月19日以降エラーで動かなくなっていることに気づきました。

エラーの内容や経緯には興味無く、解決する方法をすぐに知りたい方は、この記事の「解決方法」まで読み飛ばしても構いません。

エラー内容

curlコマンドで、「こんにちは」と喋らせようとすると、下記のようなメッセージが出力されて、処理が中断され、Google Homeは何も反応しません。

127.0.0.1 - - [27/Sep/2018 06:45:35] "GET / HTTP/1.1" 404 -Host: localhost:8000User-Agent: curl/7.52.1Accept: */*Content-Length: 26Content-Type: application/x-www-form-urlencoded

POST requestBody={"text":"こんにちは"}
**JSON**
{
    "text":"\u3053\u3093\u306b\u3061\u306f"
}
get speech data for こんにちは(ja)
Traceback (most recent call last):
  File "/usr/lib/python3.5/socketserver.py", line 313, in _handle_request_noblock
    self.process_request(request, client_address)
  File "/usr/lib/python3.5/socketserver.py", line 341, in process_request
    self.finish_request(request, client_address)
  File "/usr/lib/python3.5/socketserver.py", line 354, in finish_request
    self.RequestHandlerClass(request, client_address, self)
  File "/usr/lib/python3.5/socketserver.py", line 681, in __init__
    self.handle()
  File "/usr/lib/python3.5/http/server.py", line 422, in handle
    self.handle_one_request()
  File "/usr/lib/python3.5/http/server.py", line 410, in handle_one_request
    method()
  File "./google_home_speech.py", line 105, in do_POST
    do_speech(text, language)
  File "./google_home_speech.py", line 66, in do_speech
    tts.save(speech_filename)
  File "/home/pi/.local/lib/python3.5/site-packages/gtts/tts.py", line 247, in save
    self.write_to_fp(f)
  File "/home/pi/.local/lib/python3.5/site-packages/gtts/tts.py", line 187, in write_to_fp
    part_tk = self.token.calculate_token(part)
  File "/home/pi/.local/lib/python3.5/site-packages/gtts_token/gtts_token.py", line 28, in calculate_token
    seed = self._get_token_key()
  File "/home/pi/.local/lib/python3.5/site-packages/gtts_token/gtts_token.py", line 62, in _get_token_key
    a = re.search("a\\\\x3d(-?\d+);", tkk_expr).group(1)
AttributeError: 'NoneType' object has no attribute 'group'

Googleのテキスト読み上げサービスを使って音声ファイルを取得するgTTSパッケージ配下でエラーを出しているようです。何度やっても同じエラーで失敗しました。

エラーが発生しているのは私だけ?

数ヶ月にわたって安定して動いていたのに、急に全く動かなくなったのは、Googleのテキスト読み上げサービス側に何か変更があったのが疑わしいです。その場合、同じパッケージを使っている他の人にも影響があるはず。gTTSパッケージのgithubサイトのissuesを見ると、案の定、私と同じ時期に同じエラーを報告している人が複数いることがわかりました。

https://github.com/pndurette/gTTS/issues/138
https://github.com/pndurette/gTTS/issues/60

ソースのここを直したら良いなどの情報が見つかりますが、すでに本家のパッケージがアップデートされているため、パッケージのアップデートで解決するようです。おそらくもともとあった問題が、Google側になんらかの変更があったことで顕在化したのだと想像します。

アップデートが必要なパッケージはgTTS-tokenです。バージョン1.1.1が問題あり。1.1.2で解決されています。インストールされているパッケージはpip3 freezeで確認できます。

パッケージのバージョン

問題発生時


$ pip3 freeze

beautifulsoup4==4.6.0

bs4==0.0.1

certifi==2018.4.16

chardet==3.0.4

click==6.7
cryptography==1.7.1
gTTS==2.0.0
gTTS-token==1.1.1
idna==2.7
keyring==10.1
keyrings.alt==1.3
netifaces==0.10.6
protobuf==3.6.0
pyasn1==0.1.9
PyChromecast==2.2.0
pycrypto==2.6.1
pygobject==3.22.0
python-apt==1.1.0b5
pyxdg==0.25
requests==2.19.1
SecretStorage==2.3.1
six==1.11.0
urllib3==1.23
zeroconf==0.20.0

問題解決後 


$ pip3 freeze

beautifulsoup4==4.6.0

bs4==0.0.1

certifi==2018.8.24

chardet==3.0.4

click==6.7
cryptography==1.7.1
gTTS==2.0.0
gTTS-token==1.1.2
idna==2.7
keyring==10.1
keyrings.alt==1.3
netifaces==0.10.6
protobuf==3.6.0
pyasn1==0.1.9
PyChromecast==2.2.0
pycrypto==2.6.1
pygobject==3.22.0
python-apt==1.1.0b5
pyxdg==0.25
requests==2.19.1
SecretStorage==2.3.1
six==1.11.0
urllib3==1.23
zeroconf==0.20.0

解決方法

google_home_speechをインストールしたディレクトリで、下記のコマンドでgTTS-tokenパッケージをアップデートしてください。
pip3 install gTTS-token --upgrade

アップデートが完了したら、google_home_speechを再起動してください。

2018年7月9日月曜日

ラズパイマガジン2018年8月号に執筆記事が掲載されました

本日発売のラズパイマガジン2018年8月号の特集4「ラズパイでAIスピーカーをもっと賢く」のPert2 、「塾に着いた」を音声でお知らせスマホの位置情報からGoogle Homeで(P62〜)です。



Codyl ConnectのWebHookコネクターを使って、Raspberry Piを経由してGoogle Homeから音声通知をしています。是非ご覧ください。一般書店で購入可能です。また、Amazonなど、インターネットショッピングでもお求めになれます。上の表紙イメージをクリックするとAmazonの商品紹介のページに飛べます。

Codyl Connectを利用していますので、記事では、子供のスマートフォンの位置情報を契機にしていますが、特定のメールを受信したり、Twitterでダイレクトメッセージを受信したりなど、自分でいろいろな条件を組み合わせることもできます。

ラズパイマガジンの記事のリンクからこのブログを訪問してくださった方へ


記事をお読みいただきありがとうございました。記事はいかがでしたか?感想やコメントなどありましたら、下のコメント欄からお知らせいただけると幸いです。また、本ブログでもいろいろと具体例を紹介していきますので、今後とも、ときどき訪問してみてください。

2018年6月8日金曜日

今年もバケツ稲作の定点観測やってます


Codyl Connectを使って、

  • ラズパイカメラで写真撮影
  • Google Driveにアップロード
  • FaceBookに投稿


毎日自動で動いています。



2017年10月17日火曜日

イベントウォークのステート名

今日のCodyl Connectのアップデートで、イベントウォークのステート名に自分の好きな名前がつけられるようになったので、早速使ってみました。

これまでは例えばstate2のような、自動的に採番されたステート名が勝手に付けられ、変更できなかったのですが、自分で名前を変更できるようになったので、どういう処理をするステートかがわかる名前をつけることができます。

「ラズパイ写真をLINEに通知」のシナリオで実際にやってみます。



state2をwait_for_photo
state5をsave_to_googledrive
state8をnotify_via_LINE

という名前にそれぞれ変更してみました。これだけでも、イベントウォークの見通しがずっと良くなったと感じます。

シナリオ一覧画面では、実行中のステート名が表示されます。ここでも変更後のステート名が表示されますので、今、イベントウォークのどこを実行中かを把握しやすくなりました。


ステートにコメントを追加することもできます。wait_for_photoステートに「ラズパイカメラからの写真データ受信を待ち合わせ」というコメントを追記してみました。追加したコメントはマウスオーバーで表示されます。


自分でイベントウォークをあれこれ編集する人には、とても便利になりました。




2017年7月14日金曜日

iPhoneのホーム画面にConnectのボタンを配置

本ブログでは、自宅のPCを外出先からボタン一つで起動したり、外出先からHDDレコーダーを起動する方法を紹介しました。また、Connectには、ボタン一つで定型メールを送ったり、次の予定を通知したり、自宅までの経路を通知したり、現在位置の近くのお店を検索したりといったことが簡単にできるテンプレートが用意されています、。

この便利なConnectのボタンをiPhoneのホーム画面に配置する方法を紹介します。



これで、Connectアプリを起動してボタンをタップするという2アクション必要だったのが、ホーム画面のボタンをタップするという1アクションでできるようになります。

※この機能を使うためにはiPhoneアプリの0.0.6以降が必要です。それより古いバージョンをお使いの方は、AppStoreから最新版を入手してください。また、Android版にはまだこの機能はありません。


早速やってみましょう。例として、Connectの青ボタンをiPhoneのホーム画面に配置する手順を紹介します。


配置手順

  • Connectアプリを起動します。
  • 左上のハンバーガーメニューをタップします。
  • 「設定」を選びます。
  • 「青ボタンをホーム画面に追加」をタップします。

  • 画面下の中央のアイコンをタップします。


  • 「ホーム画面に追加」をタップします。
  • 「青ボタン」と書かれているところを、わかりやすい名前(例:「PC起動」)に変更します。この名前がホーム画面に表示されます。
  • 右上の「追加」をタップします。
  • ホーム画面に青ボタンのアイコンが表示されます。

完成

以上で完成です。ホームボタンからこのアイコンをタップすると、自動的にConnectアプリが起動して、ボタンが押されて、シナリオが動作し、この例の場合はPCが起動します。是非お使いください。

2017年7月4日火曜日

環境センサーログ6月分

7月になったので、ラズパイとCodyl Connectで自動的に蓄積された6月分の環境センサーログを振り返ってみます。グラフはクリックして拡大してご覧ください。


 全データを一つにまとめたグラフです。気圧が低くて湿度が高いのが6月21日頃と6月26日頃なのが読み取れます。天気が悪かったのかなと思って調べてみると、6月21日は雨、26日は曇りだったようです。


照度センサーだけのグラフです。グラフからはわかりにくいですが、生データをみると、夜更かししているのがバレてしまいます。


気温。最低気温が右肩上がりです。暑くなってきました。


湿度。月の後半は高いですね。


気圧。天気と相関が高そうです。

この記事のグラフは、日経Linux 2017年7月号およびラズパイマガジン2017年8月号に掲載された記事の「Case2」の構成で自動的に作成しました。






生データが入っているGoogleスプレッドシートは「リンクを知っている全員が閲覧可」になっていますので、興味がある方は以下のリンクからご覧ください。

センサーログ2017年06月



2017年6月29日木曜日

バケツ稲作定点観測アニメーション

バケツ稲作のRasbperry Piカメラの定点観測を始めて約3週間が経過しました。
残念ながら最初の稲はダメになってしまいましたが、次の稲は今の所すくすくと成長しています。発芽時期が遅かったので、秋までに実ってくれるかどうか心配です。

折角定点観測しているので、写真をつなげてアニメーションにしてみました。

ダメになった最初の稲

雨が降ったり、藻が湧いたり、意外と変化に富んでいますね。


次の稲

こちらはまだ数日分しか写真がありませんが、着実に成長している様子がわかります。また来月あたりにもアニメーション作ってみようと思います。青々と成長した姿がお見せできるといいのですが。



2017年6月19日月曜日

外出先からHDDレコーダーを起動して録画予約

観たいテレビ番組があったのに、録画予約を忘れて外出してしまった。よくありますよね。

最近のHDDレコーダーには、メーカーが用意したクラウドサービスを使って外出先から予約ができるものも多いですが、少し前の機種ではそうもいきません。私は東芝のRD-BZ810という機種を使っています。メールを使った外出先からの予約録画の機能はありますが、使い勝手が悪く使っていません。

この機種は、

  • マジックパケットによるWake-on-LANが可能
  • ウェブブラウザからの予約録画可能
という特長があります。ということは、HDDレコーダーの80番ポートをポート開放することができれば、先日の記事「自宅のPCを外出先からボタン一つで起動」がそのまま使えます。しかし、私の場合、すでに80番ポートは別の用途に使ってしまっていますのでこの方法が使えません。HDDレコーダーのhttpサーバーのポート番号の変更も多分できません。
そこで、Raspberry Piを、外出先からHDDレコーダーのhttpサーバーへの中継にも使おうと考えました。具体的にはリバースプロキシサーバーの機能を持たせて、外出先から自宅のグローバルIPの9005番へのアクセスをRaspbery Piで受けて、Raspberry PiからHDDレコーダーの80番ポートへ転送するようにします。
Wake-on-LANのきっかけには、先日の記事と同様にスマートフォンのボタンを使います。
  1. スマートフォンのボタンを押す
  2. ConnectからのWebHookをRasbperry Piで受ける
  3. Rasbperry PiからHDDレコーダーにWake-on-LANのマジックパケットを送信
  4. Rasbeprry PiからConnectにWebHookでURLを送信する
  5. ConnectからスマートフォンにURLを通知
  6. スマートフォンで通知をタップするとブラウザが起動して、自宅のHDDレコーダーに接続される。
という流れです。2〜5はConnectのシナリオとRasbpery Piのプログラムが協調して自動で動くので、意識して操作するのは1と6だけです。


前提知識

この記事では、下記の説明は省略します。
  • 自宅ネットワークの固定グローバルIPまたはダイナミックDNS設定手順
  • 自宅ネットワークのポート開放手順
  • HDDレコーダーに自宅LAN内の固定IPアドレスを設定する手順

用意するもの

  • HDDレコーダー
    • Wake-on-LANの機能を持つこと
    • ブラウザから録画予約可能なこと
  • Raspberry Pi
    • SDカードにraspbianインストール済み
    • 有線LANまたは無線LAN
  • ネットワーク環境
    • インターネットに常時接続
    • グローバルアドレスで接続されていること
    • 固定グローバルIPまたはダイナミックDNSが使えること
  • スマートフォン
    • iOS 9.3以降のiPhone

作業1 HDDレコーダーに固定IPアドレスを設定

ルーターの設定でDHCPで固定IPを払い出すように設定する方法と、HDDレコーダーの設定で、DHCPではなく固定IPを使うようにする方法があります。詳細は省略します。

作業2 Raspberry Piにetherwakeをセットアップ

etherwakeをインストールして、一般ユーザーからもWake-on-LANのマジックパケットを送信できるようにします。
  • sudo apt-get install -y etherwake
  • sudo chmod +s /usr/sbin/etherwake
HDDレコーダーがオフの状態で、テストしてみましょう。下記のeth0はRasbperry Piが有線LANに接続されている場合の例です。無線LANの場合はwlan0に変更してください。また、XX:XX:XX:XX:XX:XXはHDDレコーダーのMACアドレスに書き換えてください。
  • etherwake -i eth0 -b -D XX:XX:XX:XX:XX:XX
コマンド実行してHDDレコーダーが起動すればOKです。

作業3 Raspberry Piのプログラムを用意

まずは最新の安定版nodejsをセットアップしましょう。
  • sudo apt-get install -y nodejs npm
  • sudo npm cache clean
  • sudo npm install n -g
  • sudo n stable
  • node --version
このブログ執筆時点ではv8.0.0がインストールされました。続いて、webhookのプログラムです。
  • sudo apt-get install -y uuid-runtime
  • mkdir webhook_etherwake_recorder
  • cd webhook_etherwake_recorder/
  • npm install express
  • npm install body-parser
  • npm install http-proxy
下記の内容をwebhook_etherwake_recorder.jsというファイル名で保存します。このプログラムはConnectからのWebHookの受信、HDDレコーダーへのマジックパケットの送信、ConnectへのWebHookの送信、インターネットからHDDレコーダーへのhttpの転送(リバースプロキシサーバー)の機能をまとめたものです。




プログラムの先頭のTOKEN、MACADDR、IP、PUBLIC_HOST、POST_URLは書き換えます。TOKENはuuidgenコマンドで生成したランダムなUUIDを使うのが良いでしょう。これが漏洩すると、誰でもあなたのHDDレコーダーを起動できるようになります(大した実害はないかもしれませんが)。MACADDRとIPは、それぞれHDDレコーダーのMACアドレスとIPアドレスに書き換えます。PUBLIC_HOSTは固定グローバルIPまたはダイナミックDNSのホスト名(FQDN)に書き換えます。POST_URLは後で書き換えます。

作業4 ポート開放

Raspberry Piのポート9004, 9005をインターネットからアクセスできるようにポート開放します。

作業5 Connectアプリをインストール

スマートフォンにCodyl Connectアプリがインストールされていなければ、インストールします。AppStoreでcodylで検索するか、下記のリンクから開いてください。


Connectのアカウント登録がまだであれば、Connectアプリからアカウント登録が可能です。アカウント登録を済ませて、ログインしましょう。

作業6 シナリオ作成

ブラウザからCodyl Connectにログインしてシナリオを作成します。
  • (1)シナリオタブから「新規シナリオ作成」をクリック
  • (2)わかりやすいシナリオ名に変更
  • (3)説明を書く
  • (4)再実行頻度を0分に変更
  • (5)「イベントウォーク新規作成」をクリック
  • (6)mainの箱の下のプラス記号をクリックしてステートを追加
  • (7)追加されたステート(state2)をクリック
  • (8)「ステートにコネクターを追加」をクリック
  • (9)新しいコネクターを作成を選んで、「ボタン」を選択
  • (10)4つのボタンのうち好きなものを選択
  • (11)「作成」をクリック
  • 12)メソッドに「TRIGGER: when pushed」を選択
  • (13)textの値に「HDDレコーダー起動」と記述。この文字列がスマートフォンのボタンに表示されます。一番下の「閉じる」をクリック
  • (14)state2の下のプラス記号をクリックしてステートを追加、追加されたステート(state5)をクリック。
  • (15)「ステートにコネクターを追加」をクリック
  • (16)新しいコネクターを作成を選んで、「WebHook」を選択
  • (17)わかりやすい名前を入力(例:「WebHook(HDDレコーダー起動)」)
  • (18)外部サービスのURLに、「http://自宅のアドレス:9004/wake」と入力。自宅のアドレスには固定IPの場合はIPアドレス、ダイナミックDNSの場合はDNSホスト名を入れます。
  • (19)「作成」をクリック
  • (20)メソッドに「ACTION: post message」を選択
  • (21)tokenの値にRaspberry Piで設定したTOKENを入力。一番下の「閉じる」をクリック。
  • (22)state5の下のプラス記号をクリックしてステートを追加、追加されたステート(state8)をクリック。
  • (23)「ステートにコネクターを追加」をクリック
  • (24)コネクター一覧から「WebHook(HDDレコーダー起動)」を選択
  • (25)メソッドに「TRIGGER: when received」を選択
  • (26)message_TTLの値に1を入力。一番下の「閉じる」をクリック。

  • (27)state8の下のプラス記号をクリックしてステートを追加、追加されたステート(state11)をクリック。
  • (28)「ステートにコネクターを追加」をクリック
  • (29)コネクター一覧から「プッシュ通知」を選択。まだ作成していなければ、新しいコネクターを作成を選んで、「プッシュ通知」を選択して作成。
  • (30)メソッドに「ACTION: push message」を選択
  • (31)messageに、「HDDレコーダーを起動しました。{{label_10.url}}」を入力。一番下の「閉じる」をクリック。
  • (32)上部のリストからシナリオ名をクリックしてシナリオプロパティ画面に戻る
  • (33)スケジュール設定を有効にする

これでシナリオの設定は完了です。

作業7 Raspberry Piのプログラムを完成させる

webhook_etherwake_recorder.jsの変更部分のうち、作業3で後回しにしていた、POST_URLを書き換えます。コネクタータブから「WebHook(HDDレッコーダー起動)」を選択したときに表示される「外部サービス→Connect」のWebHook URLをコピーして、POST_URLの値としてください。




準備ができたら早速起動してみましょう。下記のコマンドで起動したらListening on port 9004と表示されます。
  • chmod +x webhook_etherwake_recorder.js
  • ./webhook_etherwake_recorder.js
別のシェルから下記のコマンドを実行して動作確認します。(XXXXの部分は設定したTOKENに変更してください)
  • curl -X POST -H "Content-type:application/json" -d '{"token":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"}' http://localhost:9004/wake
OKと表示されて、レコーダーが起動することを確認しましょう。

続いてこのプログラムをRaspberry Piで自動的に起動するように設定します。
  • crontab -e
    • @reboot /usr/local/bin/node $HOME/webhook_etherwake_recorder/webhook_etherwake_recorder.js > /dev/null 2>&1 &

リブート後、先ほどのcurlコマンドをもう一度実行して、正しく動作することを確認します。

完成

シナリオの動作中は、スマートフォンのConnectアプリ上で、「HDDレコーダー起動」ボタンがアクティブになります。



このボタンを押したらHDDレコーダーが起動し、スマートフォンにプッシュ通知が届きます。



通知をタップすると、メッセージが表示されます。


このメッセージの矢印アイコンをタップすると、ブラウザが起動し、HDDレコーダーに接続します。


その後は、自宅で接続しているときと同様に予約録画が可能です。私の所有している機種の場合は、録画予約一覧から、新規予約を選択します。


HDDレコーダーの残り容量が少ないときなど、HDDレコーダーの画面にアラートが通知されているときには、リモコンでアラートを解除するまでは新規予約ができないようです。ここまで準備して外出先でその状態になるととても残念なので、ご注意を。

また、ボタンを押してから通知が届いたタイミングでは、まだHDDレコーダーが起動完了していない場合があります。その場合はスマホのブラウザに「Proxy error」と表示されます。少し待ってリロードしてみてください。

    ラズパイでLet's Encryptの証明書更新に失敗

    ラズパイを自宅用の各種サーバー(DNS、メール、ウェブ他)として使っています。httpsのサーバー証明書の発行・更新にはLet's Encriptを使っています。今日たまたま証明書を更新しようとすると下記のエラーに陥りました。何度やっても失敗します。 ma...