雨雲レーダーを監視してメール通知する
某雨雲レーダーを監視し、指定範囲内のメッシュが一定の割合を超えるとGmailからメールを送信します。
PIL以外は全て標準ライブラリを使っているので、AndroidのQPythonでも動きました。
事前にcurlなどでリフレッシュトークンを取得しておいてください。
ソース
# -*- coding: utf-8 -*- import base64, io, json, logging, time, urllib, urllib2 from datetime import datetime, timedelta from PIL import Image from email.mime.text import MIMEText from email.header import Header # レーダー設定 AMESH_BASE_URL = "http://tokyo-ame.jwa.or.jp/mesh/100/{0}.gif" CROP_BOX = (100, 100, 100, 100) # 選択範囲 NOTIFY_PERCENTAGE = 10 # 10%以上で通知 NOTIFY_INTERVAL = 30 # 30分間隔で確認 # Gmail認証 CLIENT_ID = "YOUR_CLIENT_ID" CLIENT_SECRET = "YOUR_CLIENT_SECRET" REFRESH_TOKEN = "YOUR_REFRESH_TOKEN" # 送信メール設定 MAIL_TO = "foo@bar.ne.jp" # 宛先 MAIL_SUBJECT = u"雨雲レーダー" # 件名 MAIL_BODY = u"雨雲が接近しています" # 本文 last_notified = datetime(1, 1, 1) interval_time = timedelta(minutes = NOTIFY_INTERVAL) def main(): global NOTIFY_PERCENTAGE global NOTIFY_INTERVAL global scheduler global last_notified global interval_time logging.info("処理を開始します") now = datetime.now() if now - last_notified > interval_time: logging.info("レーダー画像を取得します") clouds_exists = check_clouds(now) if clouds_exists: token = get_token() if token: send_mail(token, now) else: logging.info("メッシュが%d%%以下です", NOTIFY_PERCENTAGE) else: logging.info("最後の通知から%d分以内です", NOTIFY_INTERVAL) logging.info("処理が終了しました") def check_clouds(now): try: timestamp = get_timestamp(now) # レーダー画像をダウンロード img_file = io.BytesIO(urllib2.urlopen(AMESH_BASE_URL.format(timestamp)).read()) # PILで開く img = Image.open(img_file) # 選択範囲でトリミングしてRGB値を取得 img = img.crop(CROP_BOX) img_rgb = img.convert("RGB").getdata() # 雨雲のメッシュを数える mesh_count = 0.0 for rgb in img_rgb: if rgb != (0, 0, 1): mesh_count += 1 # 割合を算出 mesh_percentage = mesh_count / len(img_rgb) * 100 logging.info("現在のメッシュ: %s%%", mesh_percentage) # 閾値を超えた場合は通知 if mesh_percentage > NOTIFY_PERCENTAGE: return True return False except Exception as e: logging.error("画像取得でエラーが発生しました: %s", e.message) return False def get_timestamp(now): return now.strftime("%Y%m%d%H") + str(now.minute / 5 * 5).zfill(2) def get_token(): global CLIENT_ID global CLIENT_SECRET global REFRESH_TOKEN url = "https://accounts.google.com/o/oauth2/token" params = {"client_id": CLIENT_ID, "client_secret": CLIENT_SECRET, "refresh_token": REFRESH_TOKEN, "grant_type": "refresh_token"} try: request = urllib2.Request(url, urllib.urlencode(params)) response = urllib2.urlopen(request) token = json.load(response)["access_token"] logging.info("トークンを取得しました") return token except Exception as e: logging.error("トークン取得でエラーが発生しました: %s", e.message) def send_mail(token, now): global MAIL_TO global MAIL_SUBJECT global MAIL_BODY global last_notified url = "https://www.googleapis.com/gmail/v1/users/me/messages/send" # メールを作成 encoding = "utf-8" message = MIMEText(MAIL_BODY, "plain", encoding) message['to'] = MAIL_TO message['from'] = "me" message['subject'] = Header(MAIL_SUBJECT, encoding) data = {'raw': base64.urlsafe_b64encode(message.as_string())} # リクエスト送信 try: request = urllib2.Request(url, json.dumps(data)) request.add_header("Content-Type", "application/json") request.add_header("Authorization", "Bearer " + token) urllib2.urlopen(request) logging.info("メールを送信しました") last_notified = now except Exception as e: logging.error("メール送信でエラーが発生しました: %s", e.message) if __name__ == "__main__": logging.basicConfig(level = logging.INFO, format = "%(asctime)s %(levelname)s %(message)s") while True: main() # 5分間隔で実行 time.sleep(300)
Base64にエンコードする際、URLセーフにするのがポイントではないでしょうか。
しかしglobalが多すぎる、これはひどい。