部屋の温度をモニタリング

#部屋の温度をモニタリング(自分用メモ)
##ステータス
最近Pythonに興味あり
raspberry piで以前に家電を赤外線で遠隔コントロールした経験あり。
unittestを使ってみたかった（初心者）

##目標
部屋の温度を聞いたら答えてくれるbotを作りたい。
部屋の温度を知ってレコメンドを作りたい
28度以上になったら🍧食べましょうとか、
18度以下になったらオススメは🍢です、みたいな感じ。

##今回実現したこと  
温度センサーをArduinoから読み取る。
読み込んだ値をslackにあげる。
slackで”今何度”と聞くと温度を答えてくれる。

##温度センサーをArduinoから読み取る。
使ったセンサーは
LM35DZ
友人に借りました。

配線には
analog0と5VとGNDを使った！
<img width="375" alt="18-08-03 22-06-25 1702.png" src="https://qiita-image-store.s3.amazonaws.com/0/110060/ff41c340-4667-c941-6688-68b959b75256.png">


ひとまずコードを

```c 

// 温度センサーの値をシリアル通信で送信するプログラム.
// to Arduino UNO code by odenn.

int sensPin = A0; 
int sensVal = 0;
byte s_val;

void setup() {
  // serial setting 19200 bps
  Serial.begin(19200);
}

void loop() {
  float temp;
  if(Serial.available() > 0){
    s_val = Serial.read();
    
    if(s_val == 'T'){                         //T is Get Temp
      sensVal = analogRead(sensPin);          //get num from Analog0Pin
      temp    = modTemp(sensVal);             //convert from sensVal
      Serial.println(temp);                   //send result 
      
    }
  }
}

//Temp - convert from Analog0Pin
float modTemp(int analog_val){
float volt  = 5;                                   //base volt
float tempS = ((volt * analog_val) / 1024) * 100;  //convert //analoginput max 1023 so use 1024 //100(摂氏)
return tempS;                                      //temp ok

}

```

ひとまずセンサ値を読めました。
それから仕様に沿って摂氏に変換した。
http://akizukidenshi.com/catalog/g/gI-00116/

摂氏変換式

```:式
((5volt * analog0からの数値) / 1024) * 100;  
```

#温度の値をslackへ飛ばすために。
Arduinoを直接ネットへ繋げればいいのだがraspberrypiが転がっていたのでシリアル通信することにしました。
あとslackへの投稿にIncoming Webhooksを利用。
参考サイト
https://qiita.com/ik-fib/items/b4a502d173a22b3947a0
使用したのはraspberry pi 3 B
B+欲しい...
ライブラリのインストール

```bash

#initial
sudo apt update

#editor
sudo apt -y install vim

#git
sudo apt -y install git
git config --global user.email ""
git config --global user.name ""

#pyserial
sudo apt -y install python3-pip
sudo pip3 install pyserial
sudo pip3 install slackbot

#slack
sudo pip3 install requests
sudo pip3 install slackweb

#com
echo please check tty
dmesg | grep tty

```

ひとまずコードを

```python
# code by odenn.
# -*- coding: utf-8 -*-
import serial
import time
import slackweb
from datetime import datetime 

class TempAlert():

    slackUrl="yourURL"

    def arduino_serial(Serialt):
        #open serial
        Serialt = serial.Serial('/dev/ttyACM0', 19200)
        time.sleep(3.5)
        return Serialt

    def slack_initial(self,slack):
        result = slack.notify(text="NOW TEMPERATURE:")
        return result

    def get_process(self,St):
        #Arduino is need value stabilization so 3 times loop.
        for num in range(3):
            #Get Temp from Arduino UNO Command : T
            St.write(b'T')
            time.sleep(1)
            Temp=St.readline()
        return float(Temp)
        #close serial
        St.close()
            
    def slack_throw(self,slack,sTemp):
        return slack.notify(text="=>%s C(%s)"% (sTemp,datetime.now().strftime("%Y/%m/%d %H:%M:%S")))

if __name__ == "__main__":

    t = TempAlert()

    #slackweb setting.
    slackinfo = slackweb.Slack(TempAlert.slackUrl)

    #slack init
    t.slack_initial(slackinfo)

    #serial open
    Stinfo = t.arduino_serial()

    #get temp
    gettmp = t.get_process(Stinfo)

    #slack throw
    t.slack_throw(slackinfo,gettmp)
```
ちなみにunittestを初めて使ってみた（あんましスマートではない気がする。）

```python
# code by odenn.
# -*- coding: utf-8 -*-
import unittest
import slackweb
from datetime import datetime
from cron_temp_alert import TempAlert

class TestStringMethods(unittest.TestCase):

    slackUrl="yourURL"
    
    def test_arduino_serial(self):
        #serial val is need True.
        t = TempAlert()
        self.assertTrue(t.arduino_serial())

    def test_slack_initial(self):
        t = TempAlert()
        slackinfo = slackweb.Slack(TestStringMethods.slackUrl)
        self.assertTrue(t.slack_initial(slackinfo))

    def test_get_process(self):
        t = TempAlert()
        '''
        serial val is need True.
        temp 0 ~ 40 test.
        temp val is need float.
        '''
        self.assertTrue(t.arduino_serial())
        Stinfo = t.arduino_serial()
        self.assertIsInstance(t.get_process(Stinfo), float)
        self.assertLess(0,t.get_process(Stinfo))
        self.assertGreaterEqual(40,t.get_process(Stinfo))

    def test_slack_throw(self):
        #serial val is need True.
        t = TempAlert()
        self.assertTrue(t.arduino_serial())
        Stinfo    = t.arduino_serial()
        slackinfo = slackweb.Slack(TestStringMethods.slackUrl)
        gettemp   = t.get_process(Stinfo)
        self.assertTrue(t.slack_throw(slackinfo,gettemp))

if __name__ == "__main__":
    unittest.main()

```
ちなみにbotsは
https://qiita.com/sukesuke/items/1ac92251def87357fdf6
がすごくわかりやすい解説でした、ありがとうございます。
この解説をみて一通り設定後
自分のpluginsを書いてみた。
ひとまずコードを

```python
# -*- coding: utf-8 -*-
from slackbot.bot import respond_to     
from slackbot.bot import listen_to  
from slackbot.bot import default_reply
import serial
import time
from datetime import datetime 

class TempGet():

    def arduino_serial(Serialt):
        #open serial
        Serialt = serial.Serial('/dev/ttyACM0', 19200)
        time.sleep(3.5)
        return Serialt

    def get_process(self,St):
        #Arduino is need value stabilization so 3 times loop.
        for num in range(3):
            #Get Temp from Arduino UNO Command : T
            St.write(b'T')
            time.sleep(1)
            Temp=St.readline()
        return float(Temp)
        #close serial
        St.close()

@respond_to('今何度')
def mention_func(message):
    t = TempGet()
    Stinfo = t.arduino_serial()
    StTemp = t.get_process(Stinfo)
    message.reply("%s度だよ！(%s)"% (StTemp,datetime.now().strftime("%Y/%m/%d %H:%M:%S")))

@listen_to('今何度')
def listen_func(message):
    message.send('温度を測るよ！')
    t = TempGet()
    Stinfo = t.arduino_serial()
    StTemp = t.get_process(Stinfo)
    message.reply("%s度だよ(%s)"% (StTemp,datetime.now().strftime("%Y/%m/%d %H:%M:%S")))
    message.reply('どうぞ！')
```

ちなみにテストコードも（これでいいんだろうか、、、だれか教えて欲しい、、、）

```python

# code by odenn.
# -*- coding: utf-8 -*-
# このテストはpython3 で実行するように！
import unittest
from datetime import datetime
from temp_mention import *
from slackbot.bot import *     

class TestStringMethods(unittest.TestCase):

    def test_arduino_serial(self):
        #serial val is need True.
        t = TempGet()
        self.assertTrue(t.arduino_serial())

    def test_get_process(self):
        t = TempGet()
        '''
        serial val is need True.
        temp 0 ~ 40 test.
        temp val is need float.
        '''
        self.assertTrue(t.arduino_serial())
        Stinfo = t.arduino_serial()
        self.assertIsInstance(t.get_process(Stinfo), float)
        self.assertLess(0,t.get_process(Stinfo))
        self.assertGreaterEqual(40,t.get_process(Stinfo))

    def test_mention_func(self):
        t = TempGet()
        Stinfo = t.arduino_serial()
        StTemp = t.get_process(Stinfo)
        self.assertTrue(t.arduino_serial())
        self.assertTrue(t.get_process(Stinfo))

    def test_listen_to(self):
        t = TempGet()
        Stinfo = t.arduino_serial()
        StTemp = t.get_process(Stinfo)


if __name__ == "__main__":
    unittest.main()

```
ちなみに
こんな感じになりました
![スクリーンショット 2018-07-28 23.49.29.png](https://qiita-image-store.s3.amazonaws.com/0/110060/d6de9ce8-4e41-78c0-e08f-18ed63f3f6f7.png)

ちなみに温度が灼熱になっているのはセンサが今手元になかった（借り物なので...）のでセンサなしでの実行結果です...
早く買わねば笑
次はレコメンドをつくる！

ひとまずレコメンドを形にしたいのですごく単純な仕組みで作ってみる。
この温度センサーは0から100度を測定できるので温度によって条件分岐して表示する食品を変えようと思う。
ちなみに参考にしたサイトは
http://www.foodwatch.jp/strategy/tabledesk/35742

ひとまずコードを

```python
# -*- coding: utf-8 -*-
from slackbot.bot import respond_to     
from slackbot.bot import listen_to  
from slackbot.bot import default_reply
import serial
import time
from datetime import datetime 

class TempGet():

    def arduino_serial(Serialt):
        #open serial
        Serialt = serial.Serial('/dev/ttyACM0', 19200)
        time.sleep(3.5)
        return Serialt

    def get_process(self,St):
        #Arduino is need value stabilization so 3 times loop.
        for num in range(3):
            #Get Temp from Arduino UNO Command : T
            St.write(b'T')
            time.sleep(1)
            Temp=St.readline()
            StTemp=float(Temp)
            return StTemp
        #close serial
        St.close()

    def recommend_get_process(self,Temp):
        StTemp = Temp
        if   StTemp >= 0 and StTemp <= 15:
            return "コーヒー,鍋料理,日本酒"
        elif StTemp > 15 and StTemp <= 16:
            return "シチュー"
        elif StTemp > 16 and StTemp <= 18:
            return "おでん"
        elif StTemp >= 20 and StTemp < 22:
            return "アイスコーヒー"
        elif StTemp >= 22 and StTemp < 23:
            return "ざるそば"
        elif StTemp >= 23 and StTemp < 24:
            return "メロン,牛乳"
        elif StTemp >= 24 and StTemp < 25:
            return "ぶどう,スイカ"
        elif StTemp >= 25 and StTemp < 32:
            return "アイスクリーム,ビール"
        elif StTemp >= 200:
            return "地獄へ来てしまった..."
        elif StTemp >= 100:
            return "ドライアイス"
        elif StTemp >= 60:
            return "死にそうな暑さだね、、、"
        elif StTemp >= 32:
            return "かき氷"
        else:
            return "好きなもの食べて笑"

@respond_to('おすすめ')
def mention_func(message):
    t = TempGet()
    Stinfo = t.arduino_serial()
    StTemp = t.get_process(Stinfo)
    ResultContent = t.recommend_get_process(StTemp)
    message.reply("%sだよ！(%s度,%s)"% (ResultContent,StTemp,datetime.now().strftime("%Y/%m/%d %H:%M:%S")))

@listen_to('おすすめ')
def listen_func(message):
    message.send('おすすめの食べ物は！')
    t = TempGet()
    Stinfo = t.arduino_serial()
    StTemp = t.get_process(Stinfo)
    ResultContent = t.recommend_get_process(StTemp)
    message.reply("%sだよ(%s度,%s)"% (ResultContent,StTemp,datetime.now().strftime("%Y/%m/%d %H:%M:%S")))
    message.reply('どうぞ！')
```

見てもらったらわかるように何の工夫もないコードですね笑
これにニューラルネットワークやら組み合わせてユーザの好みを学習できるようなもっと高度なレコメンドを作りたいなあ笑
時間があればトライします。

ちなみに実行結果
![スクリーンショット 2018-08-03 21.50.28.png](https://qiita-image-store.s3.amazonaws.com/0/110060/7f0ac609-d82a-5b13-0763-8e37103f355b.png)

まだセンサー届かない、、、笑
レコメンドのテストもあとでちゃんと書かねば（未完成）

```python
# 2018 ICTRoom Temp monitor.
# code by kazuya.
# -*- coding: utf-8 -*-
import unittest
from datetime import datetime
from foods_mention import *
from slackbot.bot import *     

class TestStringMethods(unittest.TestCase):

    def test_arduino_serial(self):
        #serial val is need True.
        t = TempGet()
        self.assertTrue(t.arduino_serial())

    def test_get_process(self):
        t = TempGet()
        self.assertTrue(t.arduino_serial())
        Stinfo = t.arduino_serial()
        self.assertIsInstance(t.recommend_get_process(Stinfo), str)

    def test_mention_func(self):
        t = TempGet()
        Stinfo = t.arduino_serial()
        StTemp = t.recommend_get_process(Stinfo)
        self.assertTrue(t.arduino_serial())

    def test_listen_to(self):
        t = TempGet()
        Stinfo = t.arduino_serial()
        StTemp = t.recommend_get_process(Stinfo)


if __name__ == "__main__":
    unittest.main()

```

ついでに天気予報を答えてくれるbotも追加しました。
jsonをパースしてるだけ笑
http://weather.livedoor.com/weather_hacks/webservice
ひとまずコードを

```python
# -*- coding: utf-8 -*-
from slackbot.bot import respond_to     
from slackbot.bot import listen_to  
from slackbot.bot import default_reply
import time
from datetime import datetime 
import requests
import json

@respond_to('天気')
def mention_func(message):
    message.send('大阪の天気を答えるよ!')
    message.send('(LWWSのデータより)')
    getdate = "情報取得日(%s)"% (datetime.now().strftime("%Y/%m/%d %H:%M:%S"))
    message.reply(getdate)
    url = 'http://weather.livedoor.com/forecast/webservice/json/v1'
    payload = {'city': '270000'} # Osaka
    data = requests.get(url, params = payload).json()
    message.reply(data['title'])

    for weather in data['forecasts']:
        slackweather = weather['dateLabel'] + '(' + weather['date'] + '):' + weather['telop']
        message.reply(slackweather)
        maxtemp = weather['temperature']['max']
        mintemp = weather['temperature']['min']
        if maxtemp is not None and mintemp is not None:
            maxcel = maxtemp['celsius']
            mincel = mintemp['celsius']
            message.send("予想最高気温:%s 予想最低気温:%s" % (str(maxcel),str(mincel)))
        else:
            message.send("気温データ無し")
    
    message.reply('どうぞ！')

@listen_to('天気')
def listen_func(message):
    message.send('大阪の天気を答えるよ!')
    message.send('(LWWSのデータより)')
    getdate = "情報取得日(%s)"% (datetime.now().strftime("%Y/%m/%d %H:%M:%S"))
    message.reply(getdate)
    url = 'http://weather.livedoor.com/forecast/webservice/json/v1'
    payload = {'city': '270000'} # Osaka
    data = requests.get(url, params = payload).json()
    message.reply(data['title'])

    for weather in data['forecasts']:
        slackweather = weather['dateLabel'] + '(' + weather['date'] + '):' + weather['telop']
        message.reply(slackweather)
        maxtemp = weather['temperature']['max']
        mintemp = weather['temperature']['min']
        if maxtemp is not None and mintemp is not None:
            maxcel = maxtemp['celsius']
            mincel = mintemp['celsius']
            message.send("予想最高気温:%s 予想最低気温:%s" % (str(maxcel),str(mincel)))
        else:
            message.send("気温データ無し")
    
    message.reply('どうぞ！')

```

実行結果
![スクリーンショット 2018-08-03 22.03.09.png](https://qiita-image-store.s3.amazonaws.com/0/110060/0f4f55af-a122-69b2-8758-4352fc8f23ed.png)

ちなみに現状こんな見た目
結構コンパクトです。
![18-07-30 15-17-28 1687.jpg](https://qiita-image-store.s3.amazonaws.com/0/110060/d8cbe5e5-91fb-2270-678b-bcfe00828564.jpeg)



さて今日はここまで



参考文献

第7回 Arduinoで作る簡易百葉箱(その1)。
https://deviceplus.jp/hobby/entry_007/

PythonのslackbotライブラリでSlackボットを作る
https://qiita.com/sukesuke/items/1ac92251def87357fdf6

温度による食品の関係
http://www.foodwatch.jp/strategy/tabledesk/35742

SlackのIncoming Webhooksを使い倒す
https://qiita.com/ik-fib/items/b4a502d173a22b3947a0