頭痛ーるから気圧情報もらってLINEに送信するようにした

0.気圧の上下で体調が左右される

最近気圧が下がるとあからさまに頭が重くなるようになりました。歳。

ところで、頭痛ーるというアプリをご存知でしょうか。ご存知ですよね。きっと。

頭痛ーるは、気圧の変化による体調不良が起こりそうな時間帯の確認や、痛み・服薬記録ができる気象予報士が開発した気象病対策アプリです。
頭痛ーる公式より

気圧を基に体調的な注意情報とかを教えてくれるアプリです。
このアプリ開くときって、「む…体調悪い気が…」って時に開いてることに気づきました。そう、そんな時に開いても時すでに遅し

「通知機能を使え」!?!???!?!!?
うるせえ!なんかなんとかしてやるよ!
ヤバそうなときにLINEで通知させたるで!

1.LINE Notifyのトークン取得

LINE Notifyにアクセスして、
ログイン>マイページ>トークンを発行する
からトークンを発行します。(トークン名とか部屋はお好きに。)
発行されたLINEのトークンはどっかにメモっときます。

2.頭痛ーるから気圧情報をもらう

頭痛ーるはAPI情報を公開しているわけではないですが、
頭痛ーるwebでchromeのDeveloper Toolを使用するとリクエストの内容が取得できます。(AWS使ってるみたいです)

以下参考:低気圧で頭痛がひどいのでslackで頭痛ーるを表示するappをlambdaで作った

こんな感じらしい。

curl https://zutool.jp/api/getweatherstatus/13104 | jq 

↑みたいな感じで取得すると(新宿区の例です)

$ curl https://zutool.jp/api/getweatherstatus/13104 | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  8197    0  8197    0     0   7581      0 --:--:--  0:00:01 --:--:-- 24468
{
  "place_name": "東京都新宿区",
  "place_id": "104",
  "prefectures_id": "13",
  "dateTime": "2023-02-19 15",
  "yesterday": [
    {
      "time": "0",
      "weather": "100",
      "temp": "5.2",
      "pressure": "1025.7",
      "pressure_level": "0"
    },
    {
      "time": "1",
      "weather": "100",
      "temp": "4.1",
      "pressure": "1025.3",
      "pressure_level": "0"
    },
   ~中略~
  ],
  "today": [
    {
      "time": "0",
      "weather": "200",
      "temp": "9.0",
      "pressure": "1015.6",
      "pressure_level": "4"
    },
    {
      "time": "1",
      "weather": "200",
      "temp": "9.5",
      "pressure": "1014.4",
      "pressure_level": "4"
    },
   ~中略~
  ],
  "tommorow": [
    {
      "time": "0",
      "weather": "100",
      "temp": "9.1",
      "pressure": "1006.8",
      "pressure_level": "0"
    },
    {
      "time": "1",
      "weather": "100",
      "temp": "8.5",
      "pressure": "1007.2",
      "pressure_level": "0"
    },
~中略~
  ],
  "dayaftertomorrow": [
    {
      "time": "0",
      "weather": "100",
      "temp": "2.7",
      "pressure": "1014.5",
      "pressure_level": "0"
    },
~中略~
    {
      "time": "23",
      "weather": "100",
      "temp": "1.2",
      "pressure": "1027.5",
      "pressure_level": "0"
    }
  ]
}

てな感じでURLで指定した地域の1時間ごとの天気、温度(℃)、気圧(hPa)、気圧の危険レベル(?)が昨日、今日、明日、明後日の4日分取得できます。

time:時刻
weather:晴→100、曇→200、雨→300、雪→400
temp:気温
pressure:気圧(hPa)
pressure_level:OK→0or1(1あんまり見ない)、やや注意→2、注意→3、警戒→4
て感じらしいです。

URLで指定する末尾の数字(新宿区でいう「13104」)は、 全国地方公共団体コードの上5桁です。おそらく。(6桁目はチェックサムみたいなので不要)
なので、先ほどのURLの「13104」を取得したい地域に合わせて変更するといい感じに取得できると思います。できなかったら頭痛ーるwebで調べるといいと思います。

3.1日1回取得→ヤバかったらLINE送信するようにする

こっからです。
毎日、curlして取得される長ったらしいjsonを読み取ってたら気が狂います。
そういうことでpythonです

ディレクトリはこんな感じ

/home/hogehoge/pressure_13104
  │
  ├ formatted_data.txt
  ├ main.py
  └ pressure_today.txt

main.pyの中は以下です(注意!素人の書いたコード!)

import subprocess, linecache ,os ,requests
from subprocess import PIPE

#頭痛ーるwebからcurlで新宿区の気圧情報jsonを引っ張ってきて当日分のみをpressure_todayに保存
proc = subprocess.run("curl https://zutool.jp/api/getweatherstatus/13104 | jq '. | {today} ' > /home/hogehoge/pressure_13104/pressure_today.txt", shell=True, text=True)

#格納されているはずの前日分整形済みデータを削除(formatted_data.txtがないとエラーになる)
os.remove('/home/hogehoge/pressure_13104/formatted_data.txt')

#取得したjsonから各時間の天気、気圧、気圧レベルを取得し各時間ごとに変数に格納する
#格納コマンドを作成、コマンドを変数として格納>exec実行
for i in range(24):
    time_cmd = 'p_time_' + str(i) + '=\"' + str(i) + '時\"'
    weather_cmd_if = 'p_weather_if=linecache.getline("/home/hogehoge/pressure_13104/pressure_today.txt", ' + str(7*i+5) + ').strip().strip(' + '\'\"weather\": \" ,\')'
    temp_cmd_a = 'p_temp_' + str(i) + 'a=' + 'linecache.getline("/home/hogehoge/pressure_13104/pressure_today.txt", ' + str(7*i+6) + ').strip().strip(' + '\'\"temp\": \" ,\')'
    temp_cmd = 'p_temp_' + str(i) + '=str(p_temp_' + str(i) + 'a)+\"℃\"'
    level_cmd_if = 'p_level_if=linecache.getline("/home/hogehoge/pressure_13104/pressure_today.txt", ' + str(7*i+8) + ').strip().strip(' + '\'\"pressure_level\": \" ,\')'

    exec(time_cmd)
    exec(weather_cmd_if)
    exec(temp_cmd_a)
    exec(temp_cmd)
    exec(level_cmd_if)

    #天気を絵文字に変換し、変数に格納
    if p_weather_if == "100" :
        weather_cmd = 'p_weather_' + str(i) + '=' + 'chr(9728)'
        exec(weather_cmd)
    elif p_weather_if == "200" :
        weather_cmd = 'p_weather_' + str(i) + '=' + 'chr(9729)'
        exec(weather_cmd)
    elif p_weather_if == "300" :
        weather_cmd = 'p_weather_' + str(i) + '=' + 'chr(9730)'
        exec(weather_cmd)
    elif p_weather_if == "400" :
        weather_cmd = 'p_weather_' + str(i) + '=' + 'chr(9731)'
        exec(weather_cmd)
    else :
        weather_cmd = 'p_weather_' + str(i) + '=' + '"不明"'
        exec(weather_cmd)

    #気圧レベルを絵文字に変換し、変数に格納
    if p_level_if ==  "0" :
        level_cmd = 'p_level_' + str(i) + '=' + 'chr(127383)'
        exec(level_cmd)
    elif p_level_if ==  "1" :
        level_cmd = 'p_level_' + str(i) + '=' + 'chr(127383)'
        exec(level_cmd)
    elif p_level_if ==  "2" :
        level_cmd = 'p_level_' + str(i) + '=' + 'chr(10549)'
        exec(level_cmd)
    elif p_level_if ==  "3" :
        level_cmd = 'p_level_' + str(i) + '=' + 'chr(9888)'
        exec(level_cmd)
    elif p_level_if ==  "4" :
        level_cmd = 'p_level_' + str(i) + '=' + 'chr(128163)'
        exec(level_cmd)
    else :
        level_cmd = 'p_level_' + str(i) + '=' + '"不明"'
        exec(level_cmd)

#気圧レベル3,4判定用変数
danger = '\"pressure_level\": \"4\"'
caution = '\"pressure_level\": \"3\"'

#elif使うべき。そのうち直したい
#↑条件のたびにいちいちwithopenすればいいのはわかったけどめんどい
#danger(気圧レベル4)の場合formatted_dataの先頭に警告文追記
with open('/home/hogehoge/pressure_13104/pressure_today.txt') as f:
    if danger in f.read():
        with open('/home/hogehoge/pressure_13104/formatted_data.txt', 'a') as f :
            print("【!!警戒!!】本日気圧の変化に警戒が必要です", file=f)
    else :
        pass

#caution(気圧レベル3)の場合formatted_dataの先頭に警告文追記
with open('/home/hogehoge/pressure_13104/pressure_today.txt') as f:
    if caution in f.read():
        with open('/home/hogehoge/pressure_13104/pressure_today.txt') as f:
            if danger in f.read():
                pass
            else :
                with open('/home/hogehoge/pressure_13104/formatted_data.txt', 'a') as f :
                    print("【!注意!】本日気圧の変化に注意が必要です", file=f)
    else :
        with open('/home/hogehoge/pressure_13104/formatted_data.txt', 'a') as f :
            print("今日の気圧変化", file=f)

#formatted_dataに一行ずつ追記していく
for i in range(24):
    print_cmd_1 = 'formatted_data=p_time_' + str(i) + ' + "  " + ' + 'p_weather_' + str(i) + ' + "  " + ' + 'p_temp_' + str(i) + ' + "  " + ' + 'p_level_' + str(i)
    exec(print_cmd_1)
    with open('/home/hogehoge/pressure_13104/formatted_data.txt', 'a') as f :
        print(formatted_data, file=f)

#気圧レベルが3or4のときLINE送信
#elif使うべき。そのうち直したい
with open('/home/hogehoge/pressure_13104/pressure_today.txt') as f:
    if danger in f.read():
        url = "https://notify-api.line.me/api/notify"
        access_token = 'xxxxxxxxxxxxxxx'
        headers = {'Authorization': 'Bearer ' + access_token}
        with open('/home/hogehoge/pressure_13104/formatted_data.txt') as f:
            message = f.read()
        payload = {'message': message}
        r = requests.post(url, headers=headers, params=payload,)
        print("done!")
    else :
        pass

with open('/home/hogehoge/pressure_13104/pressure_today.txt') as f:
    if caution in f.read():
        with open('/home/hogehoge/pressure_13104/pressure_today.txt') as f:
            if danger in f.read():
                pass
            else :
                url = "https://notify-api.line.me/api/notify"
                access_token = 'xxxxxxxxxxxxxxxxxxxxxxxx'
                headers = {'Authorization': 'Bearer ' + access_token}
                with open('/home/hogehoge/pressure_13104/formatted_data.txt') as f:
                    message = f.read()
                payload = {'message': message}
                r = requests.post(url, headers=headers, params=payload,)
                print("done!")
    else :
        print("done!")

ちょっとずつ説明。

#頭痛ーるwebからcurlで新宿区の気圧情報jsonを引っ張ってきて当日分のみをpressure_todayに保存
proc = subprocess.run("curl https://zutool.jp/api/getweatherstatus/13104 | jq '. | {today} ' > /home/hogehoge/pressure_13104/pressure_today.txt", shell=True, text=True)

なんかこれでcurlして取ってきたものから{today}の中だけ抽出して、
/home/hogehoge/pressure_13104/pressure_today.txtに保存してます。上書き。

#格納されているはずの前日分整形済みデータを削除(formatted_data.txtがないとエラーになる)
os.remove('/home/hogehoge/pressure_13104/formatted_data.txt')

コメントのまま。前日分の整形済みデータを消してます。
ファイルそのものがないとエラーになるので初回実行時は注意

#取得したjsonから各時間の天気、気圧、気圧レベルを取得し各時間ごとに変数に格納する
#格納コマンドを作成、コマンドを変数として格納>exec実行
for i in range(24):
    time_cmd = 'p_time_' + str(i) + '=\"' + str(i) + '時\"'
    weather_cmd_if = 'p_weather_if=linecache.getline("/home/hogehoge/pressure_13104/pressure_today.txt", ' + str(7*i+5) + ').strip().strip(' + '\'\"weather\": \" ,\')'
    temp_cmd_a = 'p_temp_' + str(i) + 'a=' + 'linecache.getline("/home/hogehoge/pressure_13104/pressure_today.txt", ' + str(7*i+6) + ').strip().strip(' + '\'\"temp\": \" ,\')'
    temp_cmd = 'p_temp_' + str(i) + '=str(p_temp_' + str(i) + 'a)+\"℃\"'
    level_cmd_if = 'p_level_if=linecache.getline("/home/hogehoge/pressure_13104/pressure_today.txt", ' + str(7*i+8) + ').strip().strip(' + '\'\"pressure_level\": \" ,\')'

    exec(time_cmd)
    exec(weather_cmd_if)
    exec(temp_cmd_a)
    exec(temp_cmd)
    exec(level_cmd_if)

24時間分の気圧とか天気とか取得するコマンドを生成します。
コマンドを生成→execで実行を繰り返すことで各時間の時間、天気、気温、気圧レベルを取得しています。


2時(i=2)を例にすると、
time_cmdには
p_time_2="2時"

weather_cmd_ifには
p_weather_if=(pressure_today.txtの7*2+5行目からstripで空白と"weather:"と","を取り除いたもの)(100とか200とかを格納したい)

temp_cmd_aには
p_temp_2a=(pressure_today.txtの7*2+6行目からstripで空白と"temp:"と","を取り除いたもの)(気温の数字のみを格納したい)

temp_cmdには
p_temp_2=p_temp_2a+"℃"

level_cmd_ifには
p_level_if=(pressure_today.txtの7*2+8行目からstripで空白と"pressure_level:"と","を取り除いたもの)(0とか1とか2とかを格納したい)

という感じで変数(コマンド)を格納しています


まあつまり、変数を宣言するコマンドを宣言→execで実行してるってことです。ややこし。今このコード書けねえ

    exec(time_cmd)
    exec(weather_cmd_if)
    exec(temp_cmd_a)
    exec(temp_cmd)
    exec(level_cmd_if)

で、宣言したコマンドを実行します。

こうすることで、
p_time_'時刻'='時刻'時
p_weather_if=100とか
p_tmp_'時刻'=〇〇℃
p_level_if=1とか
が変数に入ります(わかります?ここが山場です)

    #天気を絵文字に変換し、変数に格納
    if p_weather_if == "100" :
        weather_cmd = 'p_weather_' + str(i) + '=' + 'chr(9728)'
        exec(weather_cmd)
    elif p_weather_if == "200" :
        weather_cmd = 'p_weather_' + str(i) + '=' + 'chr(9729)'
        exec(weather_cmd)
    elif p_weather_if == "300" :
        weather_cmd = 'p_weather_' + str(i) + '=' + 'chr(9730)'
        exec(weather_cmd)
    elif p_weather_if == "400" :
        weather_cmd = 'p_weather_' + str(i) + '=' + 'chr(9731)'
        exec(weather_cmd)
    else :
        weather_cmd = 'p_weather_' + str(i) + '=' + '"不明"'
        exec(weather_cmd)

p_weather_ifに100とか200とかいう天気を示す値が入っているわけですが、そのままだとわからんなので絵文字に変換して別の変数に格納します。
100のときは☀、200のときは☁…といった感じにp_weather_x(xは時刻)に格納していきます。

    #気圧レベルを絵文字に変換し、変数に格納
    if p_level_if ==  "0" :
        level_cmd = 'p_level_' + str(i) + '=' + 'chr(127383)'
        exec(level_cmd)
    elif p_level_if ==  "1" :
        level_cmd = 'p_level_' + str(i) + '=' + 'chr(127383)'
        exec(level_cmd)
    elif p_level_if ==  "2" :
        level_cmd = 'p_level_' + str(i) + '=' + 'chr(10549)'
        exec(level_cmd)
    elif p_level_if ==  "3" :
        level_cmd = 'p_level_' + str(i) + '=' + 'chr(9888)'
        exec(level_cmd)
    elif p_level_if ==  "4" :
        level_cmd = 'p_level_' + str(i) + '=' + 'chr(128163)'
        exec(level_cmd)
    else :
        level_cmd = 'p_level_' + str(i) + '=' + '"不明"'
        exec(level_cmd)

気圧(警戒)レベルも同様です。
0か1のときはOK、2のときは下矢印…といった絵文字をp_level_x(xは時刻)に宣言していきます。

ここまでを0~23時分、計24回繰り返し処理することで
LINEの文面に必要な
p_time_x , p_temp_x , p_weather_x , p_level_x (各xは時刻)
の4つの変数×24時間分、計96個の変数が宣言されるという感じです。

#気圧レベル3,4判定用変数
danger = '\"pressure_level\": \"4\"'
caution = '\"pressure_level\": \"3\"'

pressure_level: 4もしくは3があったときに通知を出したいので
該当文面をdanger,cautionとして変数として入れておきます

#elif使うべき。そのうち直したい
#↑条件のたびにいちいちwithopenすればいいのはわかったけどめんどい
#danger(気圧レベル4)の場合formatted_dataの先頭に警告文追記
with open('/home/hogehoge/pressure_13104/pressure_today.txt') as f:
    if danger in f.read():
        with open('/home/hogehoge/pressure_13104/formatted_data.txt', 'a') as f :
            print("【!!警戒!!】本日気圧の変化に警戒が必要です", file=f)
    else :
        pass

#caution(気圧レベル3)の場合formatted_dataの先頭に警告文追記
with open('/home/hogehoge/pressure_13104/pressure_today.txt') as f:
    if caution in f.read():
        with open('/home/hogehoge/pressure_13104/pressure_today.txt') as f:
            if danger in f.read():
                pass
            else :
                with open('/home/hogehoge/pressure_13104/formatted_data.txt', 'a') as f :
                    print("【!注意!】本日気圧の変化に注意が必要です", file=f)
    else :
        with open('/home/hogehoge/pressure_13104/formatted_data.txt', 'a') as f :
            print("今日の気圧変化", file=f)

curlで取得した当日分データ(pressure_today.txt)の中に
先ほど宣言したdangerもしくはcautionに当てはまる文面が存在する場合に、
LINE送信文面(formatted_data.txt)の先頭に警告文を追加します。
コメントで残していますが、ifを何回も重ねていてダサいです。直したい。
あと変数宣言の中でp_level_ifの値を見て危険レベル設定するとかもできるじゃんね。なんでやってないんだ。そのうち直します。そのうち。

#formatted_dataに一行ずつ追記していく
for i in range(24):
    print_cmd_1 = 'formatted_data=p_time_' + str(i) + ' + "  " + ' + 'p_weather_' + str(i) + ' + "  " + ' + 'p_temp_' + str(i) + ' + "  " + ' + 'p_level_' + str(i)
    exec(print_cmd_1)
    with open('/home/hogehoge/pressure_13104/formatted_data.txt', 'a') as f :
        print(formatted_data, file=f)

先頭行に文章が追加されたら、
あらかじめ整形した変数を時刻ごとに1行にまとめて、合計24行追記していきます。

#気圧レベルが3or4のときLINE送信
#elif使うべき。そのうち直したい
with open('/home/hogehoge/pressure_13104/pressure_today.txt') as f:
    if danger in f.read():
        url = "https://notify-api.line.me/api/notify"
        access_token = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
        headers = {'Authorization': 'Bearer ' + access_token}
        with open('/home/hogehoge/pressure_13104/formatted_data.txt') as f:
            message = f.read()
        payload = {'message': message}
        r = requests.post(url, headers=headers, params=payload,)
        print("done!")
    else :
        pass

with open('/home/hogehoge/pressure_13104/pressure_today.txt') as f:
    if caution in f.read():
        with open('/home/hogehoge/pressure_13104/pressure_today.txt') as f:
            if danger in f.read():
                pass
            else :
                url = "https://notify-api.line.me/api/notify"
                access_token = 'xxxxxxxxxxxxxxxxxxxxxxxx'
                headers = {'Authorization': 'Bearer ' + access_token}
                with open('/home/hogehoge/pressure_13104/formatted_data.txt') as f:
                    message = f.read()
                payload = {'message': message}
                r = requests.post(url, headers=headers, params=payload,)
                print("done!")
    else :
        print("done!")

最後、何でもないときもLINE送られてきてもしょうがないので
警戒レベルが高い(3or4)ときにLINEを送信するようにします。
大体先頭行への警告文の追加と同じ。
LINEの送信はaccess_tokenを最初に取得したトークンに書き換えたらいけると思います。

4.反省

  • ifの入れ子が起こっているのをホントどうにかしないと。条件式のたびにwith openしないといけないことに気付くのが遅くてこんなことになりました。ちゃんと動いたら満足してしまった。elif使わないと。

  • そもそも↑みたいにいちいちwith openしなくても、気圧レベルの変数を格納している段階で3or4があったらdangerとかcautionのフラグを立てて置いたらあとは一発なのでは?

5.結果

なんだかんだ動いてます。


こんな感じ

一行ずつ時間と天気と気温と警戒レベルです。なんもない日には届きません。

雑なまとめだ~~~~~~~~~~~~~~~~~~~!!!
作ってから数ヵ月たってるしそろそろきれいにしたいな~~~とか思って反省点を振り返るために書いてみたけどわけわからん。自分の書いたコード。
過去の自分へ:もっと丁寧にコーディングして下さい

セブンのハッシュポテト代になる