読者です 読者をやめる 読者になる 読者になる

YAMAGUCHI::weblog

土足で窓から失礼いたします。今日からあなたの息子になります。 当年とって92歳、下町の発明王、エジソンです。

Linuxから自宅のエアコンを操作できるようにしたかった

Python

はじめに

たまにエアコンのリモコンがどこに行ったかわからなくなったりするので、PCからエアコン操作できたらいいな、と思って作ろうとした。結局断念したんだけどね。

使ったもの

ハードウェア

BUFFALO PCastTV2対応 PC用学習リモコンキット PC-OP-RS1

BUFFALO PCastTV2対応 PC用学習リモコンキット PC-OP-RS1


渋谷で買おうと思ったんですが、ビックカメラでは「渋谷、新宿、池袋ともに在庫なし」、ヤマダ電機では「取り扱っていない」と言われました。取り寄せしてもらうくらいならAmazonで、ということでAmazonで購入。

ソフトウェア

シリアル通信に関してはpySerialを使います。それ以外はPythonの標準ライブラリです。
あとシリアルドライバとしてftdi_sioを使います。

その他

準備

デバイスの認識

なにはともあれ、まずデバイスを認識してくれないと話になりません。接続後に確認してみました。

$ dmesg
[524593.176079] usb 4-1: new full speed USB device using uhci_hcd and address 2
[524593.435634] usb 4-1: configuration #1 chosen from 1 choice
[525002.587724] usbcore: registered new interface driver usbserial
[525002.587751] USB Serial support registered for generic
[525002.587813] usbcore: registered new interface driver usbserial_generic
[525002.587817] usbserial: USB Serial Driver core

$ lsusb
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 005 Device 002: ID 0483:2016 SGS Thomson Microelectronics Fingerprint Reader
Bus 005 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 004 Device 002: ID 0411:00b3 MelCo., Inc. PC-OP-RS1 RemoteStation
Bus 004 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 003 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub

ちゃんとUSBポート4番でPC-OP-RS1を認識しているようですね。/proc/bus/usb/devicesが存在しなかったので作成。

$ sudo mount -t usbfs none /proc/bus/usb/devices
$ less /proc/bus/usb/devices
(略)
T:  Bus=04 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#=  2 Spd=12  MxCh= 0
D:  Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs=  1
P:  Vendor=0411 ProdID=00b3 Rev= 4.00
S:  Manufacturer=BUFFALO
S:  Product=BUFFALO RemoteStation PC-OP-RS1
S:  SerialNumber=00002304
C:* #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=500mA
I:* If#= 0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none)
E:  Ad=81(I) Atr=02(Bulk) MxPS=  64 Ivl=0ms
E:  Ad=02(O) Atr=02(Bulk) MxPS=  64 Ivl=0ms
(略)
$ sudo modprobe ftdi_sio vendor=0x0411 product=0x00b3
$ sudo chmod 666 /dev/ttyUSB0
$ less /proc/bus/usb/devices
T:  Bus=04 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#=  2 Spd=12  MxCh= 0
D:  Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs=  1
P:  Vendor=0411 ProdID=00b3 Rev= 4.00
S:  Manufacturer=BUFFALO
S:  Product=BUFFALO RemoteStation PC-OP-RS1
S:  SerialNumber=00002304
C:* #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=500mA
I:* If#= 0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=ftdi_sio
E:  Ad=81(I) Atr=02(Bulk) MxPS=  64 Ivl=0ms
E:  Ad=02(O) Atr=02(Bulk) MxPS=  64 Ivl=0ms
(略)
実行環境の作成
$ sudo easy_install pyserial
Searching for pyserial
Best match: pyserial 2.3
Adding pyserial 2.3 to easy-install.pth file

Using /usr/lib/python2.6/dist-packages
Processing dependencies for pyserial
Finished processing dependencies for pyserial

あとはコード書いてみるだけです。

作ったもの

参考のリンクにある方々のソースを見てPython版にしてみました。とりあえず動けば的なテストコードです。
Hexの扱い方が面倒だなと思いました。

import serial
from struct import *
from binascii import *

import time


ser = serial.Serial('/dev/ttyUSB0', 115200, timeout=1)

ledBlink = pack('B', 0x69)
receive  = pack('B', 0x72)
send     = pack('B', 0x74)
chAY     = pack('B', 0x31)
chAB     = pack('B', 0x32)
chBY     = pack('B', 0x33)
chBB     = pack('B', 0x34)


try:
    ser.open()
    print '--> 0x69 #LED BLINK'
    ser.write(ledBlink)
    print '<-- ', unpack('<H', b2a_hex(ser.read(1)))

    print '--> 0x72 #RECEIVE'
    ser.write(receive)

    time.sleep(5)

    print '<-- ', unpack('<H', b2a_hex(ser.read(1)))
    print '<-- ', unpack('<H', b2a_hex(ser.read(1)))
    sdata = ser.read(240)
    data = b2a_hex(sdata)
    print data
    print '<-- ', unpack('<H', b2a_hex(ser.read(1)))

    time.sleep(10)
    print '5 sec left'
    time.sleep(5)

    for i in range(0, 10):
        print '--> 0x74 #SEND'
        ser.write(send)
        print '<-- ', unpack('<H', b2a_hex(ser.read(1)))
        print '--> 0x31 #CHANNEL A (Yellow)'
        ser.write(chAY)
        print '<-- ', unpack('<H', b2a_hex(ser.read(1)))
        print '--> DATA'

        #print unhexlify(data)
        ser.write(unhexlify(data))
        ser.flush()
        print '<-- ', unpack('<H', b2a_hex(ser.read(1)))
        print 'sleep 3 sec ...'
        time.sleep(3)

    ser.close()

except Exception, e:
    ser.close()
    print Exception, e

動かしてみた

エアコンの操作は断念した

上のテストは本来の目的ではなくて、エアコンの操作をしたかったわけです。しかしながら、どうもエアコンの場合何度やっても上手くいかない。改めて調べてみると次のようなエントリ発見。

うちはダイキンではないけど、やっぱりうちのエアコンのリモコンでの送受信に使ってるプロトコルがいかんのかなあ、と思ってサポートセンターに電話してみた。

自分:「あ、すいません、○○ってエアコン使ってるんですけど、リモコンで使ってるプロトコルってなんですか?」
コールセンター:「は?今なんと?(怪訝そうな声)」
自分:「あ、技術的なこと聞きたいんですけど」
コールセンター:「こちらの電話番号では技術的なことはお答えできません」
自分:「そうなんですか、すいません。じゃあどこなら聞けますか?」
コールセンター:「個人の方にはお答えできる電話窓口はございません」
自分:「じゃあ販売代理店とかでもいいんですけど」
コールセンター:「すいません、できません」
自分:「じゃあいいです。ありがとうございましたー」

というわけでわからず。まあ結論としてはわからんけど使えない、ということだな。残念。どうもエアコンとかは差分データだけじゃなくて毎回状態を送信するから送信データが240バイト以上になってしまうので無理なのだということらしい。ほえー。
もし「こうしたらいいんじゃない?」というようなアドバイスあったらお願いします。

追記

はてブにもあったんですが、「機械仕掛けにしてリモコン押せばプロトコル関係ないよ」というのは考えましたが、ダサいのでやってません。

追記2

上の追記を書いたときになんかエントリがぶっこわれてしまいました。どうもChromeで更新するとたまにこういう事が起きます。良くないですね。

追記3

実際にサポートに電話したときはもちろんあんなに単刀直入に訊いてないですよ。ただ流としてはあんな感じでした。