make the smart speaker smarter 15

04/20

Index

Analyze Some Sensors’ Values

さて、前々からの積み残し

  1. When I Want(時間で)
  2. If She Felt Temp/Humidity(温湿度センサから)
  3. When She Saw(カメラから)
  4. If She Felt Someone(人感センサから)

ここでは4番をトリガにいろいろ
作業させてみたって話を。

Who’s Coming?

先日購入した人感センサで収集データを基に
あらゆる判定にこの情報を使っていこうという
ネタ。

先日の加湿器のネタも仮に自動化したとしても
人のいないときに動くのは電気の無駄使いなので
私が部屋にいるときだけ動いてほしい。
そんな時にもこのセンサ情報が役に立つ。

また、暗くなったら電気を自動で付けるのも
人のいないときにやっても無人の部屋が
照らされるだけで無駄。

寒くなってきたり、熱くなってきたら
無人の部屋に「寒くありませんか?」とか
「暑いですね?」などと響くのも
一種の心霊現象と言ってもいい。

で、人を検知したとしてもまた別の問題があり
例えば、私が考え事をしているときや
DVDで映画を見ているときなどは殆ど動かないので
人がいないと判断して電気を消してしまう
可能性がある。
よくホテルとかビルのトイレで用を足している
ときに、便座に座って動かないでいると勝手に
電気が消えてしまう時がある。まさにアレ。

もう一つ問題が。
例えば、湿度がこれ以下なら加湿器を付けるって
した場合、20秒間隔でチェックして
下回ってたらまた加湿器をつけて、、を繰り返す。
要は加湿器のステータスをチェックして
もう電源が入っていたら何もしないって評価を
しないとならん。でも、閾値近辺の場合は
さらに厄介で20秒間隔で付けたり消したりを
繰り返す可能性がある。なので、加湿器の場合は
電源を入れる閾値と電源を消す閾値を分ける
必要がある。

これらを完璧に解決する方法を見つけた。

Check In & Check Out

前回も上記問題の解決策を載せてみたのだが
その具体的実装をやっていくこととする。

先ずは、前回から時間も経っていることだし
今の仕組みを明るさのチェックを例にとって紹介。
私自身UMLみたいなフロー図をあまり理解して
いないので使っている図形は間違っていると
思うがご容赦いただきたい。

graph TD
    title[How to manage a room lamp]
    subgraph Explanatory Notes
        notes1[A]==>|External Relation|notes2[B]
        notes3[A]-->|Internal Relation|notes4[B]
        notes5[A]-.->|DB Relation|notes6[B]
        notes7[Module]
        notes8((Devices))
        notes9(Database)
        notes10>API]
        notes11{Conditions}   
    end
  subgraph Database
    db(database)
    db2>API]
  end
  subgraph Sensors/Home Devices
    mot((Motion Sensor))
    lux((Brightness Sensor))
    lamp1((Room Lamp))
    felica((Pasori))
    sensor0[Sensor Daemon]
    sensor3[Felica Daemon]
    sensor1>IoT API]-->|get sense|mot
    sensor2>IoT API]-->|get sense|lux
    sensor4>IoT API]-->|get sense|felica
    sensor0-->sensor1
    sensor0-->sensor2
    sensor3-->sensor4    
    sensor0-.->db2
    sensor3-.->db2
  end
  subgraph Main Daemon
    pi1[Rpi+2s]
    command1[KuroRS Daemon]==>lamp1
  end
  subgraph Judge Sequence
    judge1>Judge API]-.->db2
    judge2>Judge API]-.->db2
    judge3>Judge API]-.->db2
    judge4>Judge API]
    db2-->|select latest motion data|db
    db2-->|select latest lux data|db
    db2-->|select latest felica data|db
    db2-->|accumlation|db
    subgraph Conditions
      cond1{Is my Lord in my room?}
      cond2{Did something move?}
      cond3{Is it bright in my room?}
      cond4{Is it night now?}
    end
    cond1==>|yes|pi1
    cond2-->|yes|judge3
    cond3-->|yes|judge4
    cond4==>|yes|command1
  end
  cond1-->|no|judge2
  cond2==>|no|pi1
  cond3==>|no|pi1
  cond4==>|no|pi1
  pi1==>|start|judge1
  judge1-->|1|cond1
  judge2-->|2|cond2
  judge3-->|3|cond3
  judge4-->|4|cond4
  classDef nodeapp fill:#acf
  classDef nodeapi fill:#fa9
  classDef nodecmd fill:#88e
  classDef nodecond fill:#ef9
  class db,pi1,sensor0,sensor3 nodeapp
  class sensor1,sensor2,sensor4,judge1,judge2,judge3,db2 nodeapi
  class command1 nodecmd
  class cond1,cond2,cond3 nodecond

ざっくりこんな感じ。
センサーデバイスの取得した値だけでなく

今は明るいのか暗いのか
今は朝なのか夜なのか
今動いている物体がいるのかいないのか
今家主はいるのかいないのか
今家主は動いているのかいないのか

これを調べることにしている。
家主がいるかいないかは
ラズパイにPasoriを挿してFelicaライブラリから
ICカードを載せられたかを判定して
DBにステータスを書き込んでいる。
これでカードが載っていたら家主がいると
判断することにしている。

Using Felica

早速Pasoriを挿して認識させる。
家にあったPasoriは以下のもの。

品名 型番
Sony 非接触ICカードリーダ/ライタPasori RC-S380

オーソドックスなUSB接続のタイプなもの。
おもむろに指してみる。

$ lsusb
Bus 001 Device 010: ID 054c:06c3 Sony Corp. 
Bus 001 Device 006: ID 0411:00b3 BUFFALO INC. (formerly MelCo., Inc.) PC-OP-RS1 RemoteStation
Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. SMSC9512/9514 Fast Ethernet Adapter
Bus 001 Device 002: ID 0424:9514 Standard Microsystems Corp. 
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

こんな感じで見えている。
一番上のやつがPasoriであろう。
ちなみに上から2番目のやつはKURO-RS。

参考にした記事はこちらの先駆者の方。
本当素晴らしい。
こちらの記事を参考にFelica.jsというモジュールを作る。

Felica.js

const NfcpyId = require('node-nfcpy-id').default;
const DB=require('./db');

const nfc = new NfcpyId({mode: 'loop'}).start();
//const nfc = new NfcpyId({mode: 'non-loop'}); // non-loop mode
//const nfc = new NfcpyId({mode: 'non-touchend'}); // non-touchend

nfc.on('touchstart', (card) => {
    console.log('touchstart', 'id:', card.id, 'type:', card.type);
    db=new DB();
    db.open();
    db.insertNowOn().then(()=>{
        db.close();
    });
});;

nfc.on('touchend', () => {
    console.log('touchend');
    db=new DB();
    db.open();
    db.insertNowOff().then(()=>{
        db.close();
    });
});

nfc.on('error', (err) => {
    console.error('err: '+err);    
});

本当に簡単にDB操作を加えただけ。
db.jsモジュールの内容は今後紹介は
してもいいのだが、on/off(1/0)を
テーブルに書き込んでいるだけで至ってシンプル
なのでここでは割愛。
こちらをforeverに渡してやる。

$ forever start Felica.js

Judge.jsモジュールはこちらの値と
モーションセンサの値を見て
家主が今いるかを判断する。

Main Daemon

今回は電気をつけるってことに
目的を絞っているが、他のものも
大体は前項のようなフローで動いている。

以前に書いたメインモジュールは
その後手を入れており
現在は以下のような作り。

Rpi+2s.js

const async = require('async');
const googleSpeak = require('./GoogleSpeak');
const scheduleCaller = require('./ScheduleCaller');
const Tenki = require('./Tenki');
const jdg = require('./Judge');
const kuro=require('./KURO-RS');
const google=require('./GoogleAPI');
const bulb=require('./setBulb');

var sleep=(s) => {
	var e = new Date().getTime() + (s * 1000);
	while (new Date().getTime() <= e);
}

var mq=async.queue((msg, callback) => {
	var gs=new googleSpeak();
	gs.bowwow(msg);
	setTimeout(() => {
		callback(null);
	}, 2000);
}, 2);

class Rpi2s {

    constructor() {
    }

    howling(){
        this.ret ;
        this.fc = new Tenki();
        let sc = new scheduleCaller();

        //check Lux
        let getlux=async () => {
            ...(snip)...
        }
        getlux();

        //check Temp
        let gettemp=async () => {
            ...(snip)...
        }
        gettemp();
        
        //check Humidity
        let gethumi=async () => {
            ...(snip)...
        }
        gethumi();
        
        //check Schedule
        let getsch=async()=>{
            ...(snip)...
        }
        getsch();

        //check YouTube or Twitter
        let getlastupdated=async () => {
            ...(snip)...
        }
        getlastupdated();
        
    }

    async bowwow(msg){
        let j=await new jdg();
        let isDupli=false;
        isDupli=await j.isDuplicated(msg);
        if(!isDupli){
            console.log(this.changeVariables(msg));
            mq.push(this.changeVariables(msg));
        }  
    }

    changeVariables(txt){
        var message=txt;
        var weather=this.fc.getWeather();
        var j=new jdg();
        var hellos=['おはようございます','こんにちは','こんばんは'];
        var hellotype=((j.isDaytime())?((j.isMorning())?0:1):((j.isNighttime())?2:0));
        var sayhello=hellos[hellotype];
        return message
            .replace(/\$MONTH/ig, new Date().toFormat("M"))
            .replace(/\$DAY/ig, new Date().toFormat("D"))
            .replace(/\$HOUR/ig, new Date().toFormat("H"))
            .replace(/\$MINUTE/ig, new Date().toFormat("M"))
            .replace(/\$SECOND/ig, new Date().toFormat("S"))
            .replace(/\$WEATHER/ig, weather.weather[0].wamei)
            .replace(/\$SAYHELLO/ig, sayhello);
    }
}
module.exports=Rpi2s;   

howling()というメソッドが
プログラムのmainにあたるところ。
bowwow()で喋らせるのだが
今はmqueueクラスを使っているので
ここではGoogleSpeakを呼ば出さなくなった。
同じ言葉を何回も喋らないように
JudgeモジュールにisDupricatedメソッドを
用意して以前喋らせた内容と一緒なら
queueに渡さないって処理を追加している。

今のところこれでなんとなく動いてはいる。
上のフロー図を見ていただくとわかると
思うのだが、各センサーデバイスから取得した
データを評価しているだけなので、例えば
電灯が点いているか、NASの電源が入っているか
加湿器の電源が落ちているかという
家電の電源ステータスは把握できてない。
今後はここが課題。

あと、Judge.jsも後々紹介していくかな。

Next

さて次は各リモートコントロールする機器の
ステータスをちゃんと取得してから
コマンド送るようにする。

目下、用意する必要があるのは、電灯と加湿器、NAS。
これらの電源が入っているかを確認してリモートからでも
電源状況が把握できるようにしてみる。

全然本題と関係ないが
今のヘビーローテーションはASAYAKE 01


コメント: