2015年6月21日日曜日

サイネージ端末としてのraspberry pi ( dashing ) 〜サードパーティウィジットの設定〜

前回までで、raspberry piにDashingをインストールし、標準で付いているWidgetを表示するところまで出来ました。

Dashingには、サードパーティによって作られている追加ウィジットがあり、以下のサイトにソースコードが公開されています。
https://github.com/Shopify/dashing/wiki/Additional-Widgets

この中から今回は天気を表示するWeatherと、写真のスライドショーを表示するSlide Showを使ってみます。

■Weather
YahooのWeather APIを使って、指定したロケーションの天気を取得し、表示するウィジットです。スクリプトのコードがgithubに公開されており、基本的には以下のURLに記載されている手順で設定できます。

上のURLに書いてある手順そのままですが、参考までにやったことを書いておきます。

まず依存関係の準備として、~/dashboard/Gemfileファイルに、以下の一行を追加します。

gem 'xml-simple'

そして、$bundle installを実行しておきます。

次に、widgetsフォルダにweatherフォルダを作成し、以下の3つのファイルを配置します。

pi@raspberrypi ~/dashboard/widgets/weather $ ls
weather.coffee  weather.html  weather.scss

次に、jobsフォルダにweather.rbファイルを配置します。
そして、天気を表示させたいロケーションのwoe_idというものを調べます。以下のサイトでOsaka, Japanを検索すると、先頭に15015370と表示されたので、これを使うことにします。

weather.rbファイルを編集し、最初から書かれてあるwoe_idをコメントアウトし、
woe_id = 15015370を追記します。

ついでに、Fahrenheit表示ではなくCelcius表示を指定するために、format = 'c'としておきます。

pi@raspberrypi ~/dashboard_project/jobs $ more weather.rb 
require 'net/http'
require 'xmlsimple'

# Get a WOEID (Where On Earth ID)
# for your location from here:
# http://woeid.rosselliot.co.nz/
#woe_id = 3369
woe_id = 15015370

# Temerature format:
# 'c' for Celcius
# 'f' for Fahrenheit
format = 'c'

以下 略

--
ウィジットの表示の際、バックグラウンドに天気のアイコンを表示するために、Climacons Webfontというものを使っているようです。

以下のサイトからダウンロードし、 .eot, .ttf,  .woff ファイルを~/dashboard/assets/fonts フォルダに配置します。

pi@raspberrypi ~/dashboard_project/assets/fonts $ ls
climacons-webfont.eot   fontawesome-webfont.eot  fontawesome-webfont.woff
climacons-webfont.ttf   fontawesome-webfont.svg
climacons-webfont.woff  fontawesome-webfont.ttf

ここまでやってから、以下のweather.erbファイルを作るとwidgetを表示することができます。

pi@raspberrypi ~/dashboard/dashboards $ more weather.erb 
<% content_for :title do %>My weather dashboard<% end %>
<div class="gridster">
  <ul>
  <li data-row="1" data-col="1" data-sizex="1" data-sizey="1">
      <div data-id="weather" data-view="Weather"></div>
    </li>
  </ul>
</div>


■Slide Show
ローカルにおいてある写真を一定間隔でランダムに表示する、いわゆるスライドショーを実現するウィジットです。以下のURLにコードが公開されていますが、説明のなかのリンクが切れていたりするところがあり、うまく動かすまでに結構時間がかかりました。

ソースを見るとこのスクリプトで実現していることは、主に2つあります。一つ目は、あるフォルダに格納した画像ファイルを、毎日5:00と18:00にチェックし、Widgetに適したサイズにリサイズして、SlideShow用のフォルダにコピーすること。2つ目はSlideShow用のフォルダの中からランダムに画像を選択し、20秒ごとに切り替えてスライドショー表示することです。なお、今回は、一つ目のリサイズする機能は不要だったので、すべてコメントアウトしています。

まず、SlideShowウィジットの設定ファイルとして、assetsフォルダにconfigフォルダを作成し、slide_show_setttings.jsonファイルを配置します。

pi@raspberrypi ~/dashboard_project/assets/config $ more slide_show_settings.json{"SlideShow":{"directory":"/home/pi/dashboard/assets/images/slide_show/S
lideShow","pattern":"**/*.{jpg,JPG,gif,GIF,png,PNG}","maxImageSize":[1920,1080],
"subDirectoryExceptions":["very bad party","personal employee pictures","somethi
ng very private"]}}

この設定ファイルにより、スライドショー用の写真は、
/home/pi/dashboard/assets/images/slide_show/SlideShowフォルダに格納されているjpegファイル、GIFファイル、PNGファイルである
という設定をしています。(もともとはリサイズをかける前の画像ファイルを格納するフォルダの指定だと思います。)

次にスクリプトの作者は、標準ウィジットであるImageウィジットを、少し修正しているようなのですが、そのリンクが切れています。同じ作者の以下のサイトがその切れているリンク先だと推測されますので、ここを参考にImageウィジットを置き換えます。

widgets/imageフォルダにある、image.coffee, image.html, image.scssの3つのファイルを上のURLで公開されている内容に置き換えます。ただし、公開されているソースコードそのままではうまく動かなかったので、htmlファイル等に少し修正を加えています。coffeeスクリプトとか本質的なところは全く理解していないので、修正の仕方は美しくないかもしれません。

pi@raspberrypi ~/dashboard/widgets/image $ more image.coffee
class Dashing.Image extends Dashing.Widget

  ready: ->
    # This is fired when the widget is done being rendered
    @handleSize()

  onData: (data) ->
    # Handle incoming data
    # You can access the html node of this widget with `@node`
    # Example: $(@node).fadeOut().fadeIn() will make the node flash each time data comes in.
    @handleSize()

  handleSize: ->
    if !$(@node).data('width') && !$(@node).data('height') && @get('image_width') && @get('image_height
')
      $(@node).fadeOut()
      img = $(@node).find('img')
      paRatio = img.parent().width() / img.parent().height()
      if @get('image_width') >= @get('image_height')
        ratio = @get('image_width') / @get('image_height')
        img.width(img.parent().width() * ratio/paRatio)
      else
        ratio = @get('image_height') / @get('image_width')
        img.height(img.parent().height() * ratio/paRatio)
      $(@node).fadeIn()
---
pi@raspberrypi ~/dashboard/widgets/image $ more image.html
<img data-bind-src="image | prepend '/assets'" data-bind-width="width" data-bind-height="height"/>
<div data-bind="image"> </>
---
pi@raspberrypi ~/dashboard/widgets/image $ more image.scss
// ----------------------------------------------------------------------------
// Sass declarations
// ----------------------------------------------------------------------------
$background-color:  rgba(255,255,255,0);

// ----------------------------------------------------------------------------
// Widget-image styles
// ----------------------------------------------------------------------------
.widget-image {

  background-color: $background-color;

}
---

後にスライドショーウィジットの動作の根幹であるslide_show.rbファイルをjobsフォルダに配置します。少し長くなりますが、動作したコードをそのまま記載します。途中デバッグ用に挿入したコードもコメントで残っていますし、resize_imagesの関数も結局呼んでないので削除してよいのですが、そのままです。とりあえず、動くようになったということで。

pi@raspberrypi ~/dashboard/jobs $ more slide_show.rb 
require 'net/http'
require 'rmagick'

class SlideShow
  SETTINGS_FILE = "assets/config/slide_show_settings.json"
  CURRENT_DIR = Dir.pwd
  DEBUG = 0

  def debug
    DEBUG
  end

  # function to validate json
  def valid_json? (json)
    JSON.parse(json)
    return true
  rescue JSON::ParserError
    return false
  end

  def get_settings
     str = IO.read(SETTINGS_FILE)
     return [] if not str or str.empty? or not valid_json?(str)
     JSON.parse(str)
  end

  def get_dir_file_list(directory, pattern, exceptionDirs = [])
    Dir[directory+'/'+pattern].delete_if { |x| exceptionDirs.any? { |d| x =~ /#{d}/ } }
  end

  def resize_images(files, widget, directory, maxImageSize)
    return if not files or files.length == 0
    files[0..100].each do |f|
      newFile = f.sub directory, CURRENT_DIR+"/assets/images/slide_show/#{widget}"
      next if File.exists?(newFile)
      FileUtils.mkdir_p File.dirname(newFile)
      img = Magick::Image.read(f).first
      newImg = img.change_geometry(maxImageSize[0].to_s+'x'+maxImageSize[1].to_s) { |cols, rows, i|
        newImg = i.resize(cols, rows)
        newImg.write(newFile)
      }
    end
  end

#  def get_file_list(widget, settings)
#    (get_dir_file_list(CURRENT_DIR+"/assets/images/slide_show/#{widget}", settings['pattern'], setting
s['subDirectoryExceptions']).shuffle)[0..30]
#  end
  
  
  def make_web_friendly(widget, directory, file)
    file.sub directory, "/slide_show/#{widget}"
  end
end

@SS = SlideShow.new()

#SCHEDULER.cron '* 5/18 * * *' do |job|
#  settings = @SS.get_settings
#  settings.each do |widget, project|
#    puts DateTime.now.to_s+" Resizing images for #{widget}, #{project.to_s}"
#    @SS.resize_images(
#      @SS.get_dir_file_list(project['directory'], project['pattern'], project['subDirectoryExceptions'
]).shuffle,
#      widget,
#      project['directory'],
#      project['maxImageSize'])
#  end
#end

@files = nil
SCHEDULER.every '10s', :first_in => 0 do |job|
  settings = @SS.get_settings
  settings.each do |widget, project|

#puts DateTime.now.to_s+" Reading images for #{widget}, #{project.to_s}"
    
  @files = { widget => @SS.get_dir_file_list(project['directory'], project['pattern'], project['subDire
ctoryExceptions']) } if not @files or not @files[widget] or @files[widget].length == 0

#    @files = { widget => @SS.get_file_list(widget, project) } if not @files or not @files[widget] or @
files[widget].length == 0

  file = @files[widget].shuffle!.first

  puts DateTime.now.to_s+" Working with #{widget}, #{project.to_s}, #{file}" if @SS.debug > 0

#  puts "make_web_friendly file = " + @SS.make_web_friendly(widget, Dir.pwd+"/assets/images/slide_show/
#{widget}", @files[widget][rand(@files.length)]) if @SS.debug > 0

    send_event(widget, { 
#     image: "/slide_show/#{widget}/2014-12-23_11-31-40-825x510.jpg"
     image: @SS.make_web_friendly(widget, Dir.pwd+"/assets/images/slide_show/#{widget}", @files[widget]
[rand(@files.length)])
     })
  end
end
---

ここまでやってから、以下のslideshow.erbファイルを作るとwidgetを表示することができ、写真とその画像ファイルのファイルパスがスライドショー表示されます。

pi@raspberrypi ~/dashboard/dashboards $ more slideshow.erb 
<% content_for :title do %>My slide show dashboard<% end %>
<div class="gridster">
  <ul>

<li data-row="1" data-col="1" data-sizex="4" data-sizey="2">
  <div data-id="SlideShow" data-view="Image" data-width="100%"></div>
</li>

  </ul>
</div>

■すべてのウィジットを1080pのディスプレイに表示
最後の仕上げに、これまで作成してきたウィジットをすべて並べて、1080pのディスプレイに表示するための1080p.erbという設定ファイルを作りました。デフォルトで準備されているサンプルファイルであるsampletv.erbを参考にしました。


pi@raspberrypi ~/dashboard/dashboards $ more 1080p.erb 
<script type='text/javascript'>
$(function() {
  Dashing.widget_base_dimensions = [370, 340]
  Dashing.numColumns = 5
});
</script>

<% content_for :title do %>My 1080p dashboard<% end %>
<div class="gridster">
  <ul>

    <li data-row="1" data-col="1" data-sizex="1" data-sizey="1">
      <div data-id="my_clock" data-view="Clock"></div>
      <i class="icon-time icon-background"></i>
    </li>

   <li data-row="1" data-col="2" data-sizex="1" data-sizey="1">
      <div data-id="weather" data-view="Weather"></div>
    </li>

    <li data-row="1" data-col="3" data-sizex="2" data-sizey="1">
      <div data-id="my_event" data-view="Text" data-title="本日のイベント" data-text="今日は18:30から懇親会" data-moreinfo=" "></div>
    </li>

    <li data-row="1" data-col="5" data-sizex="1" data-sizey="1">
      <div data-id="my_number" data-view="Number" data-title="数値実績(前年比)" style="background-color:#96bf48;"></div>
      <i class="icon-heart icon-background"></i>
    </li>

    <li data-row="2" data-col="1" data-sizex="2" data-sizey="1">
      <div data-id="twitter_mentions" data-view="Comments" style="background-color:#ff9618;" data-moreinfo="Tweets by @copywritebot"></div>
      <i class="icon-twitter icon-background"></i>
    </li>

   <li data-row="3" data-col="1" data-sizex="2" data-sizey="1">
      <div data-id="twitter_mentions2" data-view="Comments" style="background-color:#ff9618;" data-moreinfo="Tweets by @kando_lyrics"></div>
      <i class="icon-twitter icon-background"></i>
    </li>

<li data-row="2" data-col="3" data-sizex="3" data-sizey="2">
     <div data-id="SlideShow" data-view="Image" data-width="100%"></div>
   </li>

  </ul>
</div>
---

インストールの際、chromiumのキオスクモードでの立ち上げ用に作ったスクリプトに、今回作成した1080pを設定し、実行するとraspberry piのHDMI出力に接続された地デジテレビにキレイにウィジットが配置されて表示されました。

pi@raspberrypi ~ $ more brstart.sh 
#!/bin/sh
export DISPLAY=:0.0

chromium --kiosk --ignore-certificate-errors --dsiable-sync --disable-restore-se
ssion-state http://localhost:3030/1080p




これで、とりあえずDashing on raspberry piは完成です。

0 件のコメント:

コメントを投稿