独身貴族のスマートホーム -スマートリモコンの課題解決-

# はじめに
この記事は、弊社[リンク情報システム](https://www.lis.co.jp/)が主催する「[アドベントカレンダー](https://qiita.com/advent-calendar/2018/lis)」のリレー記事です。
[アドベントカレンダー](https://qiita.com/advent-calendar/2018/lis)は engineer.hanzomon のグループメンバによってリレーされます。
7日目である本記事は私、阿部が書きました。よろしくどうぞ。

## シリーズ方針
「[独身貴族のスマートホーム -Alexaからローカルの音楽を再生する-](https://qiita.com/r-abe/items/34bd22766075441cb57f)」に引き続き、「独身貴族のスマートホーム」シリーズ第2弾です。（シリーズ化することにしたんだ）
本シリーズの方針は、**スマートホームを世に広めること**です。だって、間違いなく"一家に一台"の時代が来るじゃないですか。でも遅いんですよ。スマホの時もそう。スピード感って重要じゃない？流行ってくれないと技術が進歩しないし経済も良くならん。だから買おう！

## 背景
「スマートスピーカー（Alexa）からスマートリモコン（eRemote mini）を経由して家電の簡単な操作をする」はデフォルトの機能で出来ます。（オフィシャルサイトに載っている手順だけでめっちゃ簡単に出来ますよ）
しかし、私はもっと柔軟に家電を操作したい！

スマートリモコンというより赤外線リモコンの弱みになりますが、リモコン-家電間の信号のやり取りって一方通行なんですよね。家電からリモコンに対して応答がないんですよ。例を出します。
　１．エアコンに向かってリモコンで"暖房"を押す
　２．リモコンの表示が"暖房"になる
　３．エアコン「ピッ」温風が出る
　４．赤外線の届かなそうな場所（または角度）でリモコンで"冷房"を押す
　５．リモコンの表示が"冷房"になる
　６．エアコン「 … 」温風が出たまま
こんな経験ありません？
この４～６の現象は、リモコンからエアコンに対し出した命令に、**エアコンがそれに気づいてなくても**リモコンは冷房にしたつもりになってるんですね。勝手なやつだ。

で、まぁそれは良いんですよ。
しかし、スマートスピーカー経由で家電を操作すると少し気持ち悪いことになるんです。例を出します。
　１．テレビが付いていることを確認する
　２．あべ「アレクサ、テレビ付けて」
　３．アレクサ『はい』
　４．テレビ「ピッ」　なんと、テレビが消える！！
テレビのリモコンって、オンとオフのボタン分かれてないじゃないですか。
オンもオフも同じボタンなんですよね。
アレクサさんもリモコンも、テレビが付いているのか知りもしないでオン／オフ信号を送ってるんですね。勝手なやつだ。

なので、このやり取りのどこかで家電の状態を持っておきたい。⇒じゃあ仕組みを作るか！そんな経緯です。

## 使う環境
まず今回使う環境とかツールを紹介です。

| なにやつ | 名前とか | その他 |
|:-----------|:------------|:------------|
| スマートスピーカー | Amazon Echo Spot | Alexaちゃん |
| 仲介やさん① | Node-red v0.19.4 | Node.js v8.11.4 |
| サーバー | Raspberry Pi 2 Model B+ | Volumio(RaspbianベースのLinux) |
| 仲介やさん② | **今回作るやつ** | Python2.7 |
| ライブラリ | BlackBeanControl | - |
| データベース | MySQL(サーバー) | HeidiSQL(クライアント) |
| スマートリモコン | eRemote mini | - |

MySQLもPythonをちゃんと書くの初めて。

## やること
今回やること
![予想フロー図2.png](https://qiita-image-store.s3.amazonaws.com/0/284458/04414074-6083-262e-3d0f-7ae4a6d0189b.png)

　１．**Alexa**から**node-red**経由でサーバーの**<今回作るやつ>**にエアコンをオンにするよう命令
　２．**<今回作るやつ>**は**MySQL**からエアコンの状態（オン／オフ）を取得
　３．エアコンの状態により処理を分岐
　　⇒オンの場合：これ以降何もしない
　　⇒オフの場合：赤外線送信コマンドをスマートスピーカーに命令
　４．スマートスピーカーは赤外線でエアコンをオンにする
　５．**<今回作るやつ>**はエアコンの状態（オン）を**MySQL**に格納

これの**<今回作るやつ>**を作ります！

# 本番
ここまで前置きでした。長かったねぇ

## 環境構築
今の環境に足りないものをインストールしたりなんだり
### MySQL Serverのインストール
家電情報を格納しておくデータベース

```bash
$ sudo apt-get install mysql-server
```

### MySQLのGUIクライアントツールのインストール
Windows（筆者がメインで使っているPC）からMySQLにアクセスしてどーのこーのできるGUIのツール
![heidisql.PNG](https://qiita-image-store.s3.amazonaws.com/0/284458/c154629f-d42e-b2df-bc28-b84e2bf5ad7d.png)
ついでにテーブルとレコードも作っちゃった。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/284458/27f5af27-78a7-9cee-a178-f9a1c7910642.png)
今はリビングの照明とエアコンだけ入れてる状態ですね。

### MySQL-Connector-Pythonのインストール
PythonからMySQLにアクセスしてどーのこーのできるパッケージ

```bash
$ sudo pip install mysql-connector-python
```

### dein-vimのインストール
vim（筆者が使うエディタ）にPython補完機能を付けるためのプラグイン
インストール手順は長いので省略。
　→実はvimのバージョンが古いせいでインストール失敗したが、コードに色付けはできたのでまぁ良しとする

### BlackBeanControl関連のインストール
eRemoteをコマンドラインで扱うPythonのパッケージ

```bash
# 外部ライブラリ
$ sudo apt-get install python2.7-dev
$ sudo python2 -m pip install configparser
$ sudo python2 -m pip install netaddr
$ sudo python2 -m pip install pycrypto

# python-broadlink
$ git clone https://github.com/mjg59/python-broadlink.git
$ cd python-broadlink $ sudo python2 setup.py install

# BlackBeanControl
$ git clone https://github.com/davorf/BlackBeanControl.git
```

設定ファイルにeRemoteのIPアドレスとMACアドレスを設定する

### 赤外線登録すっるよー
自作プログラムから赤外線を送信するため、とりあえずエアコンのオンオフ操作を登録

```bash
$ cd BlackBeanControl
$ python2 BlackBeanControl.py -c ac-power-on
# ここでエアコンの電源ボタン押下
$ python2 BlackBeanControl.py -c ac-power-off
# ここでエアコンの電源ボタン押下
```

## つくる
### プログラムかっくよー
出来る限り**PEP-8**ってやつに準拠してます。慣れないな～

```python:main.py
#!/usr/bin/env python
# coding: utf-8

import sys
import remocon

Args = sys.argv

# main Method
def main():
    # Check Args
    if len(Args) == 5:
        # Create Instance
        remoteController = remocon.RemoteController(Args[1], Args[2], Args[3], Args[4])
        remoteController.rmt_ctl()
        # Delete Instance
        del remoteController
    else:
        return

# Enable Main Method
if __name__ == "__main__":
    main()
```


```python:remocon.py
#!/usr/bin/env python
# coding: utf-8

import sys
import ConfigParser
import mysqlutil
import subprocess

# Remote Controller Class
class RemoteController:
    # Constructor
    def __init__(self):
        # Initialize
        self.room = room
        self.appliance = appliance
        self.action = action
        self.content = content
        # Read Config
        conf_path  = "./config/remocon.conf"
        self.read_config(conf_path)
    
    # Destructor
    def __del__(self):
        # Create Instance 
        del self.mySQLUtil

    # Remote Control Method
    def rmt_ctl(self):
        # Create Instance 
        self.mySQLUtil = mysqlutil.MySQLUtil(self.user, self.pswd, self.host, self.db)
        # Check Velidity
        aid = self.check_velidity()
        # Create Command
        if self.action == "power":
            command = self.create_powercmd(aid)
        elif self.action == "status":
            command = self.create_statuscmd(aid)
        elif self.action == "volume":
            command = self.create_volumecmd(aid)
        else:
            return
        # Infrared transmission Command
        res = subprocess.call(command, shell=True)
        # Create SQL String
        sql = "UPDATE appliances_info SET {0}='{1}' WHERE id='{2}'"
        sql = sql.format(self.action, str(self.content), aid)
        # Write appliance Data
        result = self.mySQLUtil.write_data(sql)
        return

    # Check Velidity Command Option
    def check_velidity(self):
        # Create SQL String
        sql = "SELECT id FROM appliances_info WHERE room='{0}' AND name='{1}'"
        sql = sql.format(self.room, self.appliance)
        # Get appliance id
        aid = self.mySQLUtil.read_data(sql)
        if aid is None:
        return aid

    # Create Power Command
    def create_powercmd(self, aid):
        # Create SQL String
        sql = "SELECT {0} FROM appliances_info WHERE id='{1}'"
        sql = sql.format(self.action, aid)
        # Get appliance Data
        data = self.mySQLUtil.read_data(sql)
        # Command
        command = None
        # Check Input and SQLData
        if data == 0 and self.content == '1':
            # Create Command
            command = "python2 " + self.bean + " -c Ac-Power-on"
        elif data == 1 and self.content == '0':
            # Create Command
            command = "python2 " + self.bean + " -c Ac-Power-off"
        return command

    # Create Status Command ※未実装
    def create_statuscmd(self, aid):
        return

    # Create Volume Command ※未実装
    def create_volumecmd(self, aid):
        return

    # Read Config Method
    def read_config(self, conf_path):
        method = sys._getframe().f_code.co_name
        # Read Config
        config = ConfigParser.SafeConfigParser()
        config.read(conf_path)
        # Get Config
        self.user = config.get('DataBase', 'user_name')
        self.pswd = config.get('DataBase', 'password')
        self.host = config.get('DataBase', 'host_name')
        self.db = config.get('DataBase', 'database')
        self.bean = config.get('eRemote', 'black_been')
```


```python:mysqlutl.py
#!/usr/bin/env python
# coding: utf-8

import sys
import mysql.connector

# MySQL Utility Class
class MySQLUtil:
    # Constructor
    def __init__(self, username, password, hostname, database):
        # Connecting MySQL
        connect_conf = {
                'user': username,
                'password': password,
                'host': hostname,
                'database' : database,
        }
        self.conn = mysql.connector.connect(**connect_conf)
    
    # Destructor
    def __del__(self):
        # Close Connection
        self.conn.close()

    # Read MySQL Data
    def read_data(self, sql):
        # MySQL:SELECT
        cursor = self.conn.cursor()
        cursor.execute(sql)
        # Fetch SELECT result
        result = cursor.fetchall()
        if len(result) == 1 == len(result[0]):
            ret = result[0][0]
        else:
            ret = None
        # Close Cursor
        cursor.close()
        return ret

    # Write MySQL Data
    def write_data(self, sql):
        # MySQL:UPDATE
        cursor = self.conn.cursor()
        try:
            cursor.execute(sql)
            # MySQL:Commit
            self.conn.commit()
        except:
            # MySQL:RollBack
            self.conn.rollback()
        # Close Cursor
        cursor.close()
        return
```

```python:remocon.conf
# ----- eRemote Info -----
[eRemote]
# eRemote Command Path
black_been = /home/volumio/BlackBeanControl/BlackBeanControl.py

# ------- Log Info -------
[Log]
file_path = ./log/remocon.log
log_level = TRACE

# ----- DataBase Info -----
[DataBase]
# Host Name
host_name = localhost
# User Name
user_name = XXXXXXXX
# Password
password = XXXXXXXX
# DataBase
database = remote_control
# Charset Code
charset = 'utf8'
```

### Node-Redのフローかっくよー
[前回](https://qiita.com/r-abe/items/34bd22766075441cb57f)も使用したNode-Redで、Alexaからの指示を解釈し、**<今回作ったやつ>**を実行するようにする。これも手順は省略。
デバイス登録
![image.png](https://qiita-image-store.s3.amazonaws.com/0/284458/34040128-9715-fe1b-9199-20c5af5e5e4e.png)
フロー作成
![image.png](https://qiita-image-store.s3.amazonaws.com/0/284458/ca45d28f-622a-d650-0ddb-a6c1b1de8a9f.png)

## 動作確認
できた～。このためだけにTwitterアカウント作った
※音出るので注意
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">動作確認用の動画 <a href="https://t.co/HPQLfE3aYA">pic.twitter.com/HPQLfE3aYA</a></p>&mdash; r-abe (@r_abe_2014) <a href="https://twitter.com/r_abe_2014/status/1069958687966486529?ref_src=twsrc%5Etfw">2018年12月4日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
アレクサちゃんかわええのう
でも、なんでもかんでも「はい」というのは良くないぞ。
（スマートスピーカーがエアコン付けたかどうかをスマートスピーカーは認知していない）

## さいごに
今回は夢が広がった感があります。楽しかったなー
次は何をしよ～っていう構想が既にいくつか出てきてます。
・Alexaスキルを作って音声入力を柔軟にする
・GUIアプリ作って家のリアルタイムな家電状況をテレビに表示する
・サブマシン（Linux）にkodiを入れてブルーレイ＆メディアプレイヤーにする
などなど

本日12/7は[サイバーマンデー](https://www.amazon.co.jp/%E3%82%B5%E3%82%A4%E3%83%90%E3%83%BC%E3%83%9E%E3%83%B3%E3%83%87%E3%83%BC/b?ie=UTF8&node=3959621051)（Amazonのセール）です。なんと私が使ってる**Amazon Echo Spot**も出るらしい…！この記事を読んだあなたも、欲しくなってきたんじゃないんですか？よし、買おう！

次の記事は@k-tsukamotoによる「現在志向バイアスとキャッシュレス社会」です。ウチの新人が書いた記事ですが、とても面白い！読んでみてください～

## 参考
### BlackBeanControl
・[Raspberry PiからコマンドラインでRM mini3（スマートリモコン）を制御する](https://qiita.com/canadie/items/7de3092df9b5c549536c)
・[スマートリモコン eRemote mini を Raspberry Pi から使う](https://vogel.at.webry.info/201805/article_9.html)
### MySQL-Connector
・[MySQL Connector/PythonからSQLを投げる](https://dev.classmethod.jp/server-side/python/query-with-mysql-connector-python/)
### dein-vim
[VimにPythonコードの補完機能を追加して簡単に編集できるようにする](https://qiita.com/wakaba130/items/4fc4c08ce76c78f36da3)