はじめに
ちょっと使わずにいたRaspberryPiがあって、有効に使いたいなぁと思っていた。そこで、 よく天気予報が気になって、その都度、検索するのも面倒なので常にRaspberryPiに表示させておいて、それを見ることにするのがいいだろうと思い実装してみた。
材料
- 出版社/メーカー: RS Components Ltd (ソースマーキング及び販売 Umemoto LLP)
- メディア: エレクトロニクス
- この商品を含むブログ (12件) を見る
サインスマート 2.8インチ TFTモニタ lcd ディスプレイ モニター タッチパネル付き Raspberry Pi用
- 出版社/メーカー: サインスマート(SainSmart)
- メディア: Personal Computers
- この商品を含むブログを見る
PiTFTエンクロージャ (Raspberry Pi Model B用)
- 出版社/メーカー: スイッチサイエンス
- メディア: エレクトロニクス
- この商品を含むブログを見る
3M スコッチ クッションゴム 12.7×3.6mm 14粒 CC-05
- 出版社/メーカー: 3M(スリーエム)
- メディア: Tools & Hardware
- 購入: 10人 クリック: 90回
- この商品を含むブログを見る
すでに持っていた古いType Bに合わせて液晶などを購入したので、現状ならRaspberryPi2 に対応したのを購入したほうが良い。 それとエンクロージャーのバランスが悪かったり、滑ったりするので、ゴム足的なもので対応。
簡単な設計とか
天気予報のAPI
天気予報については個人では無料で使えるAPIが複数あったのだが、降水確率を提供しているところがなかった。 降水確率は傘を準備するかどうかを判断する材料になるので、必須と考えていた。 さらに調べていると
が見つかったので、こちらを利用することにした。 個人的に対応されているサイトのようなので、永続性は微妙だけれどもすぐに終わりそうな感じもないのでこちらを採用した。
UI
UIについては、調べたところ
があった。
自分の自由が効くように細かく作りこみたいと思っていたので、それができそうなpygameを使用して内容を表示させる方法を採用した。 同じようなことを考える人はいるようで、ベースはそちらを使わせてもらった。
プラグインとして、作成した。そのうち綺麗にしてforkしたのをgithubにあげておこうと思っているけど、してない。
UIと処理が混ざってちょっと気持ち悪さも感じながらもゴリゴリ座標を指定して、動くようにした。
ソース
ライセンスはGPLv3
screen.py
# -*- coding: utf-8 -*- import os import pygame import locale import re import json from time import strftime from displayscreen import PiInfoScreen from pygame.locals import * from datetime import datetime class myScreen(PiInfoScreen): refreshtime = 1 displaytime = 30 pluginname = "Clock" plugininfo = "Basic digital clock" def setPluginVariables(self): self.clockfont = os.path.join(self.plugindir, "resources", "SFDigitalReadout-Medium.ttf") self.takaofont = os.path.abspath("/usr/share/fonts/truetype/takao-gothic/TakaoPGothic.ttf") self.myfont = pygame.font.Font(self.clockfont, 200) self.myfontsmall = pygame.font.Font(self.clockfont, 80) self.myfontmoresmall = pygame.font.Font(None, 75) self.myfonttakao = pygame.font.Font(self.takaofont, 60) self.myfonttakao50 = pygame.font.Font(self.takaofont, 50) self.myfonttakao40 = pygame.font.Font(self.takaofont, 40) self.myfonttakao35 = pygame.font.Font(self.takaofont, 35) self.myfonttakaosmall = pygame.font.Font(self.takaofont, 30) self.weatherfont = pygame.font.SysFont(None, 80) self.weatherfont50 = pygame.font.SysFont(None, 50) self.weatherfontsmall = pygame.font.SysFont(None, 40) self.weathersource = self.pluginConfig["Weather"]["weatherurl"] self.weatherrefresh = int(self.pluginConfig["Weather"]["weatherrefresh"]) * 60 self.cacheFile = os.path.join(self.plugindir, "resources", "cachedWeather.json") def updateWeather(self): weather = json.loads(re.sub(r'([a-zA-Z_0-9\.]*\()|(\);?$)','',self.getPage(self.weathersource))) self.cacheWeather(weather) return weather def cacheWeather(self, weather): cache = {} cache['timestamp'] = int(datetime.now().strftime("%s")) cache['weather_info'] = weather with open(self.cacheFile, 'w') as outfile: json.dump(cache, outfile) def loadWeather(self): try: raw = open(self.cacheFile, 'r') cached = json.load(raw) except: cached = False if cached: if int(datetime.now().strftime("%s")) < int(cached['timestamp']) + self.weatherrefresh: weather = cached['weather_info'] else: weather = self.updateWeather() else: weather = self.updateWeather() return weather def drawClock(self): clockSurface = pygame.Surface((694,177)) clockSurface.fill([0,0,0]) mytime = strftime("%H:%M") mysecs = strftime("%S") myyear = strftime("%Y/%m/%d") weekday = (u'日曜日',u'月曜日',u'火曜日',u'水曜日',u'木曜日',u'金曜日',u'土曜日') mydayw = weekday[int(strftime("%w"))] clocklabel = self.myfont.render(mytime, 1, [255,255,255]) #secondlabel = self.myfontsmall.render(mysecs, 1, [255,255,255]) yearlabel = self.myfontmoresmall.render(myyear, 1, [255,255,255]) if int(strftime("%w")) == 0: daywlabel = self.myfonttakao.render(mydayw, 1, [255,0,0]) elif int(strftime("%w")) == 6: daywlabel = self.myfonttakao.render(mydayw, 1, [0,0,255]) else: daywlabel = self.myfonttakao.render(mydayw, 1, [255,255,255]) textpos = clocklabel.get_rect() textpos.topleft = clockSurface.get_rect().topleft #secpos = [ textpos[0] + textpos[2] + 10, textpos[1] + 70 ] yearpos = yearlabel.get_rect() yearpos.topright = clockSurface.get_rect().topright daywpos = daywlabel.get_rect() daywpos.right = clockSurface.get_rect().right daywpos.top = yearpos.bottom + 1 #self.surface.blit(secondlabel, secpos) self.surface.blit(clocklabel, textpos) self.surface.blit(yearlabel, yearpos) self.surface.blit(daywlabel, daywpos) pygame.draw.rect(clockSurface, (0, 0, 0), (0, 0, 694, 177), 1) return clockSurface def renderTodayWeather(self, daily): todayweatherrect = pygame.Surface((347,256)) todayweatherrect.fill([255,255,255]) self.myfonttakao50.set_bold(True) #weatherdate = daily['date'] weatherdate = datetime.strptime(daily['date'],'%Y/%m/%d') weathertext = daily['weather'] weathericon = daily['img'] weekday = (u'日',u'月',u'火',u'水',u'木',u'金',u'土') mydayw = weekday[int(weatherdate.strftime("%w"))] yesterday = weatherdate.replace(day=1) datelabeltext = "X" + weatherdate.strftime('%d') + mydayw datelabeltext = datelabeltext.replace('X0','X').replace('X','') if int(weatherdate.strftime("%w")) == 0: datelabel = self.myfonttakao50.render(datelabeltext, 1, (255, 0, 0)) elif int(weatherdate.strftime("%w")) == 6: datelabel = self.myfonttakao50.render(datelabeltext, 1, (0, 0, 255)) else: datelabel = self.myfonttakao50.render(datelabeltext, 1, (0, 0, 0)) self.myfonttakao50.set_bold(False) weatherimage = pygame.transform.scale(self.LoadImageFromUrl(weathericon),(200, 128)) weatherlabel = self.myfonttakao50.render(weathertext, 1, (0, 0, 0)) temperature = daily['temperature']['range'] for i,t in enumerate(temperature): templabel = self.weatherfont.render(t['content'] + u'℃', 1, (0, 0, 0)) temppos = templabel.get_rect() temppos.y = templabel.get_rect().bottom*i temppos.right = todayweatherrect.get_rect().right todayweatherrect.blit( templabel, temppos ) #print '%s %s' % (t['centigrade'],t['content']) rainfallchance = daily['rainfallchance']['period'] for i,r in enumerate(rainfallchance): #pophourlabel = self.weatherfontsmall.render(r['hour'] , 1, (0, 0, 0)) #pophourpos = pophourlabel.get_rect() #pophourpos.centerx = todayweatherrect.get_rect().right/4 * i + todayweatherrect.get_rect().right/8 #pophourpos.y = datelabel.get_rect().centery + weatherimage.get_rect().bottom + weatherlabel.get_rect().bottom poplabel = self.weatherfont50.render(r['content'] + "%", 1, (0, 0, 0)) poppos = poplabel.get_rect() poppos.centerx = todayweatherrect.get_rect().right/4 * i + todayweatherrect.get_rect().right/8 #poppos.y = datelabel.get_rect().centery + weatherimage.get_rect().bottom + weatherlabel.get_rect().bottom poppos.bottom = todayweatherrect.get_rect().bottom #todayweatherrect.blit(pophourlabel, pophourpos) todayweatherrect.blit(poplabel, poppos) #todayweatherrect.blit(poplabel, (86.75*i, datelabel.get_rect().bottom+weatherimage.get_rect().bottom+weatherlabel.get_rect().bottom)) #print '%s:%s%%' % (r['hour'],r['content']) #print datelabel.get_rect().bottom+weatherimage.get_rect().bottom imagepos = weatherimage.get_rect() imagepos.right = todayweatherrect.get_rect().centerx imagepos.y = datelabel.get_rect().centery imagepos.x = 1 weatherlabelpos = weatherlabel.get_rect() weatherlabelpos.centerx = todayweatherrect.get_rect().centerx weatherlabelpos.y = imagepos.bottom + 1 todayweatherrect.blit(weatherimage, imagepos) todayweatherrect.blit(datelabel, (0, 0)) todayweatherrect.blit(weatherlabel, weatherlabelpos) pygame.draw.rect(todayweatherrect, (0, 0, 0), (0, 0, 347, 256), 1) return todayweatherrect def renderWeeklyWeather(self, weekly): weeklyrect = pygame.Surface((138.8,66)) weeklyrect.fill([255,255,255]) weatherdate = datetime.strptime(weekly['date'],'%Y/%m/%d') weathericon = weekly['img'] weekday = (u'日',u'月',u'火',u'水',u'木',u'金',u'土') mydayw = weekday[int(weatherdate.strftime("%w"))] weatherimage = pygame.transform.scale(self.LoadImageFromUrl(weathericon),(92, 59)) datelabeltext = "X" + weatherdate.strftime('%d') + mydayw datelabeltext = datelabeltext.replace('X0','X').replace('X','') self.myfonttakao35.set_bold(True) if int(weatherdate.strftime("%w")) == 0: datelabel = self.myfonttakao35.render(datelabeltext, 1, (255, 0, 0)) elif int(weatherdate.strftime("%w")) == 6: datelabel = self.myfonttakao35.render(datelabeltext, 1, (0, 0, 255)) else: datelabel = self.myfonttakao35.render(datelabeltext, 1, (0, 0, 0)) daywpos = datelabel.get_rect() daywpos.right = weeklyrect.get_rect().right imagepos = weatherimage.get_rect() imagepos.top = weeklyrect.get_rect().top imagepos.left = weeklyrect.get_rect().left weeklyrect.blit(weatherimage, imagepos) weeklyrect.blit(datelabel, daywpos) pygame.draw.rect(weeklyrect, (0, 0, 0), (0, 0, 138.8, 66), 1) return weeklyrect def showScreen(self): self.surface.fill([0,0,0]) # Draw the clock self.surface.blit(self.drawClock(), (694,0)) #self.drawClock() # Draw the weather weather = self.loadWeather() obj = weather['pref']['area'][u'東京地方']['info'] #今日明日の天気 for i in range(0,2): forecast = obj[i] self.surface.blit(self.renderTodayWeather(forecast), (347*i, 144)) #明後日以降の天気 for i in range(2,7): forecast = obj[i] self.surface.blit(self.renderWeeklyWeather(forecast), (138.8 *(i-2), 400)) # Scale our surface to the required screensize before sending back scaled = pygame.transform.scale(self.surface,self.screensize) self.screen.blit(scaled,(0,0)) return self.screen
削除し忘れて普通に動いてたところがあって、DoSになりかねないバグだったので修正(2016/01/7 追記)
JSONP形式を読み込んでいろいろと処理している形。 takaoフォントを使用しているので、インストールする必要がある。
sudo apt-get install fonts-takao
もしくは、好みのフォントに変更してほしい。
config/screen.ini
[Weather] # JSON link for weather. # You'll need to register for a WeatherUnderground API key # available from http://www.wunderground.com/weather/api/d/pricing.html # weathersource=http://api.wunderground.com/api/[API-KEY]/hourly/q/UK/London.json weatherurl=http://www.drk7.jp/weather/json/13.js # Interval in minutes to refresh weather data weatherrefresh=5
LoadImageFromUrlについては、キャッシュではないが、resourceに対象のファイルが無いか確認して、ある場合は、ファイルから、ない場合は画像ファイルを書き込むという修正を加えた。
違う地域にしたいなら
違う地域にしたい場合は、screen.pyで
obj = weather['pref']['area'][u'東京地方']['info']
としていることを適宜変更するのと、 config/screen.ini で
weatherurl=http://www.drk7.jp/weather/json/13.js
としているのを適宜変更してください。
例えば大阪なら
obj = weather['pref']['area'][u'大阪府']['info']
config/screen.ini で
weatherurl=http://www.drk7.jp/weather/json/27.js
かな。
最後に
プロセスのCPU使用率が9割となっていてどうかな~と思うところはある。 液晶が2.8インチと小さいのでちょっと見づらいかもしれない。 とりあえず1週間程度動かしてみておかしくはなっていないが、もう少し様子を見てみたい。