デジタルフォトフレームを簡易モニターとして使う

週間天気予報


前回に引き続きWEBページから必要な情報を抜き出して、フォトフレームに表示する方法 を紹介します。

以前は、YAHOO天気・災害のRSSフィードから、週間天気予報を抜き出して表示していましたが、
このサービスが2022年3月末で終了してしまったので、こちらのページから 10日間の天気予報を抜き出して、
7日分だけフォトフレームに表示します。

【STEP1】パッケージのインストール

以下のパッケージをインストールします。
$ sudo apt install python3-pip python3-setuptools fonts-takao imagemagick
$ python3 -m pip install -U pip
$ python3 -m pip install beautifulsoup4
$ python3 -m pip install requests

【STEP2】pythonスクリプト

WEBページから10日分の天気予報を抜き出すスクリプトは以下のとおりです。
BeautifulSoupのfindやfind_allを使って、必要なタグだけを抜き出しています。
スクリプトのurlは、お住まいの地域に合わせて変更してください。
こちらのページで地域を選ぶ と表示されるURLをそのまま指定します。
#!/usr/bin/python3
#-*- encoding: utf-8 -*-
import sys
import os
import requests
from bs4 import BeautifulSoup

'''
sudo apt install python3-pip python3-setuptools fonts-takao imagemagick
python3 -m pip install -U pip
python3 -m pip install beautifulsoup4
python3 -m pip install requests

'''

if __name__ == '__main__':
    argv = sys.argv  # コマンドライン引数を格納したリストの取得
    argc = len(argv) # 引数の個数

    # 天気予報取得
    url = 'https://tenki.jp/forecast/5/26/5110/23104/10days.html'
    print("url={}".format(url))
    #opener = urllib2.build_opener()
    #contents = opener.open( url ).read()
    #response = request.urlopen(url)
    response = requests.get(url)
    #print(response.text)
    soup = BeautifulSoup(response.text, "html.parser")

    section = soup.find('section', class_='section-wrap')
    section_h2 = section.find('h2')
    print("section_h2={}".format(section_h2))
    section_h2.find('time').decompose()
    print("section_h2=[{}]".format(section_h2.get_text()))

    #forecast10days = soup.find('dd', class_='forecast10days-actab')
    forecast10days_list = soup.find_all('dd', class_='forecast10days-actab')
    print("len(forecast10days_list)={}".format(len(forecast10days_list)))
    #print("forecast10days={}".format(forecast10days))


    for index in range(len(forecast10days_list)):
        print("index={}".format(index))
        if (index > 6): continue
        days = forecast10days_list[index].find('div', class_='days')
        print("days=[{}]".format(days))
        print("days=[{}]".format(days.get_text()))

        forecast = forecast10days_list[index].find('div', class_='forecast')
        print("forecast={}".format(forecast))
        alttext=forecast.find('img').get('alt')
        print("alttext=[{}]".format(alttext))

        temp = forecast10days_list[index].find('div', class_='temp')
        print("temp={}".format(temp))
        high_temp = temp.find('span', class_='high-temp')
        print("high_temp=[{}]".format(high_temp))
        print("high_temp=[{}]".format(high_temp.get_text()))
        low_temp = temp.find('span', class_='low-temp')
        print("low_temp=[{}]".format(low_temp))


上記のコードを実行すると、必要なタグだけを抜き出して表示します。
これを変更して、必要な情報だけをファイルに記録します。
IPアドレスはsocketを使って取得しています。
gethostname()を使ってIPアドレスを取り出す例がいくつか紹介されていますが、gethostname()を使うと 127.0.1.1が戻ります。
#!/usr/bin/python3
#-*- encoding: utf-8 -*-
import sys
import os
import csv
import requests
from bs4 import BeautifulSoup
#import subprocess
import socket


'''
sudo apt install python3-pip python3-setuptools
python3 -m pip install -U pip
python3 -m pip install beautifulsoup4
python3 -m pip install requests

'''

if __name__ == '__main__':
        argv = sys.argv  # コマンドライン引数を格納したリストの取得
        argc = len(argv) # 引数の個数
        #print("argc={}".format(argc))
        csv_file = argv[1]
        #print("csv_file={}".format(csv_file))

        #テストモード設定
        TestMode=""
        if argc == 3:
                if argv[2] == "test":
                        TestMode="test"

        #フォントサイズなど
        #適当に変更してください
        FontSizeHeader="60"
        FontSizeBody="33"
        FontStyleHeader="G"
        FontStyleBody="M"
        FillColorHeader="DarkBlue"
        FillColorBody="Black"

        #CSVファイル作成
        fd=open(csv_file,'w')
        writer = csv.writer(fd, lineterminator='\n')
        csvdata = []

        #tenki.jpから週間天気予報を取得
        # 名古屋
        url = 'https://tenki.jp/forecast/5/26/5110/23104/10days.html'
        #print("url={}".format(url))
        response = requests.get(url)
        soup = BeautifulSoup(response.text, "html.parser")

        #地域情報を抽出
        section = soup.find('section', class_='section-wrap')
        section_h2 = section.find('h2')
        #print("section_h2={}".format(section_h2))
        section_h2.find('time').decompose()
        #print("section_h2=[{}]".format(section_h2.get_text()))
        csvline = section_h2.get_text()
        if TestMode:
                print("{}".format(csvline))
        csvdata = []
        csvdata.append(csvline)
        csvdata.append(FontSizeHeader)
        csvdata.append(FillColorHeader)
        csvdata.append(FontStyleHeader)
        writer.writerow(csvdata)

        #改行
        csvdata = []
        csvline=""
        csvdata.append(csvline)
        csvdata.append(FontSizeHeader)
        csvdata.append(FillColorHeader)
        csvdata.append(FontStyleHeader)
        writer.writerow(csvdata)

        #天気予報を抽出
        forecast10days_list = soup.find_all('dd', class_='forecast10days-actab')
        #print("len(forecast10days_list)={}".format(len(forecast10days_list)))

        for index in range(len(forecast10days_list)):
                #print("index={}".format(index))
                if (index > 6): continue
                days = forecast10days_list[index].find('div', class_='days')
                #print("days=[{}]".format(days))
                #print("days=[{}]".format(days.get_text()))
                days_text = days.get_text()

                forecast = forecast10days_list[index].find('div', class_='forecast')
                #print("forecast={}".format(forecast))
                alt_text=forecast.find('img').get('alt')
                #print("alt_text=[{}]".format(alt_text))

                temp = forecast10days_list[index].find('div', class_='temp')
                #print("temp={}".format(temp))
                high_temp = temp.find('span', class_='high-temp')
                #print("high_temp=[{}]".format(high_temp))
                #print("high_temp=[{}]".format(high_temp.get_text()))
                high_text = high_temp.get_text()

                low_temp = temp.find('span', class_='low-temp')
                #print("low_temp=[{}]".format(low_temp))
                #print("low_temp=[{}]".format(low_temp.get_text()))
                low_text = low_temp.get_text()

                csvline = "{} {} {}/{}".format(days_text, alt_text, high_text, low_text)
                if TestMode:
                        print("{}".format(csvline))
                csvdata = []
                csvdata.append(csvline)
                csvdata.append(FontSizeBody)
                csvdata.append(FillColorBody)
                csvdata.append(FontStyleBody)
                writer.writerow(csvdata)

        #改行
        csvdata = []
        csvline=""
        csvdata.append(csvline)
        csvdata.append(FontSizeHeader)
        csvdata.append(FillColorHeader)
        csvdata.append(FontStyleHeader)
        writer.writerow(csvdata)

        #IPアドレス
        #csvline = subprocess.getoutput('sudo ifconfig wlan0 | grep "inet" | awk {\'print $2\'} | cut -f2 -d:')
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.connect(("8.8.8.8", 80))
        csvline = s.getsockname()[0]
        if TestMode:
                print("{}".format(csvline.strip('\n')))
        csvdata = []
        csvdata.append(csvline.strip('\n'))
        csvdata.append(FontSizeBody)
        csvdata.append(FillColorBody)
        csvdata.append(FontStyleBody)
        writer.writerow(csvdata)

        fd.close()

【STEP3】pythonスクリプトの実行

スクリプト起動時の最初の引数は作成するファイル名です。
2番目の引数に「test」を指定すると、ファイルに書き出す内容を表示します。
$ python3 weekly.py param.csv test
名古屋市西区の天気
05月10日(火) 曇 23℃/11℃
05月11日(水) 曇のち晴 20℃/15℃
05月12日(木) 曇のち雨 24℃/16℃
05月13日(金) 雨 22℃/18℃
05月14日(土) 雨のち晴 26℃/21℃
05月15日(日) 晴のち曇 24℃/14℃
05月16日(月) 曇のち雨 19℃/16℃
192.168.10.102

同時にカレントディレクトリに「param.csv」のファイルが作られます。
$ cat param.csv
名古屋市西区の天気,60,DarkBlue,G
,60,DarkBlue,G
05月10日(火) 曇 23℃/11℃,33,Black,M
05月11日(水) 曇のち晴 20℃/15℃,33,Black,M
05月12日(木) 曇のち雨 24℃/16℃,33,Black,M
05月13日(金) 雨 22℃/18℃,33,Black,M
05月14日(土) 雨のち晴 26℃/21℃,33,Black,M
05月15日(日) 晴のち曇 24℃/14℃,33,Black,M
05月16日(月) 曇のち雨 19℃/16℃,33,Black,M
,60,DarkBlue,G
192.168.10.102,33,Black,M

出来上がったパラメータファイル(param.csv)を使って、前回と同様の手順でイメージファイルに変換し、
イメージファイルをフォトフレーム側にダウンロードしてやれば、フォトフレームに週間天気予報を表示することができます。

【STEP4】全体のシェルスクリプト

cronで1日1回、以下のスクリプトを実行すれば、フォトフレームに週間天気予報を表示することができます。
USB-SWは/sys/class/gpioを使ってON/OFFしています。
#!/bin/bash
#
# weekly weather for Raspberry
#
#set -x

function _logger() {
    sudo sh -c "echo $1 >  /dev/kmsg"
}

_logger 'Start weekly'
ParamFile=/home/pi/weekly/param.csv
python3 /home/pi/weekly/weekly.py ${ParamFile}
InFile=/home/pi/weekly/frame0078.jpg
OutFile=/tmp/image00.jpg

Vertical=50
#水平位置は適当に変更してください
Horizontal=50
TmpFile=$(mktemp)
cp $InFile $TmpFile

#パラメータファイル読み込み
cat ${ParamFile} | while read line
do
  col0=`echo ${line} | cut -d , -f 1`
  col1=`echo ${line} | cut -d , -f 2`
  col2=`echo ${line} | cut -d , -f 3`
  col3=`echo ${line} | cut -d , -f 4`
  if test "$col3" = "" ; then
    col3="M"
  fi

#イメージに文字を挿入
  Vertical=`expr ${Vertical} + ${col1}`
  if test $col3 = "G" ; then
#    convert -font /usr/share/fonts/truetype/kochi/kochi-gothic.ttf -pointsize ${col1} -fill ${col2} -draw "text ${Horizontal},${Vertical} '${col0}'" $TmpFile $OutFile
    convert -font /usr/share/fonts/truetype/takao-mincho/TakaoMincho.ttf -pointsize ${col1} -fill ${col2} -draw "text ${Horizontal},${Vertical} '${col0}'" $TmpFile $OutFile
  else
#    convert -font /usr/share/fonts/truetype/kochi/kochi-mincho.ttf -pointsize ${col1} -fill ${col2} -draw "text ${Horizontal},${Vertical} '${col0}'" $TmpFile $OutFile
    convert -font /usr/share/fonts/truetype/takao-mincho/TakaoPMincho.ttf -pointsize ${col1} -fill ${col2} -draw "text ${Horizontal},${Vertical} '${col0}'" $TmpFile $OutFile
  fi
  cp ${OutFile} ${TmpFile}
done

#デバイスファイルとマウントポイント
device="/dev/sda1"
mpoint="/mnt/sdcard"

#USB-SWのON
_logger "USB ON"
GPIO=11

for i in `seq 1 30`
do
    sudo sh -c "echo ${GPIO} > /sys/class/gpio/export"
    sudo sh -c "echo out > /sys/class/gpio/gpio${GPIO}/direction"
    sudo sh -c "echo 1 > /sys/class/gpio/gpio${GPIO}/value"
    sleep 20

#フォトフレームの内蔵メモリのマウント
    #sudo mount -t vfat /dev/sda1 /mnt/sdcard
    sudo mount -t vfat ${device} ${mpoint}

#マウントチェック
    Check=`df | grep -c ${mpoint}`
    if test $Check -eq 1 ; then
        break;
    fi
    sleep 10
done

#SD Card内のImageファイルを削除
files=`find ${mpoint} -name "*.jpg" -print`
if test -n "$files" ; then
    sudo rm ${mpoint}/*.jpg
fi

#新しいファイルをコピー
#sudo cp /tmp/image00.jpg /mnt/sdcard
sudo cp ${OutFile} ${mpoint}
sync
sync
sync

#SD Cardのアンマウント
_logger "Umount disk"
#sudo umount -l /dev/sda1
sudo umount -l ${device}

#USB-SWのOFF
_logger "USB OFF"
sudo sh -c "echo 0 > /sys/class/gpio/gpio${GPIO}/value"
sudo sh -c "echo ${GPIO} > /sys/class/gpio/unexport"

#ファイルの削除
rm ${TmpFile}

フォトフレームへの表示は以下の様になります。


次回は、WEBページからイメージファイルを抜き出して表示する方法を紹介する予定で す。