はじめに

ムシムシとした日が福岡では続いていますがいかがお過ごしでしょうか。

既に奥さんが熱中症っぽい症状で寝込んでしまっているのを見て、旦那として看病以外で役に立てないものか思いを巡らせて辿り着いたのが、気温、湿度を収集して可視化、不快指数を計算して事前に警告する等が手元の環境で出来そうなので試してみたメモ。

OpenWeatherMap で温度と湿度を取得する

OpenWeatherMap とは

OpenWeatherMap current weather and forecast

ざっくり、無料で天気の情報を API 提供してくれる有り難いサイトで、例えば、福岡の天気は以下のようなリクエストを投げる。

$ curl -s 'http://api.openweathermap.org/data/2.5/weather?q=fukuoka-shi,jp&units=metric' | python -m json.tool

以下のようなレスポンスが返ってくる。

{
    "base": "stations",
    "clouds": {
        "all": 75
    },
    "cod": 200,
    "coord": {
        "lat": 33.609999999999999,
        "lon": 130.41999999999999
    },
    "dt": 1436654395,
    "id": 1863967,
    "main": {
        "humidity": 83,
        "pressure": 1004,
        "temp": 26.760000000000002,
        "temp_max": 28.329999999999998,
        "temp_min": 24.440000000000001
    },
    "name": "Fukuoka-shi",
    "sys": {
        "country": "JP",
        "id": 7546,
        "message": 0.0149,
        "sunrise": 1436559409,
        "sunset": 1436610639,
        "type": 1
    },
    "visibility": 10000,
    "weather": [
        {
            "description": "broken clouds",
            "icon": "04n",
            "id": 803,
            "main": "Clouds"
        }
    ],
    "wind": {
        "deg": 110,
        "speed": 1.5
    }
}

レスポンスの詳細については以下のドキュメントを。

current

ざくっと、今回利用したいキーは以下のとおり。

JSON キー 内容 備考
[‘main’][‘humidity’] 湿度
[‘main’][‘temp’] 温度 リクエストパラメータで units=metric を付けると摂氏となる

不快指数とは

不快指数

気温と湿度から算出する夏の蒸し暑さを数量的に表した指数で計算式は下記の通り。

0.81 × 気温 + 0.01 × 湿度 × (0.99 × 気温 - 14.3) + 46.3

例えば、先ほどの API レスポンスから算出すると以下のとおりとなる。

0.81 × 26.760000000000002 + 0.01 × 83 × (0.99 × 26.760000000000002 - 14.3) + 46.3 = 76.8552

算出された不快指数から体感を以下のように分類されている。(Wikipedia より引用)

不快指数 体感
~55 寒い
55~60 肌寒い
60~65 何も感じない
65~70 快い
70~75 暑くない
75~80 やや暑い
80~85 暑くて汗が出る
85~ 暑くてたまらない

ということで、温度、湿度の値を収集しつつ、不快指数を算出してアラートを上げる仕組みを作ってみることにしよう。

Sensu と Grafana の用意

構成

しきい値

しきい値は以下のように定義。

不快指数 体感 Sensu のしきい値
~55 寒い Normal(exit 0)
55~60 肌寒い Normal(exit 0)
60~65 何も感じない Normal(exit 0)
65~70 快い Normal(exit 0)
70~75 暑くない Normal(exit 0)
75~80 やや暑い Warning(exit 1)
80~85 暑くて汗が出る Warning(exit 1)
85~ 暑くてたまらない Critical(exit 2)

プラグイン

以下のような OpenWeatherMap から取得出来るレスポンスをパースして標準出力に出力するだけのスクリプトを用意。

#!/usr/bin/env python

import json, urllib2, sys, time, random

argvs     = sys.argv
cities    = argvs[1].split(",")
timestamp = int(time.time())


def temperature_humidity_index():
        index = 0.81 * int(temperature) + 0.01 * int(humidity) * (0.99 * int(temperature) - 14.3) + 46.3
        return index

def check():
        index = temperature_humidity_index()
        label = city + ' temperature-humidity_index.current'

        if index >= 85 :
                print '%s %s %s' % (label, index, 'critical')
                sys.exit(2)
        elif index >= 75 :
                print '%s %s %s' % (label, index, 'warning')
                sys.exit(1)
        else:
                print '%s %s %s' % (label, index, 'normal')
                sys.exit(0)

def metrics():
        index = temperature_humidity_index()
        label = 'stats' + '.' + city + '.' + 'temperature.current'
        print '%s %s %dn' % (label, temperature, timestamp)

        label = 'stats' + '.' + city + '.' + 'humidity.current'
        print '%s %s %dn' % (label, humidity, timestamp)

        label = 'stats' + '.' + city + '.' + 'temperature-humidity_index.current'
        print '%s %s %dn' % (label, index, timestamp)

for city in cities:
        url = 'http://api.openweathermap.org/data/2.5/weather?q=' + city + ',jp&units=metric'
        r = urllib2.urlopen(url)
        j = json.loads(r.read())

        temperature = j['main']['temp']
        humidity = j['main']['humidity']

        if argvs[2] == "check":
                check()
        elif argvs[2] == "metrics":
                metrics()

        r.close()

スクリプトはコマンドラインから以下のように実行することで動作確認をすることが出来る。

# チェック
$ ./open_weather_map.py fukuoka-shi check
fukuoka-shi temperature-humidity_index.current 78.1478 warning

# メトリクス
$ ./open_weather_map.py fukuoka-shi,tokyo metrics
stats.fukuoka-shi.temperature.current 29.88 1436683931

stats.fukuoka-shi.humidity.current 58 1436683931

stats.fukuoka-shi.temperature-humidity_index.current 78.1478 1436683931

stats.tokyo.temperature.current 31.76 1436683931

stats.tokyo.humidity.current 70 1436683931

stats.tokyo.temperature-humidity_index.current 82.883 1436683931

メトリクスは第一引数に都市名をカンマ区切りで並べることで複数の都市の温度と湿度を取得出来るようにした。

ということで、メトリクス収集用の Sensu Check の設定。

{
  "checks": {
    "metrics-weather": {
      "type": "metric",
      "handlers": ["graphite", "file"],
      "command": "open_weather_map.py fukuoka-shi,tokyo metrics",
      "interval": 300,
      "standalone": true
    }
  }
}

更に不快指数を判断してアラートを上げる為の Sensu Check 設定。

{
  "checks": {
    "check-temperature-humidity-index-fukuoka": {
      "handlers": ["file"],
      "command": "open_weather_map.py fukuoka-shi check",
      "interval": 300,
      "standalone": true
    }
  }
}

例えば、本日の状況

メトリクス

Grafana の Singlestat パネルにもしきい値を設定している。湿度は 60% 前後であるが気温が 30 度を超えており、不快指数は注意レベル。

チェック

Grafana 同様に不快指数は Warning となっている。

ということで…

参考

Weather API

今回利用した API 以外にも有償の API もあるようだ。

Objective-C – OpenWeatherMap API v2.5から天気を取得する – Qiita

API レスポンスの内容について参考にさせて頂きました。

最後に

OpenWeatherMap に感謝しつつ、気温、湿度、不快指数には注意してコマメな水分補給と休養で熱中症を予防しましょう。

元記事はこちら

Sensu 復習(2)〜 OpenWeatherMap と Sensu + Grafana で熱中症対策 〜