6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

天気予報を定期的にTwitterにつぶやく(Elixir)

Last updated at Posted at 2020-08-16

はじめに

  • 毎朝、私のNervesアプリ on Raspberry Pi 2がTwitterへ日本のどこかの地点の天気予報をつぶやくようにしていました
  • 2020/8/1からは投稿ができていませんでした
  • Livedoor Weather Web Serviceを利用させていただいておりましたが、こちらの通り、2020年7月31日(金)14:00 で終了となっておりました
  • これは2020/8/16(日) NervesJP #10 ”やってみた”回 への私の報告です

OpenWeather

  • こちらを使うことにしました
  • いつ取得していたのかは忘れてしまいましたが、ユーザー登録とAPIキーの取得は済んでいたのでそれを使います
  • One Call APIを使います
    • "https://api.openweathermap.org/data/2.5/onecall?lat=#{lat}&lon=#{lon}&lang=ja&units=metric&exclude=current,minutely,hourly&appid=#{appid}" にHTTP GETすると以下のようなJSONが返ってきます
      • 以下はJsonをMapに変換しています
%{
  "daily" => [
    %{
      "clouds" => 75,
      "dew_point" => 24.79,
      "dt" => 1597629600,
      "feels_like" => %{
        "day" => 39.81,
        "eve" => 37.77,
        "morn" => 39.81,
        "night" => 35
      },
      "humidity" => 56,
      "pop" => 0.81,
      "pressure" => 1008, 
      "rain" => 10.79,
      "sunrise" => 1597607962,
      "sunset" => 1597656497,
      "temp" => %{
        "day" => 34.88,
        "eve" => 33.83,
        "max" => 34.88,
        "min" => 30.65,
        "morn" => 34.88,
        "night" => 30.65
      },
      "uvi" => 10.44,
      "weather" => [
        %{
          "description" => "適度な雨",
          "icon" => "10d",
          "id" => 501,
          "main" => "Rain"
        }
      ],
      "wind_deg" => 138,
      "wind_speed" => 1.94
    },
    %{
      "clouds" => 92,
      "dew_point" => 20.43,
      "dt" => 1597716000,
      "feels_like" => %{
        "day" => 35.26,
        "eve" => 32.22,
        "morn" => 31.63,
        "night" => 31.69
      },
      "humidity" => 44,
      "pop" => 0.88,
      "pressure" => 1011,
      "rain" => 2.69,
      "sunrise" => 1597694411,
      "sunset" => 1597742824,
      "temp" => %{
        "day" => 34.12,
        "eve" => 31.05,
        "max" => 34.12,
        "min" => 28.23,
        "morn" => 28.28,
        "night" => 28.23
      },
      "uvi" => 11.12,
      "weather" => [
        %{
          "description" => "小雨",
          "icon" => "10d",
          "id" => 500,
          "main" => "Rain"
        }
      ],
      "wind_deg" => 94,
      "wind_speed" => 3.72
    },
    %{
      "clouds" => 76,
      "dew_point" => 20.66,
      "dt" => 1597802400,
      "feels_like" => %{
        "day" => 38.29,
        "eve" => 34.73,
        "morn" => 31.24,
        "night" => 32.19
      },
      "humidity" => 43,
      "pop" => 0,
      "pressure" => 1011,
      "sunrise" => 1597780859,
      "sunset" => 1597829149,
      "temp" => %{
        "day" => 35.07,
        "eve" => 33.76,
        "max" => 37.26,
        "min" => 27.42,
        "morn" => 27.42,
        "night" => 29.64
      },
      "uvi" => 9.9,
      "weather" => [
        %{
          "description" => "曇りがち",
          "icon" => "04d",
          "id" => 803,
          "main" => "Clouds"
        }
      ],
      "wind_deg" => 186,
      "wind_speed" => 1.08
    },
    %{
      "clouds" => 100,
      "dew_point" => 19.46,
      "dt" => 1597888800,
      "feels_like" => %{
        "day" => 38.65,
        "eve" => 34.57,
        "morn" => 32.67,
        "night" => 33.34
      },
      "humidity" => 36,
      "pop" => 0.04,
      "pressure" => 1012,
      "sunrise" => 1597867307,
      "sunset" => 1597915474,
      "temp" => %{
        "day" => 36.96,
        "eve" => 34.33,
        "max" => 37.99,
        "min" => 28.83,
        "morn" => 28.83,
        "night" => 30
      },
      "uvi" => 10.18,
      "weather" => [
        %{
          "description" => "厚い雲",
          "icon" => "04d",
          "id" => 804,
          "main" => "Clouds"
        }
      ],
      "wind_deg" => 199,
      "wind_speed" => 2.46
    },
    %{
      "clouds" => 0,
      "dew_point" => 19.85,
      "dt" => 1597975200,
      "feels_like" => %{
        "day" => 39.83,
        "eve" => 35.75,
        "morn" => 33.52,
        "night" => 34.14
      },
      "humidity" => 35,
      "pop" => 0.46,
      "pressure" => 1013,
      "rain" => 1.81,
      "sunrise" => 1597953756,
      "sunset" => 1598001798,
      "temp" => %{
        "day" => 37.8,
        "eve" => 34.17,
        "max" => 37.8,
        "min" => 29.16,
        "morn" => 29.16,
        "night" => 29.86
      },
      "uvi" => 10.1,
      "weather" => [
        %{
          "description" => "小雨",
          "icon" => "10d",
          "id" => 500,
          "main" => "Rain"
        }
      ],
      "wind_deg" => 134,
      "wind_speed" => 2.16
    },
    %{
      "clouds" => 88,
      "dew_point" => 20.9,
      "dt" => 1598061600,
      "feels_like" => %{
        "day" => 36.82,
        "eve" => 33.28,
        "morn" => 32.27,
        "night" => 33.27
      },
      "humidity" => 45,
      "pop" => 0.73,
      "pressure" => 1012,
      "rain" => 4.02,
      "sunrise" => 1598040204,
      "sunset" => 1598088120,
      "temp" => %{
        "day" => 34.54,
        "eve" => 31.43,
        "max" => 34.54,
        "min" => 28,
        "morn" => 28,
        "night" => 29.24
      },
      "uvi" => 9.1,
      "weather" => [
        %{
          "description" => "適度な雨",
          "icon" => "10d",
          "id" => 501,
          "main" => "Rain"
        }
      ],
      "wind_deg" => 88,
      "wind_speed" => 2.62
    },
    %{
      "clouds" => 94,
      "dew_point" => 16.87,
      "dt" => 1598148000,
      "feels_like" => %{
        "day" => 32.82,
        "eve" => 33.91,
        "morn" => 31.92,
        "night" => 32.85
      },
      "humidity" => 37,
      "pop" => 0.86,
      "pressure" => 1009,
      "rain" => 9.2,
      "sunrise" => 1598126652,
      "sunset" => 1598174442,
      "temp" => %{
        "day" => 33.14,
        "eve" => 31.49,
        "max" => 33.14,
        "min" => 28.38,
        "morn" => 28.38,
        "night" => 29.02
      },
      "uvi" => 9.02,
      "weather" => [
        %{
          "description" => "適度な雨",
          "icon" => "10d",
          "id" => 501,
          "main" => "Rain"
        }
      ], 
      "wind_deg" => 193,
      "wind_speed" => 3.55
    },
    %{
      "clouds" => 28,
      "dew_point" => 18.94,
      "dt" => 1598234400,
      "feels_like" => %{
        "day" => 40.32,
        "eve" => 33.44,
        "morn" => 31.95,
        "night" => 32.95
      },
      "humidity" => 33,
      "pop" => 0.51,
      "pressure" => 1007,
      "rain" => 1.01,
      "sunrise" => 1598213099,
      "sunset" => 1598260763,
      "temp" => %{
        "day" => 37.7,
        "eve" => 32.03,
        "max" => 37.7,
        "min" => 28.18,
        "morn" => 28.18,
        "night" => 29.07
      },
      "uvi" => 10.05,
      "weather" => [
        %{
          "description" => "小雨",
          "icon" => "10d",
          "id" => 500,
          "main" => "Rain"
        }
      ],
      "wind_deg" => 164,
      "wind_speed" => 0.65
    }
  ],
  "lat" => 36.2,
  "lon" => 140.1,
  "timezone" => "Asia/Tokyo",
  "timezone_offset" => 32400
}
$ ls rootfs_overlay/usr/local/share 
city.list.json
  • たとえばこんな感じにプロジェクトに配置しておくと、Nervesアプリからは/usr/local/share/city.list.jsonというパスでアクセスできます
File.read!("/usr/local/share/city.list.json")
|> Jason.decode!()
|> Enum.filter(fn %{"country" => country} -> country == "JP" end)

キモのソースコード

lib/weather/forecast.ex
defmodule Weather.Forecast do
  @api_key Application.get_env(:hello_nerves, :open_weather_api_key)
  @city_list_json "/usr/local/share/city.list.json"

  def run(%{"coord" => %{"lat" => lat, "lon" => lon}, "id" => city_id}) do
    %{
      "name" => name
    } =
      current(city_id)
      |> HTTPoison.get!()
      |> Map.get(:body)
      |> Jason.decode!()

    %{"daily" => dailies} =
      onecall(lat, lon)
      |> HTTPoison.get!()
      |> Map.get(:body)
      |> Jason.decode!()

    description =
      dailies |> Enum.at(0) |> Map.get("weather") |> Enum.at(0) |> Map.get("description")

    temp_max = dailies |> Enum.at(0) |> Map.get("temp") |> Map.get("max")
    temp_min = dailies |> Enum.at(0) |> Map.get("temp") |> Map.get("min")

    "#{name}の天気は、#{description}です。\n最高気温は#{temp_max}℃です。最低気温は#{temp_min}℃です。\n\n#{i_use_nerves()}"
  end

  def run do
    cities()
    |> Enum.random()
    |> run()
  end

  defp current(city_id) do
    "http://api.openweathermap.org/data/2.5/weather?id=#{city_id}&lang=ja&units=metric&appid=#{
      @api_key
    }"
  end

  defp onecall(lat, lon) do
    "https://api.openweathermap.org/data/2.5/onecall?lat=#{lat}&lon=#{lon}&lang=ja&units=metric&exclude=current,minutely,hourly&appid=#{
      @api_key
    }"
  end

  defp i_use_nerves do
    if Application.get_env(:hello_nerves, :target) != :host do
      "I use Nerves. I like it!"
    else
      ""
    end
  end

  defp cities do
    path =
      if Application.get_env(:hello_nerves, :target) != :host,
        do: @city_list_json,
        else: "rootfs_overlay/#{@city_list_json}"

    File.read!(path)
    |> Jason.decode!()
    |> Enum.filter(fn %{"country" => country} -> country == "JP" end)
  end
end

その他利用しているもの

  • HTTPoison
    • HTTPクライアント
  • ExTwitter
    • Twitterクライアントライブラリ
  • Quantum
    • Cronライクなスケジューラ

Wrapping Up

  • rootfs_overlay配下にあらかじめ格納しておきたいファイルやバイナリなどをおいておくと、Nervesアプリから読みだせます :lgtm::lgtm::lgtm:
  • Enjoy Elixir!
6
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?