script for me
さて、今回は、何かをトリガーに
しゃべるって仕組みを作ってみる。
トリガーは以下を用意する予定。
- When I Want(時間で)
- If She Felt Temp/Humidity(温湿度センサから)
- When She Saw(カメラから)
- If She Felt Someone(人感センサから)
今回は1番目のやつを実装する。
say something when I want her to tell me
しゃべってほしい時間にしゃべる仕組みが
1番簡単そうなので小手調べ。
どんな風にスケジュールを管理しようかな?
例えばGoogleカレンダみたいなクラウドから
持ってくるか。そうなると、例えば目覚ましとかの
ルーティンワークも登録する必要がある。
むしろ予定はこっちから聞くことが多いから
雑多でありながら忘れがちなものを
教えてくれたほうが気が利くってもんだ。
- ○月○日○時です。天気は○です。おはようございます。
- もう○時です、寝る時間ですよ。
- 家を出る時間まであと○分です。
- お誕生日おめでとうございます。
- 今日は○さんのお誕生日です。
みたいなの。平日と休日も理解して
繰り返してくれる。
本当は誰かの誕生日の○週間前の週末ぐらいに
教えてくれるのが親切なんだろうな。
これは後でやろう。
大まかな作りとしては
rpi+2s.jsというメインのモジュールを作って
サブモジュールとしてScheduleCaller.jsなんてのを
用意して、ここで実装する。
write down the recipe
ScheduleCaller.jsを書いてみる。
今回はJSON形式のファイルを解析して
喋らせようかなと思う。構造はこんな感じ。
[ {"no": no, "name": schedule name, "freq": executed frequency, "date": executed date, "time": executed time, "message": what she speaks },
{"no": no, . . . }, . . . ]
|
こんな感じにした。
noとnameは今は特に意味はなし。
freqは次の値から選べる。
- m : 毎時間同分に実行
- h : 同時刻に実行
- d : 同日同時刻に実行
- w : 同曜日同時刻に実行
dateはfreqによって入れる値が変わる。
freqがmとhの場合はemptyでOK。
dはMM/DD形式の日付。
wは曜日の英語表記を英小文字3文字で。
(例:monとかsatとか)
timeは常に時刻を入れる。HH24:MI形式で。
ただしfreqがmの場合は分の部分しか評価しない。
messageは喋らせたい言葉を入れる。
これをschedule.jsonってファイルに保存する。
どのくらいのタイミングでチェックするかは
まだ決めてないけど、予定時刻の
30分以内か1分以内であればトリガーを
発動するようにしたい。
さてやっとモジュールを書いてみる。
const fs = require('fs'); const now = require('date-utils'); const schedJSON = './schedule.json';
const chkWeekly = 'w'; const chkDaily = 'd'; const chkHouly = 'h'; const chkMinutely = 'm';
const endTimeInterval1 = 1000 * 60 * 30; const endTimeInterval2 = 1000 * 60;
class ScheduleCaller {
constructor(){ this.now = new Date(); this.load(); }
load(){ var promise=Promise.resolve(); promise .then(this.parseJSON()) .then(this.loadJSON()) .catch(this.onRejected); }
parseJSON(){ this.rs = JSON.parse('{"result": false, "message": "", "weather": "" }'); }
loadJSON(){ this.sc = JSON.parse(fs.readFileSync(schedJSON, 'utf8')); }
onRejected(err){ console.log(err); }
isInTime(validTime, validDate, checkType) { var isCorrect=false; var validtime=new Date('1990/01/01 '+validTime); switch(checkType){ case chkDaily: var validtime=new Date('1990/'+validDate+' '+validTime); var nexttime=new Date(validtime.getTime() + endTimeInterval1); if(this.now.toFormat("MM/DD")==validtime.toFormat("MM/DD")){ if((this.now.toFormat("HH24:MI")>=validtime.toFormat("HH24:MI"))&& nexttime.toFormat("HH24:MI")>=this.now.toFormat("HH24:MI")){ if(!isCorrect)isCorrect=!isCorrect; } } break; case chkWeekly: var day = new Date().getDay(); var weekday = 'sunmontuewedthufrisat'.substring((day*3),(day*3)+3); var nexttime=new Date(validtime.getTime() + endTimeInterval1); if(weekday==validDate.toLowerCase()){ if((this.now.toFormat("HH24:MI")>=validtime.toFormat("HH24:MI"))&& nexttime.toFormat("HH24:MI")>=this.now.toFormat("HH24:MI")){ if(!isCorrect)isCorrect=!isCorrect; } } break; case chkHouly: var nexttime=new Date(validtime.getTime() + endTimeInterval2); if(this.now.toFormat("HH24")==validtime.toFormat("HH24")){ if((this.now.toFormat("MI")>=validtime.toFormat("MI"))&& nexttime.toFormat("MI")>=this.now.toFormat("MI")){ if(!isCorrect)isCorrect=!isCorrect; } } break; case chkMinutely: var nexttime=new Date(validtime.getTime() + endTimeInterval2); if((this.now.toFormat("MI")>=validtime.toFormat("MI"))&& nexttime.toFormat("MI")>=this.now.toFormat("MI")){ if(!isCorrect)isCorrect=!isCorrect; } break; } return isCorrect; }
hasTimeCome(){ var message=''; var needSpeak=false; for(var i=0;i<this.sc.length;i++){ if(this.isInTime(this.sc[i].time, this.sc[i].date, this.sc[i].freq)){ message+=" "+this.sc[i].message; if(!needSpeak)needSpeak=!needSpeak; } } if(needSpeak){ this.rs.result=true; this.rs.message=message; }else{ this.rs.result=false; this.rs.message=message; } return this.rs; } } module.exports=ScheduleCaller;
|
まだまだ改造の余地があるが、ひとまず先に進みたいので
この辺にしておく。
動作としては、JSONファイルに従って動くこととして
もし同時刻の予定が重なった場合も考えて
喋らせる言葉を直列につなぐようにした。
喋らせる言葉には変数を入れられるようにして
$HOURとか$WEATHERを入れておくと
勝手に変換されるようにする。
そのうち$FORTUNEとか$TEMPみたいなのも
追加してみたい。
$WEATHERで天気を変換したいのでTenkiモジュールを作ってみる。
天気の情報をOpen Weather Map APIを使うことにする。
取得する情報の同期処理をかけたいので
sync-requestモジュールを使っている。これは大変便利。
さて書いてみる。
const request=require('sync-request'); const mycity="Sapporo-shi,JP"; const units='metric'; const openweatherapi="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
class Tenki {
constructor(){ this.URL = 'http://api.openweathermap.org/data/2.5/weather?q='+ mycity +'&units='+ units +'&appid='+ openweatherapi; this.res; this.fetch(); this.localize(); }
fetch(){ var response=request('GET', this.URL); var body=JSON.parse(response.body); this.res=body; }
localize(){ var result=''; if(this.res){ for(var i=0;i<this.res.weather.length;i++){ switch(this.res.weather[i].main){ case 'Rain': this.res.weather[i].wamei='雨'; break; case 'Mist': this.res.weather[i].wamei='霧'; break; case 'Clear': this.res.weather[i].wamei='晴'; break; case 'Clouds': this.res.weather[i].wamei='曇'; break; default: this.res.weather[i].wamei='不明'; break; } } }else{ console.error(err); } }
getWeather(){ return this.res; } } module.exports=Tenki;
|
天気の和名翻訳を自前で作っているのだが
まだ間に合ってない。
ま、これで
const tenki=require('./Tenki'); var tk=new tenki(); var w=tk.getWeather();
console.log(w.weather[0].wamei);
|
こんな感じで取得できる。
んで、スケジュール管理と天気の変換を行う
メインのモジュールを書いてみる。
どんどんやっつけになってきたかな?
const googleSpeak = require('./GoogleSpeak'); const scheduleCaller = require('./ScheduleCaller'); const Tenki = require('./Tenki');
class Rpi2s {
constructor() { }
howling(){ this.isHowling=false; this.ret; this.sc = new scheduleCaller(); this.fc = new Tenki();
this.ret=this.sc.hasTimeCome(); if(this.ret.result){ this.message=this.changeVariables(this.ret.message); if(!this.isHowling)this.isHowling=!this.isHowling; }else{ console.log(this.ret); }
if(this.isHowling){ this.bowwow(); } }
bowwow(){ var gs=new googleSpeak(); gs.meow(this.message); }
changeVariables(txt){ var message=txt; var forecast=this.fc.getWeather(); return message .replace(/\$MONTH/i, new Date().toFormat("M")) .replace(/\$DAY/i, new Date().toFormat("D")) .replace(/\$HOUR/i, new Date().toFormat("H")) .replace(/\$MINUTE/i, new Date().toFormat("MM")) .replace(/\$SECOND/i, new Date().toFormat("SS")) .replace(/\$WEATHER/i, forecast.weather[0].wamei); }
} module.exports=Rpi2s;
|
まだトリガーが一つしかないので寂しいが
これから数珠つなぎにいろんなトリガーを
くっつけていってrπ+2sを賢くしてみたい。
const Rpi2s=require('./Rpi+2s'); var rpi2s=new Rpi2s(); rpi2s.howling();
|
このファイルをrpi2s.jsみたいなファイル名で保存して
foreverに渡せばいいのかな?
ちょっとまだ良くわかってないから調べてみよう。
例えば、こんなJSONファイルを用意する。
[ {"no": "1", "name": "good morning", "freq": "h", "date": "", "time": "07:00", "message": "$MONTH月$DAY日$HOUR時です。天気は$WEATHERです。おはようございます。"} ]
|
ってファイルを作ってみる。
rpi2s.jsを実行してみると
8がつ19にち7じです。 てんきはあめです。 おはようございます。
なんて朝の情報番組のキャスターみたいなことを言ってくれる。
Next
さて次は、やっとラズパイっぽいことを
してみようかな?
センサをトリガーにして
なんかしゃべらせるってやつ。
コメント: