【冬休みオトナの自由研究】「2700円でスマートハウス化してみた」の巻

# きっかけ
年末年始だ〜。まとまった時間がとれる〜なんて思っていたら、ムクムクと制作意欲が湧いてきました。
そんな折に、家の洗面台の電球が切れ、Amazonで買おうなんて検索していたら見つけたのがこれ！！

<a href="https://amzn.to/2KEnIn3" target="_blank"><img border="0" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/138863/e45a0e63-c5b8-59dc-7c2f-fe82c384b0ef.png" ></a>
<a href="https://amzn.to/2KEnIn3">【Amazon Alexa認定 LED電球】OHLUXスマート電球</a>

え、スマートLED **2個で2700円！？**
アレクサとかにも繋がるし、ちょっと調べてみたらIFTTTとか、Tuya API(?)など利用して自動化もできそう。
**「これは安いぞ」**と思い大人の自由研究ということで、スマートホーム化で遊んでみることにしました。


# なにを作るか
最初はアレクサのアプリを作って音で操作とかも考えましたが、正直ハードウェアスイッチの利便性には及ばないし、子供に遊ばれるのが目に見えてる。。。。

なんて思っていたら
### 朝起きて、洗面台で顔洗ってる時に、色で、今日の天気がしれたら最高じゃん」

ということに気付きました。
なので今回はそれを作ります。

# どういう構成にするか
LEDの制御方法は主に3つあります。

### 1.LED操作スマホアプリ「SmartLife」での操作

SmartLifeアプリを用いることで、スマホから色とか変えられます。
また簡単な自動化レシピもつくることができます。
ただ、なぜかトリガーに設定できるのが「現在の天気」だけだったり、
色も選べなかったりと**痒いところに手が届きません。**

<img alt="SmartLife", width=400, src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/138863/45dc4147-42b9-aadb-b8c9-3ca950d5ed73.png">


### 2. IFTTT連携での操作
購入時は、このIFTTT連携を採用しようと考えていました。
LEDはSmartLifeというアプリと連動でき、SmartLifeはIFTTTと連動できるためです。
しかし、いざレシピを作ろうとしてみるとIFTTTでのONトリガーが謎のエラーにより一向に作れませんでした。

![Screen Shot 2020-12-29 at 23.11.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/138863/ee8a13b4-1ac3-1df6-b276-71185aa1e5ea.png)


ググっても、いまいちhitせず、**うまく連携できていない様でした。**
また、IFTTTは有料化して無料はかなりレスポンス悪くなってきてるとのこと。残念です。



### 3. Tuya APIでの操作
現在、中華製のスマートホームデバイスは多くがTuya APIのプロトコル採用(OEM?)しているらしく
自身の購入したものもそうでした。そのためこのTuya APIで操作することにしました。

また、Tuya APIで操作するためには、LEDと同一のwifiネットワーク上で操作元のプログラムが動作している必要があります。
そのため今回のLED操作プログラムを動かす家内サーバを用意する必要がありました。
調べたところ Node.js にて Tuya APIは動かせる様です。

ずっとノートPCをONにしとくわけには行かないと思っていたところ、
押入れの中に「RaspberryPi」が眠っていることに気付きました！！！これを使わない手はありません。

最終的に構成は、下記の様になりました。
![IMG_6645.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/138863/156cfb54-39d0-df31-9905-38ba0730ce71.jpeg)

# 動作させるまで + 実際のコード

大きく3つのパートに別れます。

### 1. Tuya API動作させるまで
ここが今回一番特徴的なところでした。
とちゅう変なバグ(後述)を踏んでしまったのですが、それ以外は先人の方をならえばいけると思います。

めちゃ丁寧に書かれてる本家setup
https://github.com/codetheweb/tuyapi/blob/master/docs/SETUP.md

参考記事
https://qiita.com/plageoj/items/dd2f1c8b55c39625d550

TuyAPI: これを使ってNode.jsからTuyaAPIを叩けます
https://github.com/codetheweb/tuyapi


大きな流れとしては
1. Tuya Iotのサイトでディベロッパー登録し、プロジェクトを作ってプロジェクトのAPI KEYとAPI SERCRETを取得
2. tuya-cli linkコマンドをつかって、LEDとリンクし、LED操作用のAPI IDとKEYを取得する

という流れです。

(自身が踏んでしまった変なバグ)
自分が踏んだのは、1でディベロッパー登録した後になぜか、認証を求められプロジェクトが作れないというバグ。
サポートに問い合わせても判然とせず、、、ググったら、同様の現状になっている人がいました。

![Screen Shot 2020-12-30 at 0.13.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/138863/10153e8a-b079-77d5-69a4-3d0450d298a4.png)


アカウント再発行で行けたよって書いてあったので別のメアドでアカウント作り直したら行けました。謎です。w

### 2. 実際のプログラム本体を書く
ここが本体なのですが、実は一番さくっとできた部分かもしれません。w
動けばOKという思想で作られておりますので、コードはツッコミだらけかもしれませんがご了解ください。
本家のサンプルコードをいじって作りました
https://github.com/codetheweb/tuyapi

Yahoo RSSから天気を取得し、それにより表示される色を変えています。
また、雪(緑) => 雨(青) => 曇り(グレー) => 晴(オレンジ)
の順番で重要なステータスと判断し、それぞれの文字が含まれるかで判定してます。
ここら辺は改善の余地ありそうです。

```javascript
// main.js

require('date-utils');
const fs = require('fs');
const client = require('cheerio-httpcli');
const TuyAPI = require('tuyapi');

// 1時間以内に、LED色変更を行っている場合は何もしないで終了する
try {
  let lastOutputDate = fs.readFileSync('lastoutputdate.txt');
  if((new Date().getTime() - new Date(lastOutputDate).getTime()) < 60 * 60 * 1000){
    console.log("Exit because the last update is too soon. lastOutputDate:" + lastOutputDate);
    return;
  }
}catch{
  console.log('lastoutputdate.txt not found!');
}

// 0:00 - 17:00以前なら今日の天気, 17:00 - 24:00なら明日の天気を取得する
var targetDay = new Date(new Date().getTime() + 7 * 60 * 60 * 1000).toFormat("DD");
var targetDayReg = new RegExp(targetDay + "日")

const device = new TuyAPI({
  id: 'XXXXXXXXXXXXXXXXXX',
  key: 'YYYYYYYYYYYYYYYYYY'});

let stateHasChanged = false;

// Find device on network
device.find().then(() => {
  // Connect to device
  device.connect();
});

// Add event listeners
device.on('connected', () => {
  console.log('Connected to device!');
});

device.on('disconnected', () => {
  console.log('Disconnected from device.');
});

device.on('error', error => {
  console.log('Error!', error);
});

device.on('data', data => {
  console.log('Data from device:', data);
  //DPS MEMO:
  //[dps 1]: switch ON/OFF(true/false)
  //[dps 2]: mode("white"/"colour")
  //[dps 5]: color code「RGB00HSV」形式 (ex. red=ff00000000ffff)

  if (!stateHasChanged) {
    // Yahoo RSSから天気取得
    let RSS = "https://rss-weather.yahoo.co.jp/rss/days/3610.xml"; //福島県のYahoo天気RSS
    client.fetch(RSS, {}, function(err, $, res){
      if(err){ console.log("error"); return; }

      $("item > title").each(function(idx){
        var title = $(this).text();
        if( title.match(targetDayReg)){
          console.log(title);
          // 雪かどうか
          device.set({dps: 2, set: 'colour'});
          if(title.match(/雪/)){
            console.log("雪です")
            device.set({dps: 5, set: '00ff00004511ff'});
          }else if (title.match(/雨/)) {
            console.log("雨です")
            device.set({dps: 5, set: '0000ff004511ff'});
          }else if (title.match(/曇/)) {
            console.log("曇りです")
            device.set({dps: 5, set: '808080004511ff'});
          }else if (title.match(/晴/)) {
            console.log("晴れです");
            device.set({dps: 5, set: 'ff4500004511ff'});
          }else {
            console.log("想定外の何か");
            device.set({dps: 5, set: '660099004511ff'});
          }
          //LED変更を記録
          stateHasChanged = true;
          fs.writeFileSync("lastoutputdate.txt", new Date().toString());
        }
      });
    });
  }
});

// Disconnect after 10 seconds
setTimeout(() => { device.disconnect(); }, 10000);

```

### 3. ラズパイでプログラムを走らせる

ここも結構苦労しました。ラズパイを触るのはほぼ初めてだったためと、
結構いろんな角度からの情報がネット上にあり、いいとこ取りするのに時間がかかったためです。w
![IMG_6642.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/138863/98ee7a2b-c15c-8a04-0866-eb1a0eea9cdf.jpeg)


やったことと参考にしたページを箇条書きします。

- OSインストール
公式から、Raspberry Pi Imager使って、micro SDにOSを入れます。
https://www.raspberrypi.org/software/

- 言語/wifi/ユーザパスワード設定(初期設定)
(参考): https://www.indoorcorgielec.com/resources/raspberry-pi/raspberry-pi-setup/
(注)バグってて、初期の変更では聞かないみたい、確かにできてなかった。

- IP固定(SSHのため)
https://mugeek.hatenablog.com/entry/2019/05/27/230256

- SSH公開鍵設置
https://tool-lab.com/raspi-key-authentication-over-ssh/

- VNC接続(まだ未設定)
https://www.indoorcorgielec.com/resources/raspberry-pi/raspberry-pi-vnc/

- nvmを利用したnodeのインストール
https://laboradian.com/how-to-use-nvm/

- RaspberryPi3で初めてcrontabを使う前に
https://qiita.com/Higemal/items/5a579b2701ef7c473062

- cronを秒単位で指定する方法
https://tech.mktime.com/entry/376

ちなみに上記を参考にcrontabは下記の様に設定しました。
15秒おきにスクリプト走らせています。

```shell
# crontab
LANG=ja_JP.UTF-8
* * * * * for i in `seq 0 15 59`;do (sleep ${i}; /home/pi/.nvm/versions/node/v14.15.3/bin/node /home/pi/dev/weather-led/main.js >> /home/pi/dev/weather-led/log.txt; ) & done;
```


# 動作の様子
0:00 - 17:00以前なら今日の天気, 17:00 - 24:00なら明日の天気を取得するようにしてあります。
そして写真の様に
雪(緑)
雨(青)
曇り(グレー)
晴(オレンジ)で光ります。
洗面台は普通にハードウェアスイッチでのON/OFFです。
明日は雪予報なので、今日夜は緑に光ってました！
![CE863D58-9F36-4152-86F4-5E852CDB6368.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/138863/616ca915-83a8-926f-ca1d-5a8926fef423.jpeg)


# 実際に便利なの?
これは正直まだ未知数です。

- ラズパイ+LEDが安定動作するのか。
- 家族が受け入れてくれるか
- 自分が実際便利になるか(天気アプリを開く回数が減るのか)

こちらは一ヶ月ぐらい使ってみてわかってくると思います。
何か特筆事項がでてきたら追記します。

また、

- ラズパイの死活監視
- 実運用でのチューニング
- 現在はLED ONかどうかを頻繁に問い合わせ(ポーリング)形式になっている => LEDがONになった時点でイベント発火する形式にできれば良いのだが、、、

などはFuture Workですね。

# まとめ
感想ですが、

- スマートLEDはすごい安くてびっくりした。Tuya API利用までは少し遠回りしたけど、わかってしまえばシンプルで使いやすいAPI
- 家に眠っていた「ラズパイ(Model3 B)」が大活躍した。数千円というこの値段で小型PCが買えてしまうというのが凄すぎる。**まだまだ潜在能力を秘めていそう！**
- 冬休み自由研究やることで、今まで触ったことのない**新しい技術に触れられたのは良かった**！！
- 先人の皆様のお力をお借りしまくり ました。ありがとうございました！！！

### 是非良かったら皆さまも、冬休みの自由研究してみてください〜！！！
あと、**お年玉としてLGTM**いただけたら嬉しいです。m(_ _)m 良いお年を〜〜。