背景
Raspberry PiからテレビのOn/Offを赤外線で制御するにはLIRCというライブラリが便利そうだということがわかりました。
しかし、LIRC向けのPythonライブラリで信号を出せるものがどうにも見つかりません(検索上位に出るpython-lircは受信=学習側のようです)。そこで念のためにLIRCのコードを見てみると、0.10.1 からPythonライブラリが同梱されていることに気づきました。
そこでこの記事ではLIRCに同梱されたPythonライブラリを使ってテレビをOn/Offするサンプルを作成します。
環境
- Raspberry Pi Model B V1.2
- 2018-11-13-raspbian-stretch-lite
- 赤外線LED OSI5FU5111C-40
- 200ohm 抵抗
LIRCのインストール
以下のコマンドでLIRCはインストールされますが、デフォルト環境では0.9.4c-9がインストールされてしまうため、Pythonのライブラリが入りません。
pi@raspberrypi:~$ sudo apt-get install lirc
そこで /etc/apt/sources.listを以下のように変更し、testingリポジトリを参照できるようにします。
deb http://raspbian.raspberrypi.org/raspbian/ stretch main contrib non-free rpi
deb http://raspbian.raspberrypi.org/raspbian/ testing main contrib non-free rpi #追加
# Uncomment line below then 'apt-get update' to enable 'apt-get source'
# deb-src http://raspbian.raspberrypi.org/raspbian/ stretch main contrib non-free rpi
そしてお約束のアップデートをかけます。
pi@raspberrypi:~$ sudo apt-get update
インストール。0.10.1-5がインストールされます。
pi@raspberrypi:~$ sudo apt-get install lirc
念のためにPythonライブラリが入っていることも確認しましょう。
pi@raspberrypi:~$ tree /usr/lib/arm-linux-gnueabihf/python3.7/site-packages/lirc
/usr/lib/arm-linux-gnueabihf/python3.7/site-packages/lirc
├── async_client.py
├── client.py
├── _client.so
├── config.py
├── database.py
├── __init__.py
└── paths.py
Python3.7のインストール
インストールされたライブラリがPython3.7用となっているのでPython3.7をインストールします。
sudo apt-get install python3.7
LIRCの設定
こちらのサイトを参考に設定を行います。感謝。
# Uncomment this to enable the lirc-rpi module
dtoverlay=lirc-rpi
dtparam=gpio_out_pin=25
dtparam=gpio_in_pin=24
[lircd]
nodaemon = False
# driver = devinput
driver = default
# device = auto
device = /dev/lirc0
再起動しておきましょう。以下のドライバがロードされていればOKなようです。
pi@raspberrypi:~$ lsmod | grep lirc
lirc_rpi 16384 2
lirc_dev 16384 1 lirc_rpi
また、制御したい機器に合わせて/etc/lirc/lircd.conf.dに設定ファイルを置きます。ここに大量の設定ファイルがあるので、該当するものがあればそのまま使えると思います。
私の場合はSONYのRM-JD030というリモコンですが、なかったのでSONYのテレビ一般用っぽいファイルをコピーし、以下のような設定にしました。
begin remote
name SONY-TV
bits 7
flags SPACE_ENC|CONST_LENGTH
eps 30
aeps 100
header 2574 444
one 1246 557
zero 625 557
ptrail 580
post_data_bits 4
post_data 0x8
gap 45239
toggle_bit 0
min_repeat 2
begin codes
POWER 0x54
end codes
end remote
設定ファイルを配置したらデーモンを再起動します。
pi@raspberrypi:~$ sudo /etc/init.d/lircd restart
設定が反映されていることを確認します。
pi@raspberrypi:~$ irsend LIST "" ""
SONY-TV
devinput-32
devinput-64
以下のコマンドで試し打ちします。
pi@raspberrypi:~$ irsend SEND_ONCE SONY-TV POWER
Pythonからの制御
同梱のテストが参考になります。公式サイトのbrowse the repositoryはリンクが切れているようなので、手元にクローンして確認します。
pi@raspberrypi:~$ git clone git://git.code.sf.net/p/lirc/git lirc
pi@raspberrypi:~$ cd lirc
pi@raspberrypi:~$ vi python-pkg/tests/test_client.py
137 def testRemotesCommmand(self):
138 ''' Do LIST without arguments . '''
139
140 if os.path.exists(_SOCKET):
141 os.unlink(_SOCKET)
142 cmd = [_SOCAT, 'UNIX-LISTEN:' + _SOCKET,
143 'EXEC:"%s ./dummy-server 100"' % _EXPECT]
144 with subprocess.Popen(cmd,
145 stdout = subprocess.PIPE,
146 stderr = subprocess.STDOUT) as child:
147 _wait_for_socket()
148 with CommandConnection(socket_path=_SOCKET) as conn:
149 reply = lirc.ListRemotesCommand(conn).run()
150 self.assertEqual(len(reply.data), 2)
151 self.assertEqual(reply.success, True)
152 self.assertEqual(reply.data[0], 'mceusb1')
153 self.assertEqual(reply.data[1], 'mceusb2')
154 self.assertEqual(reply.sighup, False)
どうやらCommandConnectionクラスにsocket_pathを設定してインスタンスを作成。さらに、Commandクラスの各種サブクラスのインスタンスを作成してrun()を呼べばよさそうです。
また、socket_pathは以下の設定ファイルに記述されています。
[lircd]
# 中略
output = /var/run/lirc/lircd
テストではListRemotesCommandクラス使用していますが(irsend LIST "" ""と同様の挙動)、コマンドの送信にはSendCommandクラスが使えそうです。
ちなみに、実装は以下の個所となります。
598 class SendCommand(Command):
599 ''' Send given key, see SEND_ONCE in lircd(8) manpage. '''
600
601 def __init__(self, connection: AbstractConnection,
602 remote: str, keys: str):
603 if not len(keys):
604 raise ValueError('No keys to send given')
605 cmd = 'SEND_ONCE %s %s\n' % (remote, ' '.join(keys))
606 Command.__init__(self, cmd, connection)
以上より、このようなサンプルとなりました。
import lirc
conn = lirc.CommandConnection(socket_path='/var/run/lirc/lircd')
lirc.SendCommand(conn, remote='SONY-TV', keys=['POWER']).run()
irsend SEND_ONCE SONY-TV POWERと一致した呼び出しで分かりやすいですね。