Node.jsのバージョンコンフリクト

Node.jsのバージョンアップなどを行ったあっと、homebridgeを起動すると、次のようなエラーが出て起動できなくなった。

 

 /usr/lib64/node_modules/homebridge/node_modules/mdns/lib/dns_sd.js:35
throw ex;
^

Error: The module '/usr/lib64/node_modules/homebridge/node_modules/mdns/build/Release/dns_sd_bindings.node'
was compiled against a different Node.js version using
NODE_MODULE_VERSION 48. This version of Node.js requires
NODE_MODULE_VERSION 57. Please try re-compiling or re-installing
the module (for instance, using `npm rebuild` or `npm install`).
at Object.Module._extensions..node (module.js:664:18)
at Module.load (module.js:554:32)
at tryModuleLoad (module.js:497:12)
at Function.Module._load (module.js:489:3)
at Module.require (module.js:579:17)
at require (internal/module.js:11:18)
at Object.<anonymous> (/usr/lib64/node_modules/homebridge/node_modules/mdns/lib/dns_sd.js:24:20)
at Module._compile (module.js:635:30)
at Object.Module._extensions..js (module.js:646:10)
at Module.load (module.js:554:32)

 

どうも、Node.jsのバージョンが、現環境と違うものでコンパイルされているよ。リビルドし直すかインストールし直してね。

と、言っているようです。

インストールし直してもダメだったので、リビルドするのですが、どうやらリビルドコマンドを実行するディレクトリがちゃんとした場所でないと動いてくれないようです。

上記のようにhomebridgeをリビルドするなら、

 

gentoo # cd  /usr/lib64/node_modules/homebridge/

gentoo /usr/lib64/node_modules/homebridge # npm rebuild

 

というように、該当のNode.jsのモジュールがインストールされているディレクトリに移動してからnpm rebuildを実行しましょう。

Let's EncryptでメールサーバーをSSL/TLS化するときの注意

SSL証明書はいままで3~4年更新の安いものを導入していました。

私が管理する義父の会社のサーバーもそうでした。

が、今は無料でSSL化できる良い時代がやってきました。

letsencrypt.jp

未だに80や443以外のポートを使っているときにWebrootでACMEによる実在確認が出来ないという不可解があるのですが(何が悪さしているのか、ログが出てこないので切り分けが出来ない・・・)、普通に80や443のWebrootで実在確認できれば、その後もちゃんと3ヶ月の有効期限が到来する前に、cronでrenewすれば更新されます。

 

で、メールも暗号化していると、Let's Encryptはrenewで更新されているし、Apache+ブラウザもなんともないのに、メールソフトだけは「証明書の有効性が確認できませんでした」という警告が出て、メールの送受信が出来なくなります。(あ、受信しか試してなかったかも)

 

私はPostfix + Dovecotで運用しているのですが、どうもPostfixやDovectは起動時だけ証明書を読むようで(Dovecotだけだったらゴメン)、起動後は証明書の読み込みは一切行わず、起動時に読み込んだ証明書を粛々とクライアントに渡しているようで、Let's Encryptが証明書ファイルを更新しても、反映されないようです。

 

そしてローレベルな解決方法。

更新は週に1度、/etc/cron.weekly配下にリニューアルコマンドのBASHスクリプトを置いていますので、そこにPostfix + Dovecotの再起動を指示しました。

 

/etc/cron.weekly

#!/bin/sh

/usr/bin/certbot renew
/etc/init.d/dovecot restart
/etc/init.d/postfix restart 

 

これで、強制的に反映されるでしょう。

再起動しなくて良い、もっとスマートな解決方法ないかしら?(証明書を買うという方法はナシで)

一眼レフで撮った写真をGoogle Photosに自動アップロード その2 サーバースクリプト設置

<2019年1月にPicasa Photos APIの廃止準備(2019年3月に完全停止)に伴い、ここに記載の方法では写真をアップロード出来なくなりました。Google Photos APIに変更する必要があります。が、本ブログではまだbashスクリプトによるアップロード方法を模索中(勉強中)です。>

 

その1では、PQI AirCardを自宅のWiFiで使うための設定を行いました。

f:id:naoyukinagano:20171031211815j:plain

 

今回は、サーバー側の設定をしていきます。

サーバーで実現することは、

PQI AirCardをIPアドレスで死活監視。

・活性後、PQI AirCard内の画像のうち、JPGだけをFTPで取得。その後に重複取得しないようにJPEGファイル名称をとあるファイルにアペンド(追加)

Google APIの認証情報(CLIENT ID,CLIENT SECRET,REFRESH TOKEN)に基づき、ACCESSS TOKENを取得します。

Google PhotosのアルバムIDに対してACCESS TOKENで認証し、JPEGをアップロードします。

・アップロードが終わったら、撮影日ごとにフォルダを作成してファイルを移動。

これをずーっとループでやっていきます。

 

まずは、Google APIの認証情報を取得します。Googleアカウント(Gmailアドレス)があることが前提です。

こちらに書いてあることをそのままやることで、Google Photos用の認証情報が全て得られます。

そう、パクりです。

qiita.com

デベロッパーコンソールにアクセスします。

https://console.developers.google.com

私は既に一度アクセスしているので、こんな画面になります。

f:id:naoyukinagano:20171031233327p:plain

おそらく、先に適当なプロジェクト名を指定しなければならないと思います。

私の場合はGooglePhotosAPIとつけていまして、上部に表示されています。

左側の「認証情報」をクリックします。

f:id:naoyukinagano:20171031233917p:plain

大きく青く「認証情報を作成」と出ていますね。ここから「クライアントIDの作成」を選びます。

f:id:naoyukinagano:20171031234335p:plain

アプリケーションの種類は「その他」を選び、適当な名前をつけます。

私の場合は「GooglePhotosUploader」とつけてみました。

「作成」を押します。

f:id:naoyukinagano:20171031235224p:plain

このように、クライアントIDとクライアントシークレットが取得できました。これをどこかにコピーしておきます。

(Googleデベロッパーコンソールサイト上でももう一度確認できます。)

 

続いては、Webサーバー上のでの作業です。

これこそ、先のtamanobiさんの記事の通りに実行します。

まずは、クライアントIDとクライアントシークレットを使ってオーソリゼーションコードを取得します。

私はGentoo Linuxをサーバに使っている変な人なので、Gentoo上で作業します。(といってもほとんどのLinuxディストリビューションで変わらない作業だと思いますが)

作業ディレクトリとして、/home/homephotoとしました。

ここにget_authorization_code.shという名前でtamanobiさんのスクリプトを作成します。

# mkdir /home/homephoto

# cd /home/homephoto

# nano get_authorization_code.sh

すみません。なんの芸もなく、tamanobiさんのスクリプトを引用させていただきます。

下記のYOUR_CLIENT_IDは、上記で取得したクライアントIDに置き換えてください。

CLIENT_ID="YOUR_CLIENT_ID"
REDIRECT_URI="urn:ietf:wg:oauth:2.0:oob"
SCOPE="https://picasaweb.google.com/data/"
echo "https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=$CLIENT_ID redirect_uri=$REDIRECT_URI&scope=$SCOPE&access_type=offline"

これは、認証ページにアクセスするためのURLを生成するスクリプトです。

実行権限を与えて、実行します。

# chmod +x get_authorization_code.sh

# ./get_authorization_code.sh

こんなURLが出てきます。

はい、このYOUR_CLIENT_IDの部分を書き換えてブラウザでアクセスしても同じことです。はい。

https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=$CLIENT_ID redirect_uri=urn:ietf:wg:oauth:2.0:oob&scope=https://picasaweb.google.com/data/&access_type=offline

 

こんな画面が出てきますので、許可しましょう

f:id:naoyukinagano:20171104215550p:plain

 許可を押すと、味気なくオーソリゼーションコードが表示されますので、どこかにコピペしましょう。

f:id:naoyukinagano:20171104215918p:plain

 

続いて、リフレッシュトークンとアクセストークンを取得します。

この2つのトークンのうち、リフレッシュトークンは明示的に変更をしない限り変わりませんが、アクセストークンの有効期限が3600秒です。

またまたtamanobiさんの記事をパクります。

スクリプトを作りましょう。

# nano get_access_token_and_refresh_token.sh

スクリプトの中身は次の通りですが、YOUR_CLIENT_ID、YOUR_CLIENT_SECRET、先ほど取得したオーソリゼーションコードをYOUR_CODEに入れ替えましょう。

CLIENT_ID="YOUR_CLIENT_ID" # クライアントID
CLIENT_SECRET="YOUR_CLIENT_SECRET" # クライアントシークレット
AUTHORIZATION_CODE="YOUR_CODE" # 認可コード
REDIRECT_URI="urn:ietf:wg:oauth:2.0:oob" # HTTPサーバを起動しなくてもAuthorization_Codeを取得できる

curl --data "code=${AUTHORIZATION_CODE}" --data "client_id=${CLIENT_ID}" --data "client_secret=${CLIENT_SECRET}" --data "redirect_uri=${REDIRECT_URI}" --data "grant_type=authorization_code" --data "access_type=offline" https://www.googleapis.com/oauth2/v4/token

で、実行権限を付与して実行します。

# chmod +x get_access_token_and_refresh_token.sh

# ./get_access_token_and_refresh_token.sh

そうするとJSON形式でリフレッシュトークンとアクセストークンが取得できます。

このリフレッシュトークンが大事になります。

 

そして、Google Photosの特定のアルバムにアップロードしたいので、アルバムIDを調べましょう。

ブラウザで次のURLにアクセスします。

USER_IDの部分はGoogleのユーザーIDに書き換えてください。ユーザーIDはGmailアドレスの@より前ですね。

https://picasaweb.google.com/data/feed/api/user/USER_ID

そうすると、アルバムのリストが現れます。

f:id:naoyukinagano:20171104222659p:plain

アルバムのリンクURLを見ると、次のような法則になっています。

https://picasaweb.google.com/"Picasaのユーザー識別"/"アルバムID"?locked=true

このアルバムIDを控えておきます。

 

さて、ここからはオリジナルだったりtamanobiさんのスクリプトの改変だったりです。

 

まず、PQI AirCardの死活監視を行い、アクセスできたらFTPでJPGだけをGETします。

GETし終わったら、同じファイルをGETしないよう、GETしたファイルリストを作ります。

アクセストークンを取得し、JPEGファイルをGoogle Photosにアップロードします。

その後、exiftoolというアプリケーションを使って、年月日(YYYYMMDD)のディレクトリに分けて保存します。

というのを繰り返します。

お気づきの方もいらっしゃると思いますが、デジカメでファイルを連番で記録するようにしていると、XXX_9999.JPGの次は新しくディレクトリを作成し、たとえばDCIM/200CANONの次はDCIM/201CANONに保存されます。

もし10000枚目だとしたらあきらめてください((^^ゞ)

ちなみに、DCIM/200CANONディレクトリの中身を全て消去し、DCIM/201CANONのディレクトリ自体を消去すると、次からデジカメはDCIM/200CANONに撮影写真を保存していきます。

あと、なぜDCIM200CANONなのかというと、PQI AirCardにはDCIM/199WIFIという制御用ディレクトリがあるため、200CANONにしないと199WIFIに保存してしまいます。制御用のJPEGもダウンロードしてしまうことになるためです。

 

getophoto.shという名前のスクリプトにしました。

#/bin/bash

PQI_AIR_IP="192.168.xxx.xxx"

PQI_DIR="sd/DCIM/200CANON"

USER_ID="USER_ID"

CLIENT_ID="YOUR_CLIENT_ID"

CLIENT_SECRET="YOUR_CLIENT_SECRET"
REFRESH_TOKEN="YOUR_REFRESH_TOKEN"

ALBUM_ID="YOUR_ALBUM_ID"

 

#自身のディレクトリに移動
cd `dirname $0`

#プロセス番号をpid.txtに保存

echo $$ > pid.txt

 

#ここからループ

while :

do

 

#PQI AirCarにhttp出来るかどうか試す
ALIVE=$(/usr/bin/wget -nv --spider --timeout 60 -t 1 http://${PQI_AIR_IP}/ 2>&1 | grep -c '200 OK')

 

#PQI AirCadが生きていた場合の処理

if [ $ALIVE -eq 1 ]; then

 

#過去にGETしたJPGファイルのリストdonelist.txtを読み込む。念のため改行を削る

DONELIST=`cat donelist.txt | sed -e ':loop; N; $!b loop; s/\n//g'`

 

#FTPでJPGをGET
/usr/bin/wget -m -q -N --reject=${DONELIST} --no-host-directories --no-directories -A .JPG ftp://${PQI_AIR_IP}/${PQI_DIR}

 

#GETしたJPGを改行などを削除してdonelistに保存

JPGLIST=`/bin/ls | grep .JPG`
if [ -n "$JPGLIST" ]; then
/bin/echo "," >> donelist.txt
/bin/ls | grep .JPG | sed -e ':loop; N; $!b loop; s/\n/,/g' >> donelist.txt
sed -i -e ':loop; N; $!b loop; s/\n//g' donelist.txt
fi

 

#アクセストークン取得
curl -s --data "refresh_token=${REFRESH_TOKEN}" --data "client_id=${CLIENT_ID}" --data "client_secret=${CLIENT_SECRET }" --data "grant_type=refresh_token" https://www.googleapis.com/oauth2/v4/token > token.JSON
ACCESS_TOKEN=`cat token.JSON | jq -r '.access_token'`


ENDPOINT="https://picasaweb.google.com/data/feed/api/user/${USER_ID}/albumid/${ALBUM_ID}?access_token=${ACCESS_TOKEN} "

 

DIR="./" # FTPでGETしたJPGファイルが保存してあるディレクトリを指定

IFS_BAK=${IFS}
IFS="
"

 

#ファイル数だけループ
FILES=`ls -1 ${DIR}`
for FILE in ${FILES}
do

 

#MIMEタイプの指定
if [ "${FILE}" != "" ]; then
TYPE="UNKNOWN"
case "${FILE}" in
*\.bmp) TYPE="image/bmp";;
*\.JPG | *\.jpg) TYPE="image/jpeg";;
*\.png) TYPE="image/png";;
*) TYPE="UNKNOWN";;
esac
# echo "$TYPE $FILE $LENGTH"
if [ "${TYPE}" = "UNKNOWN" ]; then
continue;
fi
LENGTH=`ls -l "${DIR}/${FILE}" | tail -n1 | sed -E 's/ +/ /g' | cut -d' ' -f5`

 

#ファイルをアップロード

curl -s -XPOST "${ENDPOINT}" \
-H "Content-Type:${TYPE}" \
-H "Content-Length:${LENGTH}" \
-H "Slug:${FILE}" \
--data-binary "@${DIR}/${FILE}" > /dev/null
sleep 1
fi
done
IFS=$IFS_BAK

 

#ファイルのアップロードが全て終わったら、撮影日でYYYYMMDDというディレクトリを作ってファイルを移動
/usr/bin/exiftool -q '-Directory < CreateDate' -d %Y%m%d *.JPG
fi

sleep 10
done

 

これも実行権限をつけて実行しましょう。

chmod +x getophoto.sh

./getophoto.sh &

 

これで、サーバーと同じネットワークセグメントにいる限り、つまり、室内では、撮影する度にGoogle Photosの指定したアルバムにアップロードされます。

たしかGoogle Photosの仕様で、1アルバム1000枚までしか保存できないはずですので、上限に達したら新しいアルバムに変えましょう。

一眼レフで撮った写真をGoogle Photosに自動アップロード その1 PQI AirCardの固定IP化

私は10年前に購入したCanon EOS 40Dを今も現役で使い続けています。

f:id:naoyukinagano:20171031211751j:plain

 

しかし、このEOS 40Dは、当然Wi-Fiには対応しておらず、さらに記録媒体はコンパクトフラッシュという有様。

Wi-Fiに公式対応するためには、WFT-E3が必要になりますが、これがお高い!

 

スタパ齋藤の「週刊スタパトロニクスmobile」 サクッと無線接続!!EOS 40D用ワイヤレストランスミッター「WFT-E3」

 

で、かなり前からPQI AirCardにmicroSDを入れ、SDカードーコンパクトフラッシュアダプターというものを噛まして使っていました。

f:id:naoyukinagano:20171031211815j:plain

 

室内では家庭内LANのWiFiに、屋外では主にiPadテザリングを掛け、ShutterSnitchというアプリでJPEGだけ吸い出して大画面プレビューとしていました。

これはこれで便利でしたが、この変換、書き込みが遅いため、連射を多用する野鳥撮影には向いていなかったため、最近は通常のコンパクトフラッシュに戻し、どうしてもiPadに吸い出す場合はLightning USBカメラアダプタ+モバイルバッテリー、そしてコンパクトフラッシュリーダーライターを使って読み出していました。

ちなみに、iPad Pro 9.7 32GB + Lightning USB 3カメラアダプタ + エレコムMRS-MB07 + ANKER PowerCore Speed 10000 QC で稼働しております。 モバイルバッテリが5V 2.4Aを給電できないと、iPadはリーダーライタを認識できません。これはリーダーライタのせいではありません。

 

 

 

 

 

 

さて、話がそれましたが、そうなるとPQI AirCardが外出先では使われなくなります。

そこで、家庭内利用かつイエネコ撮影用に限定し、撮影されたそばからGoogle Photosにアップロードし、Google Photosではアルバム単位や一枚ごとに共有できるようにしました。

Googleドライブにアップロードして、Photosと共有するパターンはよく見かけますが、2048px以内なら容量にカウントされないPhotosに直接アップロードします。

 

前提として、私はプログラムが書けないサーバー管理者ですので、Linuxサーバー上で様々なソフトウェアをシェルスクリプトで組み合わせるという方法で達成します。

 

まずは、PQI AirCardのIPアドレスを固定化します。

PQI AirCaedに挿入するmicroSDカード直下にautorun.shというファイルを置くと、AirCardの起動時に自動実行してくれるのですが、これを利用して、宅内のWi-FiWi-Fi子機として接続しつつ、IPアドレスを固定化します。

参考にしたのはこのサイトです。

oichinote.com

Perlを書けるのがうらやましい・・・。

リンク先のパターンでは、PQI AirCard内のLinuxコマンド、/usr/bin/w2でWiFi接続とDHCPによるIPアドレス取得までやってしまい、DHCPで取得したIPアドレスの第4ブロックだけを91に書き換えるという動作を行っています。

私の家ではクラスCプライベートアドレスの192.168.0.0/24の中からDHCPを割り当てます。なので、192.168.0.91となります。

しかし、これをそのまま使おうとしたのですが、私のAirCardは最後にWebサーバー上のCGIに自身のIPアドレスをPOSTし、メールでスマホに報告させるスクリプトがautorun.shの最後に記述されています。

これが動かないのです。

しかし、192.168.0.91にpingもhttpもftpも通る。なぜ?

突き当たったのは、/usr/bin/w2だとデフォルトゲートウェイDHCPで設定されるのですが、その後にifconfigを実行すると、デフォルトゲートウェイが消去されることがわかりました。

PQI SAirCardにはrouteコマンドも用意されているので、route add default gw 192.168.0.xxxで設定されますが、なんせプログラムが書けないので、ifconfigする前にゲートウェイIPアドレスを取得して変数に代入するやり方がわからないのです。

だれか教えてプリーズ!

で、苦肉の策、デフォルトゲートウェイIPアドレスの最終ブロックが1であると決め打ちしてroute add default gwコマンド実行させます。

 

こんな感じになりました。私の場合は91から160に変更しています。

 #!/bin/sh
sleep 5
/usr/bin/w2

# sleep disable
kcard_cmd -s 0

sleep 1

script=`cat <<'EOF'
my $ifname;
my @ipadr;
my $pqi='160';

open (my $fh, '-|', qw/ifconfig/) or die $!;
while (my $line = <$fh>) {
$ifname = $1 if ($line =~ /^(\w+)/);
@ipadr = ($1, $2, $3, $4) if ($line =~ /inet\s+.*?(\d+)\.(\d+)\.(\d+)\.(\d+)/);
if ($ifname and @ipadr and ($ipadr[0] == 192) and ($ipadr[1] == 168)) {
system "ifconfig $ifname $ipadr[0].$ipadr[1].$ipadr[2].$pqi";
system "route add default gw $ipadr[0].$ipadr[1].$ipadr[2].1";
last;
}
}
close ($fh);

EOF
`

perl -e "$script"

 

この後、ifconfigの内容をWebサーバーのCGIにPOSTするスクリプトが続いています。

これはPQI AirCardをいじり倒しているひとりブログさんが、ツイッターにメンションしているものを、メールで飛ばすように変えたものです。

hitoriblog.com

autorun.shの最後のほうに次のように記述しました。

sleep 1


ip=`ifconfig mlan0 | grep inet`

wget -O - --post-data="${ip}" http://foo/bar/mail.cgi

 

CGIはPOSTされたIPアドレスをメールで報告するだけです。

サーバーに設置してあるmail.cgiはこんな感じ。どこから持ってきたのかしら?

#!/usr/bin/env perl

use URI::Escape;
read (STDIN, $inputescaped, $ENV{'CONTENT_LENGTH'});
$input = uri_unescape($inputescaped);

$sendmail = '/usr/sbin/sendmail'; # sendmailコマンドパス
$from = 'foo@bar.com'; # 送信元メールアドレス
$to = '1hoge@docomo.ne.jp'; # あて先メールアドレス
#$cc = 'hage@example.com'; # Ccのあて先メールアドレス
$subject = 'PQIAirCard'; # メールの件名
#$msg = 'body'; # メールの本文(ヒアドキュメントで変数に代入)

# robot対策

if ($input =~ m/inet/ ) {

$input =~ s/ //g; # 空白2文字分を削除

# sendmail コマンド起動
open(SDML,"| $sendmail -t -i") || die 'sendmail error';

# メールヘッダ出力
print SDML "From: $from\n";
print SDML "To: $to\n";
#print SDML "Cc: $cc\n";
print SDML "Subject: $subject\n";
print SDML "Content-Transfer-Encoding: 7bit\n";
print SDML "Content-Type: text/plain;\n\n";

# メール本文出力
print SDML "$input";

# sendmail コマンド閉じる
close(SDML);
}

print "OK";
exit; 

 

これで、カメラの電源投入後にスマホにメールが届けば成功です。

Raspberry Piのメディアレンダー化作戦 DLNA編

Raspberry Pi 2 Model Bのメディアレンダー化作戦。次はDLNAのレンダラーです。

Raspbian Stretch Liteですので、動画はレンダーしません。

LinuxはシンプルにCLI派です。

f:id:naoyukinagano:20171030225612j:plain

 

さて、RaspbianのDLNAレンダーですが、Gstreamerを利用したGmediarenderで構築します。

よくGmediarendererと記載されているサイトがありますが、Raspbian Stretchにはgmediarenderというパッケージがありますので、それを使います。

パッケージが用意されているなんて、さすが最新OS

なので、こちらも依存関係含めてパッケージを入れるだけ。

 root@raspberrypi:~# apt-get install libupnp-dev libgstreamer1.0-dev gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-alsa gmediarender

 

さて、ハマったのが起動。

普通なら

 root@raspberrypi:~# systemctl enable gmediarender

で、自動起動の登録、

root@raspberrypi:~# systemctl start gmediarender

で起動するはずですが、ps -eでもgmediarenderというプロセスは見つからず。

 

起動スクリプトが/etc/init.d/gmediarenderに入っているので見てみると、

[ "x$ENABLED" = "x1" ] || exit 0

こんな一行が。

$ENABLEDってどこに定義してあるんだろうとスクリプトを見ても、見当たりません。

ようやく探し当てたのが、/etc/default/gmediarenderというファイル。

というやらこいつが設定ファイルだったようです。

 

設定ファイル/etc/default/gmediarenderの中の

ENABLED=1

UPNP_DEVICE_NAME="$(hostname)"

INITIAL_VOLUME_DB=0.0

ALSA_DEVICE="hw:1,0"

DAEMON_EXTRA_ARGS="--logfile /var/log/gmediarender.log"

という5項目を設定しました。

ENABLEDがコメントアウトしてあったので起動しなかったんですね。

UPNP_DEVICE_NAMEは、DLNAのコントローラに通知する名前なので、よさげなものをつけて下さい。私はホスト名で。

INITIAL_VOLUME_DBは音量をdbで指定します。0は音量最高。こうしないと出力が小さくなりますので。

ALSA_DEVICEはサウンドデバイスの設定です。aplay -lで出てくるデバイスを指定できます。shairport-syncで指定したoutput_device=と同じ、USB Audioを指定しています。

DAEMON_EXTRA_ARGSは他に定義したい起動オプションを記載するようです。とりあえずログ指定を。

 

ではようやく起動です。

root@raspberrypi:~# systemctl start gmediarender

 

Windows10から見えるようになりました。

f:id:naoyukinagano:20171031003814p:plain

Raspberry Piのメディアレンダー化作戦 AirPlay編

今年で67になる母は、つい先日までイナカの実家で一人暮らしをしていました。

そんな67の母は私のすすめで2015年9月にDENONのネットワークオーディオRCD-N9を購入。

CDとラジオ、そして海外のネットラジオを楽しんでいました。

(購入当初はらくスマのdヒッツをBluetooth経由で聞いていましたが、Bluetoothが面倒なのか、dヒッツが高かったのか、聞くのをやめてしまいました。)

 

そこで、よかれと思いRaspberry Pi 2 Model Bを調達し、OpenMediaValueをインストールして、手持ちのCDを全てリッピングして保存、リッピングのやり方も解説して置いていきましたが、こちらも使われずにいたようです。

 

CDで聞けるなら、CDを入れ替えた方がいいようです。

 

そこで、弟夫婦との同居を機に、Raspberry Piを引き上げてきました。

そのRasoberry Piに第二の人生を送ってもらおうと、寝室のメディアレンダーとして活躍してもらうこととしました。

忘れないうちにインストール記録です。

 

OSはRaspbian Stretch Liteを、NOOBSを使わずに、イメージを直接SDに焼いて起動し、有線イーサを使ってdhcpcd.confで固定IPにすること、自分の持っている秘密鍵SSH接続できるようにすること、デフォルトのpiユーザーをsudo出来ないようにすること、sudoにパスワードが必要になるようにすること、自分がrootにsu(Switch User)できるように設定しました。

このあたりは難しくないので他に譲ります。

そして、sudoは面倒なので、rootで作業してしまいます。(よい子はまねしちゃだめ)

 

ケースから出しているので、こんな感じです。

f:id:naoyukinagano:20171030225137j:plain

 

Raspberry Piのデフォルトアナログ音声出力は音の悪さに定評がありますので(笑)、USB Audioに変えます。

 

たまたま以前まで使っていたBEHRINGER UCA202 U-CONTROLが手元にあったので、こちらを使います。

中身はTI PCM 2902のようです。たいへん素直なデバイスです。

せっかくデジタルOPT出力があるので、MARANTZのNA7004に入力して音出しを試みます。

f:id:naoyukinagano:20171030225612j:plain

NA7004って既にレンダラーじゃん!っていうツッコミはなしでお願いします。

完成したら別の部屋で使いますので・・・。

 

まずはAirPlayのレンダラーになっていただきます。

shairportの後継、shairport-syncを使います。(後継と言っても派生のようですが)

 

さて、githubからshairport-syncをgitしてautoconfしてmake・・・とおもったら、stretchはもうパッケージが準備されています。

さすが最新OS!

root@raspberrypi:# apt-get install shairport-sync

さて、設定しましょう。

たぶん、shairport-syncを動かすためのユーザーが必要でしょうから、作ります。

 root@raspberrypi:# groupadd -r shairport-sync

 root@raspberrypi:# useradd -r -M -g shairport-sync -s /usr/bin/nologin -G audio shairport-sync

デフォルトのアナログやHDMI出力から音を出すわけではないので、出力先を確認しておきましょう。

再生ソフトaplayの機能を借りて、再生デバイスの一覧を出してもらいます。

root@raspberrypi:~# aplay -l
**** ハードウェアデバイス PLAYBACK のリスト ****
カード 0: ALSA [bcm2835 ALSA], デバイス 0: bcm2835 ALSA [bcm2835 ALSA]
サブデバイス: 8/8
サブデバイス #0: subdevice #0
サブデバイス #1: subdevice #1
サブデバイス #2: subdevice #2
サブデバイス #3: subdevice #3
サブデバイス #4: subdevice #4
サブデバイス #5: subdevice #5
サブデバイス #6: subdevice #6
サブデバイス #7: subdevice #7
カード 0: ALSA [bcm2835 ALSA], デバイス 1: bcm2835 ALSA [bcm2835 IEC958/HDMI]
サブデバイス: 1/1
サブデバイス #0: subdevice #0
カード 1: CODEC [USB Audio CODEC], デバイス 0: USB Audio [USB Audio]
サブデバイス: 0/1
サブデバイス #0: subdevice #0

USBはカード1のサブデバイス0なので、hw:1,0かplughw:1,0のどちらかを指定することとなります。

 

shairport-syncのコンフィグはお行儀悪く、/etcの直下にshairport-sync.confがあります。

 root@raspberrypi:~# nano /etc/shairport-sync.conf

 

 general =

{

で始まる項目を設定していきます。

コメント多過ぎですね(笑)

 

まずは、AirPlayデバイス名を決めるので、name =の前のコメントアウト//を消して、ホスト名をデバイス名にします。

name = "%H"; // This means "Hostname" -- see below. This is the name the service will advertise to iTunes.

soxrを使うと音が良くなると聞いたので、// interpolation = "basic"を下記のように変えます。

interpolation = "soxr"; // aka "stuffing". Default is "basic", alternative is "soxr". Use "soxr" only if you have a reasonably fast processor.

ALSAドライバーを使うので、一応明示しておきます。

output_backend = "alsa";

なんか音が小さいな、と思ったら、ボリュームコントロールが有効でした。無効にします。

ignore_volume_control = "yes";

 

続いて

 alsa =

{

で始まる項目を設定していきます。

USB Audioにアウトプットするので、

output_device = "hw:1,0"; 

mixer_control_name = "PCM";

mixer_device = "default";

に変更します。("hw:1,0"は"plughw:1,0"でもOK。)

 

では、nanoを閉じましょう。ctrl + xでyですね。

 

次に、デーモンを自動起動する設定と、実際に起動するところです。

まずは、raspbian起動時に自動起動するように設定。

root@raspberrypi:~# systemctl enable shairport-sync

では、最後、祈るように起動しましょう。

root@raspberrypi:~# systemctl start shairport-sync

 

なにかいろいろ(最初にgithub版をmakeしたものをインストールしたので、アンインストールしてパッケージをapt-getしたため)やっているうちに、"Failed to attach mixer"と出てきました。

Raspbian自体を再起動するとしっかりと直りました。

このFailed to attach mixerは、ユーザーshairport-syncをグループaudioに入れていないことによって、ミキサーが操作できないために起こるエラーです。

 

さて、iPadから再生してみましょう。

f:id:naoyukinagano:20171031001331j:plain

 

ちゃんと選べて再生できるようになりました!!

寝室のESPEasy in Sonoff常時通電化

以前、SonoffというESP8266が搭載されたオールインワンなIoTリレーデバイスファームウェアをESPeasyに入れ替え、サーバーにHomebrigeをインストールと設定をし、Apple Homekitでコントロールするというのをやりました。

 

gentoolinux.hatenablog.com

gentoolinux.hatenablog.com

このとき、Sonoffのルールに、

On System#Boot do
gpio,12,1
EndOn

と記述し、通電されると約10秒後にSonoffが起動し、まずは通電を開始し、寝室のライトが付くようにしました。

しかし、この10秒が結構待たされます。

嫁にも言われます。

そのうち、帰宅後から就寝まで、つきっぱなしになります。(笑)

 

そこで、リビングの3chリレーと同様、Sonoffを常時通電させ、GPIOに接続した押しボタンスイッチでON/OFF出来るようにします。

 

スイッチに100VのL(ライン)とN(ニュートラル)が来ていれば、そこにSonoffを噛ませ、GPIOを結線するだけで以上終了・・・。だといいのですが、たいていは天井に隠蔽された差し込みコネクタでLだけが分岐され、スイッチにはL側だけが配線されています。

となると、Sonoffを動作させるためにAC100V(もしくはDC5Vですが、これもAC100Vで作る必要があります。)をスイッチ部分まで別途引いてくる必要があります。

 

まずは、天井の通線作業です。

幸いに、寝室スイッチ近くの脱衣場の天井に換気口があり、換気口をとれば腕が入ることと、風呂場の天井に点検口があること、その天井に常時通電の100Vを差し込みコネクタで分岐している部分が手に届きます。

 

VVF1.6を使っての通線作業です。

VVF1.6が堅くて良かった。

点検口から換気口まで約2m弱ですが、届いてくれました。

あとは換気口から手探りで寝室スイッチまでVVFを通線します。

f:id:naoyukinagano:20171004221420j:plain

 

その後、常時通電している差し込みコネクタを1端子多いものに交換し、通線したVVFと結線します。

ちなみに、この作業は有資格者でないとできません。

f:id:naoyukinagano:20171004221515j:plain

 

では、ここからは有資格者じゃなくてもできる改造作業です。

 

まず、寝室の天井ランプに仕込んでいたSonoffを外し、ランプを原状回復させます。

f:id:naoyukinagano:20171004221833j:plain

 

Sonoffにさらなる改造を施します。

通常のSonoffは、Inputの100vをDC変換し自身の動力源としつつ、リレーを介してそのままOutputに渡しています。

しかしながら、先ほど申し上げたように、スイッチには100のL側しか来ていません。

なので、InputとOutputを絶縁した上で、Output側の接点両方をリレーでON/OFF出来るように結線し直さなくてはなりません。

 

まずは、取り出したSonoff基板のN側結線を、基板ごとラジオペンチを使って壊します。

カッターで予め切り込みを入れておくときれいに切れると思います。

赤丸の部分です。

f:id:naoyukinagano:20171004222646j:plain

 

続いて、L側の結線を絶縁です。

結線は裏面と表面両面にパターンがあります。

裏のパターンは次の写真の赤丸のところで、先に半田を吸い取って薄くしてから、カッターでカットします。

円内上部には抵抗の足が、円内右にはリレーの足があります。

f:id:naoyukinagano:20171004223011j:plain

 

表面もL側結線のパターンが走っているので、5VDC変換回路に影響のない、リレーギリギリのところでパターンカットします。

見づらいですが、リレーに沿って、カッターでパターンを削っています。

f:id:naoyukinagano:20171004223533j:plain

 

パターンカットしたら、しっかりとテスターで絶縁されているか確認しましょう。

 

続いて、Output側をリレーにつなぎます。

裏面のパターンが近い部分に、おあつらえ向きに、元々半田が持ってあります。

この間をなるべく太い線で結線します。

電流も多めにがなれるでしょうから、半田も多めに盛っておきましょう。

(写真の青い被服部分は取っ手として使っていたので、半田が終わったらカットしました。)

f:id:naoyukinagano:20171004224042j:plain

 

つづいて、スイッチとなるGPIOの結線ですが、これは以前にピンヘッダーを半田付けしていました。

シリアル通信で使わなかった1本が、実はGPIO14だったのです!

こんな小さくて安いのに、なんて拡張性が高いのでしょう!!

(今、Sonoffをもう一つ買って、温湿度センサーのDHT22をこのGPIO14に結線し、室外温度を計測することを計画中です。)

写真はもうGNDとGPIO14を結線していますが、スイッチがある側から、3.3V,RX,TX,GND,GPIO14です。

このGPIO14とGNDを取り出し、プッシュスイッチに接続し、ショートさせてSwitch Inputとします。

プルアップ抵抗をESP8266内部に持たせるので、抵抗は結線せずに直接プッシュスイッチに接続する予定です。

f:id:naoyukinagano:20171004225226j:plain

 

GPIO14とGNDのケーブルをケースから出します。

f:id:naoyukinagano:20171004225326j:plain

 

ジャンパーケーブルは細いので、そのままでは当然VVF用のスイッチには入りませんから、棒形圧着端子に入れて圧着を掛けます。

私はジャンパ端子ごと圧着しました。その方が抜けにくそうだったので。

 

棒形圧着端子はあ例えばこんな商品です。(太さがこれで適合するのかは保証しません。)

 

こんな感じで圧着しました。

f:id:naoyukinagano:20171004230106j:plain

 

では、これをスイッチがある壁の中に押し込みます。

Sonoffは高さが88mmなので、壁のスイッチの穴は簡単に入りますし、厚さも23mm程度なので、LGSスタッドの主流である45形か50形であれば、フトコロは45mmか50mmですので、余裕で入ります。

VVFが硬いことがここでは難儀しますね。

 

ここからは電気工事士有資格者の作業となります。

通線した100VのLを片切スイッチに、片切スイッチの片方をSonoffのInputのLに。100VのNはそのままSonoffのInputのNに。と、言いたいところですが、私はパイロットランプ付きのスイッチを購入したので、差し込みコネクタで分岐し、片方をパイロットランプ付きスイッチのNに接続しました。

今までのスイッチに付いていた寝室ランプのVVFをSonoffのOutputに接続します。

GPIO14とGNDのケーブルをプッシュスイッチに接続します。

 

接続途中ですが、スイッチの作業中の様子です。

f:id:naoyukinagano:20171004231407j:plain

 

全て結線して壁に押し込んで、スイッチをC形挟み込み金具で壁に固定し、スイッチパネルをつけて完成です。

f:id:naoyukinagano:20171004231515j:plain

 

上がGOIO14とGNDをショートさせるプッシュスイッチ、下がSonoffに電源を供給するパイロットランプ付き片切スイッチです。

Sonoffが暴走して再起動する際は、下のスイッチをOFF/ONすれば良いということになります。

 

ここまでできたら、SonoffのESPEasyの設定を変更します。

 

Sonoffに設定しているIPアドレスにブラウザからアクセスしましょう。

まず、Devicesです。

すでに、前回設置した際に、Task1にGPIO-12としてリレーを割り当て、Task2にGPIO-0として基板上のボタンを割り当て、このボタンでもON/OFF出来るようにしていました。が、前回も今回も、ボタンは押せない場所にあります(笑)。

このGPIO-0をGPIO-14に変更するだけです。

 

以下、ブラウザのキャプチャですが、Task2のEditを押します。(すみません。すでにGPIO-0からGPIO-14に変更した画像です。)

f:id:naoyukinagano:20171004233048p:plain

 

そして、「1st GPIO:」からGPIO-14を選び、念のため「Pull UP:」にチェックが入っていることを確認して「Submit」を押します。(なにせ、プルアップ抵抗をつないでませんからね。Pull UPにチェックしましょう。)

 

Rulesは買えるところがありませんが、一応おさらい。

f:id:naoyukinagano:20171004233427p:plain

 

次のように記載しています。

On System#Boot do
   gpio,12,1
EndOn

On PowerOn Do
   gpio,12,1
EndOn

On PowerOff Do
   gpio,12,0
EndOn

On Button#State Do
   If [Button#State] = 0
     gpio,12,0
     gpio,13,1
   Else
     gpio,12,1
     gpio,13,0
   EndIf
EndOn

 

GPIO-0のときも、GPIO-14の時も、devicesでButtunというデバイス名で定義しているので、特に変更ありませんでした。

 

ちなみに、Homebridgeも変更するところはありません。

これで完成です!

 

寝室ランプがスイッチでも、AppleHomekitでも瞬時に反応し、快適になりました。