Raspberry PIでデジタルサイネージを作ろう。
/boot/config.txt に設定を追加
/boot/config.txt
display_rotate=1 #追加することにより縦方向表示になる。
デジタルサイネージ開発中 pic.twitter.com/x0AcrZoqDh
— utaca.rich (@RichUtaka) June 8, 2019
#プログラムからChromeを起動制御するためには、Selenium Chrome webdriverが必要です。
$ mkdir chrome
$ cd chrome
# sudo pip install selenium
# wget http://launchpadlibrarian.net/361669488/chromium-chromedriver_65.0.3325.181-0ubuntu0.14.04.1_armhf.deb
# sudo dpkg -i chromium-chromedriver_65.0.3325.181-0ubuntu0.14.04.1_armhf.deb
visual studio 2017を使ってcoding 参照
テンプレートを使ってさくっと出来上がり
static/contには、表示するコンテント
.
├── DigitalSignage.pyproj
├── app.py
├── comm.py
├── routes.py
├── static
│ ├── cont
│ │ ├── Raspberries.jpg
│ │ ├── Raspberries1.jpg
│ │ ├── Raspberries2.jpg
│ │ ├── Raspberries3.jpg
│ │ └── Raspberries4.jpg
│ ├── content
│ │ ├── bootstrap.css
│ │ ├── bootstrap.min.css
│ │ └── site.css
│ ├── favicon.ico
│ ├── fonts
│ │ ├── glyphicons-halflings-regular.eot
│ │ ├── glyphicons-halflings-regular.svg
│ │ ├── glyphicons-halflings-regular.ttf
│ │ └── glyphicons-halflings-regular.woff
│ └── scripts
│ ├── _references.js
│ ├── bootstrap.js
│ ├── bootstrap.min.js
│ ├── jquery-1.10.2.intellisense.js
│ ├── jquery-1.10.2.js
│ ├── jquery-1.10.2.min.js
│ ├── jquery-1.10.2.min.map
│ ├── jquery.validate-vsdoc.js
│ ├── jquery.validate.js
│ ├── jquery.validate.min.js
│ ├── jquery.validate.unobtrusive.js
│ ├── jquery.validate.unobtrusive.min.js
│ ├── modernizr-2.6.2.js
│ ├── respond.js
│ └── respond.min.js
└── views
├── index.tpl
└── layout.tpl
6 directories, 35 files
ソースの中身は、github
app.py
"""
This script runs the application using a development server.
"""
import bottle,sys,os,time,threading
from selenium import webdriver
os.environ['DISPLAY']=':0' #HDMI出力設定
os.environ['SERVER_HOST']='0.0.0.0' #webserver 設定
os.environ['SERVER_PORT']='8890' #webserver port設定
# chrome 自動起動設定
import comm
comm.options = webdriver.ChromeOptions()
comm.options.add_argument("--kiosk")
comm.options.add_argument("--disable-infobars")
comm.browser = webdriver.Chrome(chrome_options=comm.options,executable_path="/usr/bin/chromedriver")
# routes contains the HTTP handlers for our server and must be imported.
import routes
if '--debug' in sys.argv[1:] or 'SERVER_DEBUG' in os.environ:
# Debug mode will enable more verbose output in the console window.
# It must be set at the beginning of the script.
bottle.debug(True)
def wsgi_app():
"""Returns the application to make available through wfastcgi. This is used
when the site is published to Microsoft Azure."""
return bottle.default_app()
if __name__ == '__main__':
PROJECT_ROOT = os.path.abspath(os.path.dirname(__file__))
STATIC_ROOT = os.path.join(PROJECT_ROOT, 'static').replace('\\', '/')
HOST = os.environ.get('SERVER_HOST', 'localhost')
try:
PORT = int(os.environ.get('SERVER_PORT', '5555'))
except ValueError:
PORT = 5555
@bottle.route('/static/<filepath:path>')
def server_static(filepath):
"""Handler for static files, used with the development server.
When running under a production server such as IIS or Apache,
the server should be configured to serve the static files."""
return bottle.static_file(filepath, root=STATIC_ROOT)
def geturl():
time.sleep(1)
comm.browser.get('http://%s:%s/'%(HOST,PORT))
time.sleep(1)
comm.browser.execute_script("document.body.style.zoom = '75%'")
th = threading.Thread(target=geturl)
th.start()
# Starts a local test server.
bottle.run(server='wsgiref', host=HOST, port=PORT)
関数間共有変数(selenium用)
comm.py
def init():
global browser,options
routes.py
"""
Routes and views for the bottle application.
"""
from bottle import route, view
from datetime import datetime
import os
@route('/')
@route('/home')
@view('index')
def home():
"""Renders the home page."""
lst=os.listdir("./static/cont") #/staic/cont配下にコンテンツ用画像ファイルがある
return dict(
year=datetime.now().year,pict = lst
)
# テンプレートにある以下のファイルは、削除
上部には、時計と天気をウィジェット表示
時計ウィジェット
天気ウィジェット
index.tpl
% rebase('layout.tpl', title='Home Page', year=year)
<div class="jumbotron">
<div class="row">
<!--時計の表示-->
<div class="col-md-4">
<div class="cleanslate w24tz-current-time w24tz-large" style="display: inline-block !important; visibility: hidden !important; min-width:300px !important; min-height:145px !important;"><p><a href="//24timezones.com/ja_time/japan_chiba_clock.php" style="text-decoration: none" class="clock24" id="tz24-1559793488-c213600-eyJob3VydHlwZSI6MTIsInNob3dkYXRlIjoiMSIsInNob3dzZWNvbmRzIjoiMSIsImNvbnRhaW5lcl9pZCI6ImNsb2NrX2Jsb2NrX2NiNWNmODhmNTA3NGNmNSIsInR5cGUiOiJkYiIsImxhbmciOiJqYSJ9" title="千葉 世界時計" target="_blank" rel="nofollow">千葉の現在の時刻</a></p><div id="clock_block_cb5cf88f5074cf5"></div></div>
<script type="text/javascript" src="//w.24timezones.com/l.js" async></script>
</div>
<!--天気予報の表示-->
<div class="col-md-4">
<!-- weather widget start --><div id="m-booked-weather-bl250-22795"> <div class="booked-wzs-250-175 weather-customize" style="background-color:#707d8a;width:250px;" id="width1"> <div class="booked-wzs-250-175_in"> <div class="booked-wzs-250-175-data"> <div class="booked-wzs-250-175-left-img wrz-03"> <a target="_blank" href="https://www.booked.net/"> <img src="//s.bookcdn.com/images/letter/logo.gif" alt="booked.net" /> </a> </div> <div class="booked-wzs-250-175-right"> <div class="booked-wzs-day-deck"> <div class="booked-wzs-day-val"> <div class="booked-wzs-day-number"><span class="plus">+</span>19</div> <div class="booked-wzs-day-dergee"> <div class="booked-wzs-day-dergee-val">°</div> <div class="booked-wzs-day-dergee-name">C</div> </div> </div> <div class="booked-wzs-day"> <div class="booked-wzs-day-d">H: <span class="plus">+</span>28°</div> <div class="booked-wzs-day-n">L: <span class="plus">+</span>20°</div> </div> </div> <div class="booked-wzs-250-175-info"> <div class="booked-wzs-250-175-city">千葉市 </div> <div class="booked-wzs-250-175-date">木曜日, 06 6月</div> <div class="booked-wzs-left"> <span class="booked-wzs-bottom-l">週間天気予報を見る</span> </div> </div> </div> </div> <a target="_blank" href="https://booked.jp/weather/chiba-19689"> <table cellpadding="0" cellspacing="0" class="booked-wzs-table-250"> <tr> <td>水</td> <td>金</td> <td>土</td> <td>日</td> <td>月</td> <td>火</td> </tr> <tr> <td class="week-day-ico"><div class="wrz-sml wrzs-01"></div></td> <td class="week-day-ico"><div class="wrz-sml wrzs-18"></div></td> <td class="week-day-ico"><div class="wrz-sml wrzs-18"></div></td> <td class="week-day-ico"><div class="wrz-sml wrzs-18"></div></td> <td class="week-day-ico"><div class="wrz-sml wrzs-03"></div></td> <td class="week-day-ico"><div class="wrz-sml wrzs-18"></div></td> </tr> <tr> <td class="week-day-val"><span class="plus">+</span>19°</td> <td class="week-day-val"><span class="plus">+</span>24°</td> <td class="week-day-val"><span class="plus">+</span>27°</td> <td class="week-day-val"><span class="plus">+</span>22°</td> <td class="week-day-val"><span class="plus">+</span>24°</td> <td class="week-day-val"><span class="plus">+</span>25°</td> </tr> <tr> <td class="week-day-val"><span class="plus">+</span>19°</td> <td class="week-day-val"><span class="plus">+</span>21°</td> <td class="week-day-val"><span class="plus">+</span>21°</td> <td class="week-day-val"><span class="plus">+</span>21°</td> <td class="week-day-val"><span class="plus">+</span>21°</td> <td class="week-day-val"><span class="plus">+</span>21°</td> </tr> </table> </a> </div></div> </div><script type="text/javascript"> var css_file=document.createElement("link"); css_file.setAttribute("rel","stylesheet"); css_file.setAttribute("type","text/css"); css_file.setAttribute("href",'https://s.bookcdn.com/css/w/booked-wzs-widget-275.css?v=0.0.1'); document.getElementsByTagName("head")[0].appendChild(css_file); function setWidgetData(data) { if(typeof(data) != 'undefined' && data.results.length > 0) { for(var i = 0; i < data.results.length; ++i) { var objMainBlock = document.getElementById('m-booked-weather-bl250-22795'); if(objMainBlock !== null) { var copyBlock = document.getElementById('m-bookew-weather-copy-'+data.results[i].widget_type); objMainBlock.innerHTML = data.results[i].html_code; if(copyBlock !== null) objMainBlock.appendChild(copyBlock); } } } else { alert('data=undefined||data.results is empty'); } } </script> <script type="text/javascript" charset="UTF-8" src="https://widgets.booked.net/weather/info?action=get_weather_info&ver=6&cityID=19689&type=3&scode=124<id=3458&domid=587&anc_id=64726&cmetric=1&wlangID=16&color=707d8a&wwidth=250&header_color=ffffff&text_color=333333&link_color=08488D&border_form=1&footer_color=ffffff&footer_text_color=333333&transparent=0"></script><!-- weather widget end --></div>
</div>
<div class="col-md-4">
</div>
</div>
<!--サイネージの表示-->
<div class="container">
<div id="myCarousel" class="carousel slide" data-ride="carousel">
<ol class="carousel-indicators">
%for i,x in enumerate(pict):
%it="class='active'" if i==0 else ""
<li data-target="#myCarousel" data-slide-to="{{i}} {{it}}"></li>
%end
</ol>
<div class="carousel-inner">
%for i,x in enumerate(pict):
%it="active" if i==0 else ""
<div class="item {{it}}">
<img src="/static/cont/{{x}}" alt="{{x}}" style="width:100%;">
</div>
%end
</div>
<!-- Left and right controls -->
<a class="left carousel-control" href="#myCarousel" data-slide="prev">
<span class="glyphicon glyphicon-chevron-left"></span>
<span class="sr-only">Previous</span>
</a>
<a class="right carousel-control" href="#myCarousel" data-slide="next">
<span class="glyphicon glyphicon-chevron-right"></span>
<span class="sr-only">Next</span>
</a>
</div>
</div>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ title }} - Message Application</title>
<link rel="stylesheet" type="text/css" href="/static/content/bootstrap.min.css" />
<link rel="stylesheet" type="text/css" href="/static/content/site.css" />
<link rel="shortcut icon" type="image/x-icon" href="/static/favicon.ico" />
<script src="/static/scripts/modernizr-2.6.2.js"></script>
<script src="/static/scripts/jquery-1.10.2.js"></script>
<script src="/static/scripts/bootstrap.js"></script>
<script src="/static/scripts/respond.js"></script>
</head>
<body>
<div class="container body-content">
{{!base}}
</div>
</body>
</html>