システムの概要
どんなものを作ったか
大学にいる時に雨が降ってきそうな天候の場合、さっさと家に帰るべきなのか、まだ大学にいてもいいのかわからないですよね。
今回作ったシステムは、そんな問題を手軽に解決するためのものです。三時間後の天気予報をツイートするBotアカウントを作ることで、ユーザがまだ大学にいていいのか、判断を補助するためのシステムを目指しました。
システム構成
ラズベリーパイで自校である愛知工業大学周辺地域の天気を取得し、作った専用アカウントでその情報をわかりやすい形にフォーマットして呟きます。
システムの構成は以下の通りです。
- Rasberry Pi model B+
- 専用のTwitterアカウント
- ThingTwiite
- OpenWeatherMap
Rasberry PiがOpenWeatherMapにリクエストを送信し、受け取ったレスポンス(天気情報)を整形して、その情報をThingTweetAPIに投げます。ThingTweetAPIはAPIキーデータを元にして、紐づけられた天気予報用の専用アカウントでその情報をツイートします。
この流れで天気予報を呟きます。
APIについてはこちらの記事、書籍にお世話になりました。
書籍:Raspberry Pi クックブック 369p「ThingSpeakを使ってツイートする」
記事:【WebAPI】OpenWeatherMapで3時間ごとの天気を取りたい【json】
システム詳細
ソースコード
ソースコードの全貌はGithubに載せてあります。
APIキーは残ったままになっていますが、削除&再生成しているので問題ありません。
ここでは、こだわったポイントについて載せようと思います!
Main文中のbeSureToDoメソッド
def beSureToDo(city):
while(True):
try:
data = weatherApi.getDataLatLon(city[0],city[1],1)
weatherData = extractor.DataSetting(data)
twitter.twitte( twitext.Build(weatherData) )
except apie.APIgetDataException:
sleep(60)
continue
break
やっていることとしてはまず、設定した市の緯度経度を元にJson形式の天気データを取得し、そこから抽出した天気情報をまとめてweatherData
に入れます。そして、ツイートするテキストを組み立てる役割を持ったクラスのインスタンスであるtwitext
に天気情報を渡して組み立てたツイート文章をTwitterクラスに投げてツイートさせています。
注目してほしいのは、天気情報の取得に失敗することで発生するAPIgetDataException
をキャッチして一分後にもう一度天気情報を取得できないかトライしているところです。こうすることで、APIにリクエストが集中して情報が取れなかった場合にも処理を継続して確実に天気情報をツイートすることができます!
WeatherApiUiクラス
# coding: utf-8
import json
import requests
import APIgetDataException as apide
class WeatherApiUi:
def __init__(self):
self.api_key = '***************api - key************'
self.baseUrl = 'http://api.openweathermap.org/data/2.5/forecast?&APPID={0}'
def getDataLatLon(self, lat, lon, cnt=0):
api_page = self.baseUrl + '&lat={1}&lon={2}'
if 0 < cnt and cnt <= 30:
api_page = api_page + '&cnt=' + str( cnt )
url = api_page.format(self.api_key, lat, lon)
req_response = requests.get( url )
res = json.loads(req_response.text)
if "message" in res and 'Internal error: 500001' == res["message"]:
raise apide.APIgetDataException("APIサービスが混んでいるようです")
return res
def getDataZip(self, zip, cnt=0):
api_page = self.baseUrl + '&zip={1}'
if 0 < cnt and cnt <= 30:
api_page = api_page + '&cnt=' + str( cnt )
url = api_page.format(self.api_key,zip)
req_response = requests.get( url )
res = json.loads(req_response.text)
if "message" in res and 'Internal error: 500001' == res["message"]:
raise apide.APIgetDataException("APIサービスが混んでいるようです")
return res
getDataLatLon
は緯度経度の情報に従って、その地点の天気情報をAPIから取得するためのメソッドで、getDataZip
は郵便番号を元に天気情報を取得するメソッドです。取得件数も指定できるようになっています。
OpenWeatherMapからの情報取得方法には都市名を指定してデータを取得するものや都市IDを指定してデータを取得するものもありますが、それらも当クラスに同様にように実装すれば、OpenWeatherMapからデータを取得するクラスとしてかなり充実したものとなると思います。
作成段階では、当クラスに必要な天気情報だけを抽出するメソッドも実装するという案ありましたが、メソッド数が多くなりすぎてしまうのでそれは断念しました。しかも、よくよく考えると、このクラスの役割が「APIからデータを取得すること」だけでなく、「必要な情報を抜き出すこと」も持ってしまうところでした。あぶなかったです。
教訓としてはメソッド数が多くなりすぎるときはそのクラスが責務を負い過ぎている可能性が高いというところでしょうか。
オブジェクト指向でいうところの
- 単一責務の法則
を実践できた瞬間でした。
システムの運用方法
OpenWeatherMapでは、天気情報の更新が0,3,6,9,12,15,18,21時に行われるので、3時間毎の定期処理実行が必要になってきます。これにはcron
コマンドを利用しました。
cron
とは?
cron
(クーロン)とは、Linux標準の定期処理実行を行う常駐プログラムです。crontab
と呼ばれる設定ファイルに定期で実行するコマンドを書き足すことによって利用することができます。
常駐プログラムという特性上、crontab
に実行するコマンドを記述するだけで、処理が定期実行されるようになります。
cronコマンドについてはRasberry Piを触るまで知らなかったので、色々参考にしました。
クーロン(cron)をさわってみるお
cronでスクリプトを定期実行させるときに注意したい4つのこと
Cron が走っているかどうか確認する
cronの停止コマンド
RaspberryPi cronが効かない → /etc/crontab のパーミッションと所有者を見直して解決
調査の結果、以下のコマンドをcrontabに記述しておけば良いことがわかりました。
LANG=jp_JP.UTF-8
0 0,3,6,9,12,15,18,21 * * * python3 /etc/pi/hoge/fugafuga/Main.py
一行目では、スクリプト中に登場する日本語を文字化けさせないための環境変数設定です。cron
は実行時にユーザの環境変数を引き継がないので、スクリプト中に日本語を使っている場合は文字化けを防ぐために書いた方が良いとのことです。
cron
について学んだこと一覧
-
cron
は、設定した定期処理を実行してくれる常駐プログラムである - 設定の変更は
crontab -e
で行える(ユーザ権限の設定ファイル) -
crontab -r
で設定が吹き飛ぶ(消去される)ので、注意する。 - 管理者用の
crontab
は/home/pi
階層から../../etc/crontab
の位置にある。 - なおこのファイルを変更する場合はテキストエディタで設定する。例としては
sudo nano crontab
である。 -
cron
が実行されているかはservice cron status
で調べられる。 -
cron
を起動するコマンドは、service cron start
-
cron
を再起動するコマンドはservice cron restart
-
cron
を停止するコマンドはservice cron stop
-
cron
に設定されているコマンドを確認するためのコマンドはcrontab -l
- ネットの情報を見ていると
crond
、cron
の二種類が散見されるが、Rasberry Piの場合はcron
である。 - 権限を付与しないとコマンドを実行できない場合がある。
-
cron
の時間指定は分 時 日 月 曜日
の順 - コマンドは絶対パスで記述した方が良い。
できあがったもの
こんな感じになりました!
ラズパイ天気予報! ~for AIT~のTwitterアカウント
Rasberry PiがAITの周辺の市の天気予報を三時間おきにつぶやいています。
これで早く帰らないといけないかがわかりますね(予報だから当たるとは言ってない)
参考文献
python
Pythonのdatetimeで日付や時間と文字列を変換(strftime, strptime)
Pythonで日付から曜日や月を文字列(日本語や英語など)で取得
Pythonの数値の桁数や丸めなどの操作のまとめcron
について
クーロン(cron)をさわってみるお
cronでスクリプトを定期実行させるときに注意したい4つのこと
Cron が走っているかどうか確認する
cronの停止コマンド
RaspberryPi cronが効かない → /etc/crontab のパーミッションと所有者を見直して解決git
gitでのヤバイ!を取り消す方法API
Raspberry Pi クックブック 369p「ThingSpeakを使ってツイートする」
【WebAPI】OpenWeatherMapで3時間ごとの天気を取りたい【json】