iPhone, iPad, Macのホーム.appアプリの中から使える、こんなボタンを作りました。
動画の音声を聞いていただくとわかるように、これを押すと、家の固定電話から特定番号に電話をかけてくれます。外出先でも可能です。動画の例では、SBI証券の電話番号認証サービスの番号が設定してあります。これは、証券口座に登録した電話から電話をかけると、SBI証券へのログインが3分間だけ許可される認証サービスです。
固定電話には、アナログモデムとRaspbarry Piを接続し、Homebridgeが動作する別のRaspberry PiからMQTT経由操作できるようにプログラムしました。
電話番号認証によるログイン
今年の10月に、SBI証券のログイン方式が電話番号認証に変わりました。ログインする前に、口座に登録してある電話からSBI証券に電話する方式です。私の場合、廃止した固定電話が登録電話だったので、ログインできなくなってしまいました。登録電話をケータイに変更しようにも、ログインできないので手続きができませんでした。他にも、登録番号が離れた実家の固定電話だったりして、ログインできなくなった人は多いようです。
パスワードを盗まれた顧客への損失補填の影響で、SBI証券と楽天証券がログインを厳しくしたようでした。今はどちらの証券会社も、Apple純正の生体認証を使えるようになり、iPhoneとMacで同じ顔認証・指紋認証を使ってログインできて、とても便利になってます。(WindowsとAndroidを使っている人は、シームレスに連携できてるのかな?)とはいえ、今でも固定電話の番号認証が必要な人は、皆無ではないと思われます。例えば、法人口座の場合は、携帯番号を登録できないらしいです。
ということで、「遠隔地にある固定電話から電話番号認証サービスに電話をかけて、認証を得たい」という需要は少なからずあるのではと思います。
Apple HomeKitを使う理由
そこで、「固定電話から電話する」ボタンを提供する、HomeKitアクセサリを作りました。ちなみにアクセサリというのは、HomeKitの用語で、センサー、照明、プラグ、電気錠、電動カーテン、家電などの家庭内のIoT機器のことです。固定電話をIoT化するような感じです。HomeKitのアクセサリならば、自分や家族のiPhoneやMacのホーム.appアプリケーション上に現れて操作できます。
なぜHomeKitなのかというと、「すでにHomebridgeとMosquittoをインストールしたRaspberry Piが稼働しているので、すぐに使える」、という個人的な理由からです。それだけでなく、HomeKitを使えば、家の外からのアクセスや、利用者(例えば利用する家族)管理などが、Appleの力でセキュアに確保されているのもメリットです。
「電話する」機能だけならちょっとしたDIY電子工作ですぐに作れるでしょう。でも、それを家の外からも安全に使えるようネットワーク設定するとか、他の家族にパスワード教えるとか、色々厄介な手間が必要です。HomeKitを使えば、それを全部やってくれます。家と外のアクセスは、HomePodもしくはAppleTVを経由して、私の理解を超越した安全で暗号化されたルートが確保されているはずです。Apple製品に絶対の信頼を置いているユーザ(i.e. 信者)としては、HomeKitを利用しない手はありません。
HomeKitとHomebridgeとMosquitto
前述のように、家にはHomebridgeとMosquittoをインストールしたRaspberry Piが稼働しています。それを使って、HomeKitベースのスマートホームを作ってます。以下のような構成です。
それぞれの構成要素を簡単に説明します。
Mosquitto
Mosquittoは、MQTTブローカーです。MQTTプロトコルでメッセージを交換してくれるサーバーです。LAN接続されたIoT機器とMQTTで通信します。今回作成する「電話をかけるRaspberry Pi」も、このブローカーと通信します。以下のリンクはDIY初心者向けで良いかもです。
Homebridge
Homebridgeは、HomeKitに対応していないIoT機器を、HomeKit対応アクセサリのように見せるサーバーです。HomeKit環境では、アクセサリやiPhoneなどが、HomeKit Accessory Protocol (HAP)で通信します。Homebridgeには、有志が作成した多数のプラグインがあり、さまざまな機器をHAPから操作できるようにします。詳しい内容は、同じサイトの、Homebridgeの説明ページがわかりやすいかと思います。
mqttthingプラグイン
今回は、Homebridgeプラグインの中から、「mqttthingプラグイン」を使用します。これにより、特定のMQTTメッセージを、様々なHomeKitアクセサリの動作にマッピングして、HomeKit側に公開することができます。そこで、今回作成するアクセサリのように、ユーザが操作すると特定のMQTTメッセージを発するHomeKitボタンを作成できます。この設定は、Homebridgeのconfigファイルで行います。以下は、このプラグインのgithubです。
ハードウェアを集める
このRaspberry PiにMQTT接続して、固定電話から電話するハードウェアを考えて選びます。最終的に決まった構成を以下に示します。
電話線から電話をかけるハードウェアには、色々選択肢があるので悩みました。例えば、プシュフォンなので、DTMFを発信する回路を作ればシンプルで良いかもとも思いました。探したところ、秋月電子からDTMFを出すキットが売られていたようですが、今はありませんでした。他にも、電話回線のオンフック、オフフックする回路も実装する必要があります。工作が大変そうなので、他を考えることにしました。
それで考えたのは、アナログモデムです。アナログモデムならATコマンドでオフフックしたり、電話番号をダイアルすることができます。ただ、これも、今時アナログモデムが入手できるかどうかという問題があります。昔は、パソコンのある逸般の誤家庭には、必ずアナログモデムがあったのですけどね。(私も持って持ってたけど捨ててしまってました)
USBファックスモデム(初期不良で断念)
アナログモデムを探したところ、USBシリアル接続のアナログモデムが今も入手可能でした。パソコンからファックスを発信するための、ファックスモデムとしての需要が、今もあるようです。こんな外観の製品です。
ただ、Windows用の製品だと、最初に接続するとUSB HDDに見えて、中にWindows版ソフトなどが入っているものもあるようです。Linuxからはおそらく全く使えないので、Linuxから使えることを明記している製品が良いと思いました。そこで、AmazonからLinux対応というUSB Fax Modemを購入しました。到着して、Raspberry Piに接続したところ、/dev以下にちゃんとシリアルポートが現れました。ターミナルソフトのcuをaptでインストールして使ったところ、ATコマンドが使えました。ちなみに、Macに接続しても同様にシリアルポートが現れ、ATコマンドが使えます。これならいけると感じたのですが、実際に電話線に接続してATコマンドでダイアル試みたところ、No Dialtoneというエラーが出ます。モデムのモジュラージャックに電話器を接続したところ、トーン音が全く聞こえません。電話線アナログ系統が初期不良で壊れていたようです。
アナログモデム
初期不良のファックスモデムを返品して、買い直そうかと思っていたところ、近所の友人が昔のアナログモデムを提供してくれることになりました。Multi-Techという会社のモデムで、性能と安定性に定評のある製品のようです。
良い製品なのに、ダイアルとフック操作しかしない用途で申し訳ないのですが、使わせていただくことにしました。
USBシリアル変換器
昔のアナログモデムなので、インタフェースは本物のシリアルです。電圧もTTLレベルじゃないので、電圧変換回路など作るのが面倒です。なので、素直にUSBシリアル変換器を使うことにしました。多くのUSBシリアル変換器には、PL-2303チップセットが使われていて、LinuxやmacOSからもつかえます。手元にたまたま3本のUSBシリアル変換器がありましたが、そのうち2本がこのチップセットのようで、LinuxとmacOSから使用することができました。その中から、数年前にAmazonで買ったこちらの製品を使うことにしました。
Raspberry Pi
USBシリアル変換器を使うので、USBが付いているコンピュータが必要ですが、大した仕事をしないので、軽いコンピュータで良いです。手元に余っているいくつかのRaspberry Piのうち、一番軽いのがRaspberry Pi 3でした。これでもかなり役不足ですが、使っていないので働いていただきます。
ということで、モデム、シリアル変換器、コンピュータが揃いました。いずれも手近にあったものを集めました。そのためオーバースペックなのですが、眠れる資源の有効活用になるのでヨシとします。
ATコマンドを試す
電話回線、モデム、USBシリアル変換器、Raspberry Piを接続して、ATコマンドを試しました。USBシリアル変換器を接続すると、Raspberry Piに/dev/ttyUSB0として現れました。
シリアル経由でモデム操作するために、前述のように、Raspberry Piに、ターミナルソフトのcuをaptでインストールしました。
$ sudo apt install cu
これで、以下のようにタイプすると、モデムに接続します。atコマンドを打ってokが返ってくれば成功です。
$ cu --parity=none --nostop --line /dev/ttyUSB0 --speed 115200
cuは、~.を打つと終了します。(Raspberry Piにsshで接続してcuを動かしていると、sshセッションも終了してしまいます)
atコマンドはたくさんありますが、今回の目的で使用するコマンドは、以下の3個だけです。
- ate0 入力したコマンドのエコーバックがされなくなります。人が操作する場合には、入力コマンドが見えなくなってしまうのですが、自動化する場合には、邪魔にならなくなるので、良いそうです。でもate0にしても空行が返ってきて、結局は読み飛ばす必要があったので、使う必要はなかったかもしれません。
- atdt1860120449604; 指定した番号にダイアルします。番号は一例です。186は、発信元番号非通知設定の電話番号であっても、強制的に通知する指定です。発信元番号通知は、電話認証に必要です。 0120以下の番号はSBI証券の電話番号認証サービスの番号です。これにダイアルすると、ログイン可能になった旨の自動メッセージが流れます。最後の;は重要です。atdtコマンドは、接続後に、通常は相手とのネゴシエーションを開始し、通信を開始します。なので、atdtコマンドを出した後は、通常はatコマンドが効かなくなります。相手との接続が切れるとatコマンドが復活します。ただし、atdtの最後に;を打つと、ダイアル後もatコマンドが有効になります。
- ath0 オンフックします。電話機のフック(受話器掛け)に受話器を載せる (on) ことです。つまり、電話回線を切断します。ath1はオフフックで、電話回線に接続する機能です。
cuからこれらのコマンドを打ち、電話認証サービスにダイアルして、自動メッセージが返ってきて、適当なタイミングでオンフックする、一連の手順が可能なことを確認しました。
プログラムを書く
プログラムは、Raspberry Pi上のPythonで書きました。まずは、シリアルポートにアクセスして、電話認証サービスに接続する部分を書いてテストし、次に、MQTTのメッセージで電話番号を受けたらダイアルするように拡張しました。以下に簡単に説明します。
番号をダイアルする
プログラムリストで、dial_by_modem(telnumber)という関数の部分です。引数のtelnumberという電話番号にダイアルして、15秒したら切る (オンフックする) プログラムです。実際にやってみないと、モデムがどう応答するかわからないので、試行錯誤して作りました。関数の戻り値は、エラーメッセージです。成功するとsuccessを、失敗するとmodem_errorを返します。
関数の中では、最初にate0でエコーバックを消してます。それでもatコマンドの後に空行が返ってくるので、ser.readline()で空読みして、次のser.readline()でOKが返ってくることを確認してます。
次に、'atdt186' + telnumber + ';' というatコマンドでダイアルします。そして15秒後に、ath0コマンドでオンフックしてます。
MQTT通信する
MQTTのライブラリにはpahoを使いました。MQTTブローカーは、別のRaspberry Piで動作してます。セキュリティは、気休めですが、ユーザ名とパスワードで確保してます。簡単でもっと良い方法があれば、コメントで教えてください。
MQTTでは、次の3個のトピックスを使いました。トピックス名の最初のmqttthingという文字は、後述するHomebridgeのプラグイン、mqttthingが使うトピックスだとわかるように付けてあります。
- mqttthing/modem/set HomeKitがアクセサリを操作する時に使うトピックです。ユーザがiPhoneのボタンをOn/Offするとこのトピックにメッセージが流れます。Pythonプログラムでは、このトピックスをサブスクライブ(購読)します。実際には、ユーザがボタンをOnすると、電話番号がメッセージで流れるようにしました。
- mqttthing/modem/set アクセサリが現在の状態をHomeKitに伝える時に使うトピックです。Pythonプログラムでは、必要に応じてこのトピックスにメッセージをパブリッシュ(出版)します。実際には、固定電話の通話が終了すると、アクセサリがOffになったメッセージを送ります。これを受け取ったHomeKitは、iPhone上のボタンをOff表示にします。
- mqttthing/modem/debug HomeKitとは無関係です。デバッグ用にメッセージを流すトピックです。Pythonプログラムは、必要に応じてこのトピックスにメッセージをパブリッシュします。mosquitto_subコマンドでプログラムからのメッセージを読んで、デバッグに役立てました。
プログラム全体は、以下です。
#!/usr/bin/python
import serial
import paho.mqtt.client as mqtt
from time import sleep
#modem parameters
modem_port = '/dev/ttyUSB0'
modem_speed = 115200
modem_timeout = 10 #time out for ok-reply from modem (sec)
#mqtt parameters
address = '192.168.xxx.xxx'
sub_topic = 'mqttthing/modem/set'
pub_topic = 'mqttthing/modem/get'
debug_topic = 'mqttthing/modem/debug'
username='xxxxxxxx'
password='XXXXXXXX'
def main():
client=setup()
client.loop_forever() #MQTT endless loop
def setup():
mqttc=mqtt.Client()
mqttc.on_connect=on_connect
mqttc.on_message=on_message
mqttc.username_pw_set(username,password)
mqttc.connect(address)
return mqttc
def on_connect(client, userdata, flags, rc):
if rc==0:
print('Connection established.')
client.subscribe(sub_topic)
client.publish(debug_topic,'Python client connected.')
else:
print("Failed to connect: %d\n",rc)
def on_message(client, userdata, msg):
phone_number = msg.payload
client.publish(debug_topic,'dialing ' + msg.payload.decode() + '.')
result=dial_by_modem(msg.payload)
client.publish(debug_topic,'end of dial: ' + result )
client.publish(pub_topic,'false') #turns off HomeKit switch
def dial_by_modem(telnumber):
ret='success'
try:
ser = serial.Serial( modem_port, modem_speed )
ser.timeout = modem_timeout
ser.reset_input_buffer()
#send 'ate0' (echo off) and wait 'ok'
ser.write(b'ate0\r\n')
reply = ser.readline()
reply = ser.readline()
if reply[:2] != b'OK':
raise serial.SerialException('modem_error')
#send 'atdt'(dial) and wait 'ok'
ser.write(b'atdt186' + telnumber + b';\r\n')
reply = ser.readline()
reply = ser.readline()
if reply[:2] != b'OK':
raise serial.SerialException('modem_error')
#wait 15sec
sleep(15)
#send 'ath0'(on-hook) and wait 'ok'
ser.write(b'ath0\r\n')
reply = ser.readline()
reply = ser.readline()
if reply[:2] != b'OK':
raise serial.SerialException('modem_error')
except serial.SerialException as e:
ret=str(e)
finally:
if 'ser' in locals(): #check if ser is assigned
ser.close()
return(ret) #returns success or error message
if __name__ == "__main__":
main()
Homebridgeの設定
Homebridgeには、mqttthingプラグインがすでにインストールされています。なので、今回作ったPythonアプリに対応する設定を、Homebridgeのコンフィグファイルに書き足します。コンフィグのaccessoriesの項目に、以下のブロックを書き足します。
"accessories": [
{
"type": "switch",
"name": "Dial SBI",
"username": "xxxxxxxx",
"password": "XXXXXXXX",
"topics": {
"getOn": "mqttthing/modem/get",
"setOn": "mqttthing/modem/set"
},
"history": "true",
"onValue": "0120592515",
"offValue": "false",
"accessory": "mqttthing"
},
typeは、HomeKitアクセサリの種類です。ここではスイッチとしました。名前は、スイッチの下に表示される文字です。usernameとpasswordはMQTTブローカーに登録したものです。MQTTブローカー (Mosquitto) は、Homebridgeが動作しているのと同じRaspberry Piで動作しているので、アドレスは省略してあります。topicsの中で、使用するトピックスを指定してます。HomeKit側からアクセサリにOn/Offを指示するトピックスをgetOnとして指定し、逆に、アクセサリが自分の状態をHomeKitに伝えるトピックスをsetOnとして指定します。historyは、Homebridgeの機能で、On/Off操作の履歴を記録する指定です。
mqttthinは、getOn, setOnのトピックスに、デフォルトでは、true, falseというメッセージを流して、On/Offを伝え、受け取ります。デフォルト以外の値に変更するのが、onValue, offValueの指定です。offValueをfalseとしている行は、デフォルトのままなのであまり意味ないです。onValueの方で、trueの代わりに、電話をかけるべき番号を書くことにしました。ここではSBI証券の電話認証サービスの番号が書いてあります。
これでHomebridgeを再起動すると、HomeKitのボタンが現れます。
このボタンを押すと、MQTTメッセージが流れます。冒頭のデモ動画をご覧ください。Pythonプログラムが動作していれば、それに応答してMQTTメッセージが流れるはずです。mosquittoのmosquitto_subコマンド(サブスクライブ子アンド)でモニターして動作確認できます。以下にその様子を示します。(Mosquittoが動作しているRaspberry Piで動かしているので、アドレス指定を省略してます)
$ mosquitto_sub -u xxxxxxxx -P XXXXXXXX -t mqttthing/modem/# -v
mqttthing/modem/set 0120592515
mqttthing/modem/debug dialing 0120592515.
mqttthing/modem/debug end of dial: success
mqttthing/modem/get false
最初のsetトピックスは、ボタンが押されたことでHomeKit-->Homebridgeから流れたメッセージです。電話番号を指定しています。debugトピックスは、Pythonプログラムが出すデバッグメッセージです。最後のgetトピックスは、これもPythonプログラムが出したもので、これを受けたHomeKitは、ボタンをOff状態にします。
Pythonプログラム起動設定
動作確認できたので、Rasbperry Piを起動した時に、作成したPythonプログラムが起動するよう設定しました。
まずは設定ファイルを作ります。/etc/systemd/systemに、mqttdial.serviceという名前のファイルを作り、内容を以下にします。作成したPythonプログラムを、/home/hoge/dial/mqttdial.pyとした場合の設定です。
[Unit]
Description=mqttdial
After=network.target
[Service]
Type=simple
WorkingDirectory=/home/hoge/dial
ExecStart=/home/hoge/dial/mqttdial.py
TimeoutStopSec=5
StandardOutput=null
Restart=always
[Install]
WantedBy = multi-user.target
こうしておけば、
$ sudo systemctl daemon-reload #設定ファイル有効化
$ sudo systemctl start mqttdial.service #スタートさせる
$ sudo systemctl status mqttdial.service #動作を確認
$ sudo systemctl stop mqttdial.service #ストップする
$ sudo systemctl enable mqttdial.service #電源投入時に起動するように設定
などのコマンドで動作させたり、次回のリブートで自動起動させたりできます
まとめ
固定電話から電話するHomeKitボタンを、アナログモデム、Raspberry Pi, Mosquitto, Homebridgeの構成で作成しました。世界中のどこからでも、iPhoneやMacのホーム.appを使って、固定電話から電話をかけられます。
HomebridgeとMQTTの組み合わせは、スマートホームの素人DIYに最適ではないかと思ってます。HomeKitアクセサリとかMatterデバイスの自作は大変ですが、MQTTならば、割と簡単にプログラムできます。Arduinoにもライブラリがありますので、ESP-01などの安価なボードでIoTデバイスを作り、これをMQTTで動かして、HomeKitアクセサリに仕立て上げられます。
今回は、電話認証サービスを念頭に自作しました。が、考えてみると、固定電話から自動的に電話したい状況はいろいろあるのではと思い当たります。例えば、一人暮らしお年寄りが転倒したら、これをセンサが感知して、遠くの家族に電話をかけるとか、センサが侵入者を見つけたら電話をかけるような状況です。このような場合、ネットや携帯経由で知らせれば良いのですが、固定電話もその手段に加えると、適用場面が広がると思います。



