にゃみかんてっくろぐ

猫か百合を見守る壁になりたい

Raspberry Piでダッシュボードを作る(4) -天気予報-

f:id:no_clock:20171016214958j:plain

Raspberry Piとミニディスプレイ,各種センサを使ってダッシュボードを作ります.


今回のゴール

  • 天気予報を表示させる.

体調を崩して少し間が空いてしまいました.デザインが出来たところで天気予報を表示させていきます.

livedoor Weather Hacks

livedoorが,Weather Hacksと題して天気予報などのデータを提供しています.商用利用はできませんが,事例のところで九州の方言が楽しめるばい(方言).

ここでは,そのうちのお天気Webサービスを使います.レスポンスがJSON形式で返却されるため,取り扱いが非常に簡単です.

URLやパラメタの確認

URLやパラメタの地域(city)IDについては,お天気Webサービスのページに記述がありますのでよく読みましょう.例えば東京のidは130010です.

http://weather.livedoor.com/forecast/webservice/json/v1?city=130010

f:id:no_clock:20171031222612p:plain

PCのブラウザで開くと,JSONと呼ばれる形式でデータが確認できます.

スマートフォンのブラウザ等ではJSONハイジャック防止のため表示できない場合があります.

irb(Interactive Ruby)でデータを解析

では,ダッシュボードへ表示する準備です.

いきなりダッシュボードに載せる前に,rubyを対話型実行できるirbを使って,データを解析してみましょう.

$ irb
irb(main):001:0>

irbを起動すると,入力待ちの状態になります.1行ごとに入力した内容が実行され,結果が分かるという優れものです(exitで終了します).

irb(main):001:0> require 'uri'
=> true

require 'uri'と入力して実行すると,trueという結果が返却されました.

このままじゃんじゃん入力します.

irb(main):002:0> require 'net/http'
=> true
irb(main):003:0> require 'yaml'
=> true
irb(main):004:0> data = Net::HTTP.get(URI.parse("http://weather.livedoor.com/forecast/webservice/json/v1?city=130010"))
=> "{\"pinpointLocations\":[{\"link\":\"http://weather.livedoor.com/area/forecast/1310100\",\"name\":\"\\u5343\\u4ee3\\u7530\\u533a\"},
(省略)
irb(main):005:0> json = YAML.load(data)
=> {"pinpointLocations"=>[{"link"=>"http://weather.livedoor.com/area/forecast/1310100", "name"=>"千代田区"},
(省略)

お天気WebサービスJSONを解釈するところまで進めました.なんだか読めるモノが出てきたと思います.

引き続きお天気Webサービスの仕様を見ていると,forecastsというプロパティに天気予報が入っていることが分かります.ちょっと見てみます.

irb(main):006:0> json["forecasts"]
=> [{"dateLabel"=>"今日", "telop"=>"曇時々晴", "date"=>"2017-10-31", "temperature"=>{"min"=>nil, "max"=>nil}, "image"=>{"width"=>50, "url"=>"http://weather.livedoor.com/img/icon/9.gif", "title"=>"曇時々晴", "height"=>31}},
(省略)

おっ,天気予報です.dateが日付,telopが予報,temperatureが予想気温(夜に取得したのでその日の予想情報は無い)のようです.

だいたい取得方法がわかったので,ダッシュボードに載せていきましょう.

いざダッシュボードへ

ダッシュボードアプリの views/index.erb を編集します.前回のデザインでカードを4個置いたので,そのうち1個を使いましょう.

          <div class="card">
            <h4 class="card-header">天気予報</h4>
            <div class="card-body">
              <%
                # 天気予報取得処理
                require 'uri'
                require 'net/http'
                require 'yaml'
                data = Net::HTTP.get(URI.parse("http://weather.livedoor.com/forecast/webservice/json/v1?city=130010"))
                json = YAML.load(data)

                # 今日と明日の天気
                today = json["forecasts"][0]["telop"]
                tomorrow = json["forecasts"][1]["telop"]
               %>
              <p class="card-text">
                <strong>今日</strong>: <%= today %><br>
                <strong>明日</strong>: <%= tomorrow %>
              </p>
            </div>
          </div>

補足 erb(embedded Ruby)テンプレートでは,<% ~ %>の間にRubyスクリプトを記述したり,<%= ~ %>の間にページに出力したい変数や式を記述することで,出力内容を柔軟に変えることができます.それ以外の部分は,そのまま出力されます.

すかさず http://localhost:4567/ にアクセスします.天気予報が表示されました.

f:id:no_clock:20171031225409p:plain

もうちょっと,ダッシュボードへ

物足りないのでちょっと表示を良くしましょう.スクリプトの細かい説明は省きます.

          <div class="card">
            <h4 class="card-header">天気予報</h4>
            <div class="card-body">
              <%
                # 天気予報取得処理
                require 'uri'
                require 'net/http'
                require 'yaml'
                data = Net::HTTP.get(URI.parse("http://weather.livedoor.com/forecast/webservice/json/v1?city=130010"))
                json = YAML.load(data)

                # 今日と明日の天気
                today = json["forecasts"][0]["telop"]
                tomorrow = json["forecasts"][1]["telop"]
               %>
              <p class="card-text">
                <% json["forecasts"].each { |forecast| %>
                  <strong><%= forecast["dateLabel"] %></strong>:
                  <%= forecast["telop"] %>
                  <% if forecast["temperature"]["min"] %>
                    <span class="text-info">最低<%= forecast["temperature"]["min"]["celsius"] %>℃</span>
                  <% end %>
                  <% if forecast["temperature"]["max"] %>
                    <span class="text-danger">最高<%= forecast["temperature"]["max"]["celsius"] %>℃</span>
                  <% end %>
                  <br>
                <% } %>
              </p>
            </div>
          </div>

気温も表示されるようになったと思います.

f:id:no_clock:20171031225410p:plain


ページを表示するたびにお天気Webサービスへアクセスするので,更新しすぎに注意しましょう.

今回はここまで.