【実現したいこと(Must要件)】
①年月日曜日が外部モニターに出力されていること
②現在の天気状態が外部モニターに出力されていること
③外気温と室内気温・室内湿度が外部モニターに出力されていること
④翌日の天気予報と最高気温が外部モニターに出力されていること
⑤画面右半分は写真(アルバム)を一定時間毎に切り替えて外部モニターに出力されていること
⑥画面出力は全画面で描画すること
上記6つについて、どのように実装するべきかを検討します。
①について:
→Pythonのdatetimeライブラリを使用して年月日曜日を取得する方向で実装する。
②について:
→『OpenWeatherMap』という全世界の天気情報がAPIで提供されているサイトを活用する。
“1,000 API calls per day for free”とのことなので、無料枠内でAPIをcallするように設計。
※APIキー、地区、APIコール文を予め個々人で準備しておく必要あり。
③について:
→外気温については、②で用いたAPIで取得する。
室内温度・室内湿度は DHT22 というモジュールで一定時間毎に取得する。
取得方法は、下記のサイトを参照のこと。
外部サイト: Rasperry Pi に DHT22 を接続して温度・湿度を計測する
④について:
→翌日の天気予報は②で用いたAPIで取得する。
⑤について:
→外部ディスプレイに合わせた形で描画位置を指定する。
⑥について:
→pythonのpygameライブラリを用いて実装する。
以上をプログラムコードに書き起こすと下記のようになります。
使用しているフォント名:Buildingsandundertherailwaytracks-Regular.otf
import pygame import requests from datetime import datetime, timedelta import os import board import adafruit_dht # 天気APIの設定(例としてOpenWeatherMapを使用) API_KEY = 'あなたのAPIキーを入力してください。' CITY = 'Tokyo' URL_CURRENT = f'http://api.openweathermap.org/data/2.5/weather?q={CITY}&appid={API_KEY}&units=metric' URL_FORECAST = f'http://api.openweathermap.org/data/2.5/forecast?q={CITY}&appid={API_KEY}&units=metric' # 画像のパスをリストに格納 image_paths = [f'custom_image_{i}.jpg' for i in range(1, 61)] # Pygameの初期化 pygame.init() screen = pygame.display.set_mode((800, 480), pygame.FULLSCREEN) pygame.display.set_caption('Raspberry Pi Display') # フォントのロード font_path = "Buildingsandundertherailwaytracks-Regular.otf" # 日本語フォントのパスを指定 small_font = pygame.font.Font(font_path, 30) large_font = pygame.font.Font(font_path, 45) # Initial the DHT device, with data pin connected to: dhtDevice = adafruit_dht.DHT22(board.D18) def fetch_weather(): try: # 現在の天気を取得 response_current = requests.get(URL_CURRENT) response_current.raise_for_status() data_current = response_current.json() current_weather = data_current['weather'][0]['main'] current_temp = data_current['main']['temp'] # 予報(翌日の天気)を取得 response_forecast = requests.get(URL_FORECAST) response_forecast.raise_for_status() data_forecast = response_forecast.json() # 翌日の予報を取得 daily_weather = None daily_max_temp = None max_temp = -273.15 # Initialize with a very low temperature for forecast in data_forecast['list']: forecast_time = datetime.fromtimestamp(forecast['dt']) if forecast_time.date() == (datetime.now() + timedelta(days=1)).date(): temp = forecast['main']['temp_max'] if temp > max_temp: max_temp = temp daily_weather = forecast['weather'][0]['main'] daily_max_temp = max_temp return current_weather, current_temp, daily_weather, daily_max_temp except (requests.exceptions.RequestException, KeyError, IndexError) as e: print(f"Error fetching weather data: {e}") return "Unknown", 0.0, "Unknown", 0.0 def get_weather_icon(weather): icon_map = { 'Clear': 'clear.png', 'Rain': 'rain.png', 'Clouds': 'clouds.png', 'Drizzle': 'drizzle.png', 'Thunderstorm': 'thunderstorm.png', 'Snow': 'snow.png', 'Mist': 'mist.png', 'Smoke': 'smoke.png', 'Haze': 'haze.png', 'Dust': 'dust.png', 'Fog': 'fog.png', 'Sand': 'sand.png', 'Ash': 'ash.png', 'Squall': 'squall.png', 'Tornado': 'tornado.png' } icon_path = icon_map.get(weather, None) if icon_path and os.path.exists(icon_path): icon_image = pygame.image.load(icon_path) return icon_image return None def draw_text(surface, text, position, font, color=(0, 0, 0)): text_surface = font.render(text, True, color) surface.blit(text_surface, position) def main(): clock = pygame.time.Clock() start_time = datetime.now() last_weather_update = datetime.now() - timedelta(minutes=10) # 初回すぐに更新されるように設定 last_dht_update = datetime.now() - timedelta(minutes=10) # 初回すぐに更新されるように設定 current_image_index = 0 current_weather, current_temp, daily_weather, daily_max_temp = fetch_weather() current_weather_icon = get_weather_icon(current_weather) daily_weather_icon = get_weather_icon(daily_weather) indoor_temp = 25.0 humidity = 60 while True: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() return elif event.type == pygame.KEYDOWN: if event.key == pygame.K_ESCAPE: pygame.quit() return # 画面をベージュでクリア screen.fill((245, 245, 220)) # 現在の日時を取得 now = datetime.now() date_str = now.strftime('%Y-%m-%d %A') time_str = now.strftime('%H:%M:%S') # 天気情報の取得(10分毎) if (now - last_weather_update).total_seconds() > 600: current_weather, current_temp, daily_weather, daily_max_temp = fetch_weather() current_weather_icon = get_weather_icon(current_weather) daily_weather_icon = get_weather_icon(daily_weather) last_weather_update = now # DHT22のデータを取得(1分毎) if (now - last_dht_update).total_seconds() > 60: try: indoor_temp = dhtDevice.temperature humidity = dhtDevice.humidity except RuntimeError as error: print(error.args[0]) except Exception as error: dhtDevice.exit() raise error last_dht_update = now # 画面の左半分に描画 draw_text(screen, date_str, (10, 10), small_font) draw_text(screen, time_str, (10, 50), large_font) if current_weather_icon: screen.blit(current_weather_icon, (10, 110)) draw_text(screen, current_weather, (120, 120), large_font) else: draw_text(screen, current_weather, (10, 120), large_font) draw_text(screen, f'外気温: {current_temp}度', (10, 200), large_font) draw_text(screen, f'室内温度: {indoor_temp}度', (10, 250), large_font) draw_text(screen, f'{humidity}%', (230, 300), large_font) # 翌日の天気アイコンと最高気温を表示 draw_text(screen, '翌日の天気:', (10, 350), small_font) if daily_weather_icon: screen.blit(daily_weather_icon, (10, 380)) draw_text(screen, f'最高気温 {daily_max_temp}度', (125, 425), small_font) # 20秒毎に表示する画像を変更 elapsed_time = (now - start_time).total_seconds() if elapsed_time > 20: current_image_index = (current_image_index + 1) % len(image_paths) start_time = now # 画像のロードと描画 if os.path.exists(image_paths[current_image_index]): custom_image = pygame.image.load(image_paths[current_image_index]) custom_image_rect = custom_image.get_rect(center=(600, 240)) screen.blit(custom_image, custom_image_rect.topleft) else: draw_text(screen, 'Image not found', (600, 240), large_font) # 画面の更新 pygame.display.flip() clock.tick(60) if __name__ == '__main__': main()
完成イメージは下図のようになります。