全プログラムコード
#!/usr/bin/env python3
###################################################################################################
# <設定>
#
# ホームディレクトリ =========================================================================
# このファイルを置くディレクトリ
homedir = '/home/pi/Documents/python/home-std/'
#
# Google Calendar API ================================================================
# 以下のPython クイックスタートを参照して、credentials.jsonをダウンロードし、ホームディレクトリに置くことで、Google Calendarと連携します。
# 初回起動時はGoogleアカウントへログインが必要なため、Raspberry Pi本体から(リモートでssh経由ではなく)home.pyを起動してください。
# chromeブラウザが起動するので、ログインをしてください。2回目以降は設定ファイル(token.pickle、token2.pickle)が出来てログイン不要になります。
# https://developers.google.com/calendar/api/quickstart/python?hl=ja
#
# 子供 ==================================================================================
# 名前を3人まで記入
child=['一郎','二郎','三郎']
# child=['一郎','二郎']
# child=['一郎']
#
# お手伝い ===============================================================================
# 子供の交替制お手伝い。子供の数と同じだけ設定すると、毎日順番に変わります。
kakari=['テーブルふき','せんたくもの','そうじ']
# kakari=['テーブルふき','せんたくもの']
# kakari=['']
#
# チェックリスト ===============================================================================
# list_morning : 平日朝
# list_evening : 平日夕方
# list_morning_holiday : 休日朝
# list_evening_holiday : 休日夕方
list_morning = ['着がえ','熱をはかる','歯をみがく','ハンカチ','きんちゃく','すいとう']
list_evening = ['しゅくだい','プリント','鉛筆削る','歯をみがく']
list_morning_holiday = ['着がえ','熱をはかる','歯をみがく']
list_evening_holiday = ['うわばき','たいそうふく','たいそうふく','歯をみがく']
#
# Yahoo! 気象情報API URL ================================================================
# 以下のcustom_yahooapiurlを設定すると有効になります。Yahoo!デベロッパーネットワークのご利用ガイドを参照してください。output=xmlとしてください。
# custom_yahooapiurl = 'https://map.yahooapis.jp/weather/V1/place?appid=<あなたのappid>&coordinates=<緯度>,<経度>&output=xml'
custom_yahooapiurl = ''
#
# 気象庁 天気予報エリア ===================================================================
custom_jmaarea = '東京都'
custom_jmasubarea = '東京地方'
#
# 音声 =================================================================================
# 0 ... 男性
# 1 ... 女性
voice_select = 1
#
# 時刻をしゃべるONのときのしゃべる間隔 =========================================================
# 単位:分
speaktimespan = 5
#
# 自動ディスプレイON/OFF時刻 ===================================================================
sleeph = 8 # AM 8:00以降、チェックボタン完了していればスリープ
wake1h = 6 # Wake-1 6:30
wake1m = 0
wake2h = 15 # Wake-2 15:00
wake2m = 0
#
# 監視カメラ連携 =========================================================================
# 別のRaspberry Piによる監視カメラからの画像ファイル名データを受信できるようにする場合、homeenableを Trueにし、このRaspberry PiのIPアドレスと、使用するポート番号を合わせます。
# 監視カメラの共有NFS(Network File Server)の画像ディレクトリをnfsdirに設定します。
# 検出時のサウンドファイルをsoundディレクトリに入れて、detectsoundにファイル名を設定します。
homeenable = True
homeipaddress = '192.168.1.10'
homeportaddress = 54321
nfsdir = '/mnt/camera/large/'
detectsound = 'poka.mp3'
###################################################################################################
# インクルードファイル
from tkinter import *
from tkinter import ttk
import RPi.GPIO as GPIO
import json
import requests
import subprocess
from time import strftime
import datetime
import urllib.request
import xml.etree.ElementTree as ET
import pickle
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
import threading
import pygame.mixer
import functools
from time import sleep
from PIL import Image, ImageTk
import socket
###################################################################################################
# GPIO設定
# GPIO4をディスプレイON/OFFスイッチとして使用する。
# ボード上のピン番号は7
# Raspberry Pi内でプルダウンする。5V入力検出でディスプレイON/OFF切替とする。
GPIO.setmode(GPIO.BCM)
GPIO.setup(4, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
###################################################################################################
# Tk設定・各種フラグ
root = Tk()
root.title('ホーム')
root.attributes('-fullscreen', True)
root.configure(bg='white')
root.columnconfigure(0, weight=1)
root.rowconfigure(0, weight=1)
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
displayon = True
speaktime = False
mute = False
fullscreen = True
###################################################################################################
# Grid Matrix
# | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10| 11| 12| 13| 14| 15| 16| 17| 18| 19| 20| 21| 22| 23|
#---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
# 0| rss0 |lb_rst |lb_mute|lb_full|
#---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
# 1| | weather[0] | weather[2] |
#---+ clock +---+---+---+---+---+---+---+---+---+---+---+---+
# 2| | weather[1] | weather[3] |
#---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
# 3| rain[0] | rain[1] | rain[2] | rain[3] | rain[4] | rain[5] | rain[6] | |
#---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
# 4| lb_otetsudai[0] | lb_otetsudai[1] | lb_otetsudai[2] |
#---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
# 5|lb_button[0][0]|lb_button[0][2]|lb_button[1][0]|lb_button[1][2]|lb_button[2][0]|lb_button[2][2]|
#---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
# 6|lb_button[0][1]|lb_button[0][3]|lb_button[1][1]|lb_button[1][3]|lb_button[2][1]|lb_button[2][3]|
#---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
colwidth=24
rowheight=7
frame1 = ttk.Frame(root)
frame1.grid(sticky=(E,W,S,N))
for col in range(colwidth):
frame1.grid_columnconfigure(col, weight=1);
for row in range(rowheight):
frame1.grid_rowconfigure(row, weight=1);
style = ttk.Style()
style.theme_use('classic')
style.configure('TFrame', background='white')
###################################################################################################
# 監視カメラデータ受信
def cameralisten():
global homeipaddress, homeportaddress
try:
camerasocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
camerasocket.bind((homeipaddress, homeportaddress))
camerasocket.listen(1)
while True:
clientsocket, address = camerasocket.accept()
data = clientsocket.recv(1024)
datatext = data.decode('utf-8')
cameraon(datatext)
except Exception as e:
print(e)
finally:
camerasocket.close()
###################################################################################################
# カメラ画像に切替
cameraflag = 0
camerawake = 0
currentimage = PhotoImage(file=homedir+'/image/wide6.png')
labelcamera = ttk.Label(frame1, background = 'white', font = ('Noto Sans Japanese',360 ,'bold'), foreground = 'red')
def cameraon(filename):
global detectsound,camerawake,displayon,labelcamera,cameraflag,mute,currentimage,nfsdir,rss0,lb_rst,lb_mute,lb_full,lb_clock,lb_weather,lb_rain,lb_otetsudai,lb_button,rowheight,colwidth
cameraflag=60
camerawake=0
if not displayon:
camerawake=60
displayon=True
subprocess.call('/usr/bin/vcgencmd display_power 1', shell=True)
rss0.grid_remove()
lb_rst.grid_remove()
lb_mute.grid_remove()
lb_full.grid_remove()
lb_clock.grid_remove()
for i,j in enumerate(lb_weather):
lb_weather[i].grid_remove()
for i,j in enumerate(lb_rain):
lb_rain[i].grid_remove()
for i,j in enumerate(lb_otetsudai):
lb_otetsudai[i].grid_remove()
for i,j in enumerate(lb_button):
for m,n in enumerate(j):
lb_button[i][m].grid_remove()
labelcamera.grid(row=0, column=0, rowspan=rowheight, columnspan=colwidth)
targetfile = nfsdir + filename
img=Image.open(targetfile)
img=img.resize((1368,768), Image.ANTIALIAS)
currentimage = ImageTk.PhotoImage(img)
labelcamera.config(image = currentimage)
if not mute and camerawake==0:
subprocess.call('/usr/bin/mpg321 '+homedir+'/sound/'+detectsound+' > /dev/null 2>&1', shell=True)
###################################################################################################
# カメラ画像OFF
def cameraoff():
global labelcamera,cameraflag,rss0,lb_rst,lb_mute,lb_full,lb_clock,lb_weather,lb_rain,lb_otetsudai,lb_button
cameraflag=0
rss0.grid()
lb_rst.grid()
lb_mute.grid()
lb_full.grid()
lb_clock.grid()
for i,j in enumerate(lb_weather):
lb_weather[i].grid()
for i,j in enumerate(lb_rain):
lb_rain[i].grid()
for i,j in enumerate(lb_otetsudai):
lb_otetsudai[i].grid()
for i,j in enumerate(lb_button):
for m,n in enumerate(j):
lb_button[i][m].grid()
labelcamera.grid_remove()
###################################################################################################
# 日付と天気image、気温
lb_weather=[]
lb_weather.append(ttk.Label(frame1, text = '10日', font = ('Noto Sans Japanese', 48, 'bold'), background = 'white', foreground = '#070'))
lb_weather.append(ttk.Label(frame1, text = '11日', font = ('Noto Sans Japanese', 48, 'bold'), background = 'white', foreground = '#770'))
lb_weather[0].grid(row=1, column=int(colwidth/2), columnspan=int(colwidth/4))
lb_weather[1].grid(row=2, column=int(colwidth/2), columnspan=int(colwidth/4))
lb_weather.append(ttk.Label(frame1, text = '5℃/15℃', font = ('Noto Sans Japanese', 48), background = 'white', foreground = '#070'))
lb_weather.append(ttk.Label(frame1, text = '5℃/15℃', font = ('Noto Sans Japanese', 48), background = 'white', foreground = '#770'))
lb_weather[2].grid(row=1, column=int(colwidth/2+colwidth/4), columnspan=int(colwidth/4))
lb_weather[3].grid(row=2, column=int(colwidth/2+colwidth/4), columnspan=int(colwidth/4))
###################################################################################################
# 降水量予報
lb_rain=[]
for i in range(8):
lb_rain.append(ttk.Label(frame1, text = '', font = ('Noto Sans Japanese', 36), background = 'white', foreground = 'black'))
lb_rain[i].grid(row=3, column=i*int(colwidth/8), columnspan=int(colwidth/8))
###################################################################################################
# デジタル時計
if speaktime:
clock_color = "red"
else:
clock_color = "black"
lb_clock = ttk.Label(frame1, font = ('FreeMono', 164 ,'bold'),background = 'white', foreground = clock_color, cursor='none')
lb_clock.grid(row=1, column=0, rowspan=2, columnspan=int(colwidth/2))
###################################################################################################
# RSS
rss0 = ttk.Label(frame1, font = ('Noto Sans Japanese', 36), text = 'RSS', background = 'white', foreground = 'blue')
rss0.grid(row=0, column=0, columnspan=int(colwidth/2+colwidth/4), sticky=(W))
###################################################################################################
# チェックボタン定義
lb_button=[]
button_end=[]
buttonimagenum=[]
childcomplete=[]
imgbutton=[]
imgbutton.append(PhotoImage(file=homedir+'/image/wide0.png'))
imgbutton.append(PhotoImage(file=homedir+'/image/wide1.png'))
imgbutton.append(PhotoImage(file=homedir+'/image/wide2.png'))
imgbutton.append(PhotoImage(file=homedir+'/image/wide3.png'))
imgbutton.append(PhotoImage(file=homedir+'/image/wide4.png'))
imgbutton.append(PhotoImage(file=homedir+'/image/wide5.png'))
imgbutton.append(PhotoImage(file=homedir+'/image/wide6.png'))
rowtemp=5
coltemp=0
lb_otetsudai=[]
pygame.mixer.init()
bombsound = pygame.mixer.Sound(homedir+'/sound/bomb.wav')
###################################################################################################
# 爆発アニメーション
def explosion(childnum, buttonnum):
global buttonimagenum,button_end,mute,lb_button,childcomplete
if buttonimagenum[childnum][buttonnum]<len(imgbutton):
lb_button[childnum][buttonnum].config(image = imgbutton[buttonimagenum[childnum][buttonnum]])
buttonimagenum[childnum][buttonnum]+=1
lb_button[childnum][buttonnum].after(50, explosion, childnum, buttonnum)
else:
nextchk = getnextcheck(childnum)
if nextchk != 'end':
lb_button[childnum][buttonnum].config(image = imgbutton[0], text = nextchk)
else:
button_end[childnum][buttonnum]=True
for button_end0 in button_end[childnum]:
if button_end0:
endcount+=1
if endcount<len(button_end[childnum]) and not childcomplete[childnum]:
childcomplete[childnum] = True
###################################################################################################
# チェックボタンクリック
def clickbutton(event, childnum, buttonnum):
global buttonimagenum,button_end,mute
if pygame.mixer.music.get_busy()==False:
buttonimagenum[childnum][buttonnum]=1
event.widget['text']=''
endcount = 0
for button_end0 in button_end[childnum]:
if button_end0:
endcount+=1
if endcount<len(button_end[childnum]):
if not mute:
free_channel = pygame.mixer.find_channel()
if free_channel != None:
free_channel.play(bombsound)
explosion(childnum, buttonnum)
###################################################################################################
# お手伝い取得
def getotetsudai(num):
global kakari ,child
if len(child) <= num:
return ''
if len(child) > len(kakari):
return child[num]
basedate = datetime.datetime(2020,9,18)
nowdate = datetime.datetime.now()
diff = nowdate - basedate
return str(child[num]+' '+kakari[(diff.days + num) % len(child)])
###################################################################################################
# チェックボタン表示
for i in range(int(len(child))):
sublist=[]
endbutton=[]
buttonimagesub=[]
coltemp = i*int(colwidth/len(child))
rowtemp = 5
coltemp2 = 0
lb_otetsudai.append(ttk.Label(frame1, text = getotetsudai(i), font = ('Noto Sans Japanese', 34, 'bold'), background = 'white', foreground = '#339'))
lb_otetsudai[i].grid(row=4, column=coltemp, columnspan=int(colwidth/len(child)), sticky=(S,N))
for j in range(int(12/len(child))):
sublist.append(ttk.Label(frame1, text = 't1', compound='center', font = ('Noto Sans Japanese', 24, 'bold'), image=imgbutton[0], background = 'white', foreground = 'white', cursor='none'))
endbutton.append(False)
buttonimagesub.append(1)
sublist[j].grid(row=rowtemp, column=coltemp+coltemp2, columnspan=int(colwidth/6), sticky=(N))
sublist[j].bind('<1>', functools.partial(clickbutton, childnum=i, buttonnum=j))
if rowtemp == 5:
rowtemp = 6
else:
rowtemp = 5
coltemp2+=int(colwidth/6)
lb_button.append(sublist)
button_end.append(endbutton)
buttonimagenum.append(buttonimagesub)
childcomplete.append(False)
###################################################################################################
# Yahoo! RSS News
yahoonewsurl = 'https://news.yahoo.co.jp/rss/topics/top-picks.xml'
rsstext = ''
rsspos = 0
###################################################################################################
# Yahoo! 気象情報API
# 以下のサイトで提供されている、指定した緯度経度の60分後までの降水量予報を取得できるAPI。
# https://developer.yahoo.co.jp/webapi/map/openlocalplatform/v1/weather.html
# 10分ごとの予報を色付きで表示する。
yahooapiurl = custom_yahooapiurl
raintime=['','','','','','','','']
rainfall=[0,0,0,0,0,0,0,0]
rainmax=0
###################################################################################################
# 降水量予報データ取得・表示
def getBGcolor(rfstr, colstr):
ret = 0
rf = float(rfstr)
if rf <= 2.0:
ret = int(((2-rf)*255)/2)
elif colstr=="R":
ret = int((rf-2)*25)
if colstr=="B":
ret = 255
if rf > 12.2:
ret = int((22.5-rf)*25)
if ret > 255:
ret = 255
if ret < 0:
ret = 0
return ret
def getcolorforeground(rfstr):
ret='white'
if getBGcolor(rfstr,"R")+getBGcolor(rfstr,"G")+getBGcolor(rfstr,"B")>637:
ret='black'
return ret
def getrain():
global raintime, rainmax
url = yahooapiurl
if len(url) < 60:
return
try:
xmldata = urllib.request.urlopen(url, timeout=5).read()
except Exception as e:
print(e)
return
rainmax=0
root = ET.fromstring(xmldata)
for ch1 in root:
if ch1.tag.find('Feature') >= 0 :
for ch2 in ch1:
if ch2.tag.find('Property') >= 0 :
for ch3 in ch2:
if ch3.tag.find('WeatherList') >= 0 :
for ch4 in ch3:
if ch4.tag.find('Weather') >= 0 :
for ch5 in ch4:
if ch5.tag.find('Date') >= 0:
raintime[rainmax] = ch5.text
elif ch5.tag.find('Rainfall') >= 0:
rainfall[rainmax] = ch5.text
rainmax+=1
for i in range(7):
lb_rain[i].config(text = rainfall[i], background = '#%02X%02X%02X' % (getBGcolor(rainfall[i],"R"),getBGcolor(rainfall[i],"G"),getBGcolor(rainfall[i],"B")), foreground = getcolorforeground(rainfall[i]))
###################################################################################################
# Google Calendar定義
caltext = ''
myevent=[]
myeventwhen=[]
myeventwho=[]
myeventcolor=[]
SCOPES = ['https://www.googleapis.com/auth/calendar.readonly']
###################################################################################################
# 気象庁JSON定義
jmaofficeurl = 'https://www.jma.go.jp/bosai/common/const/area.json'
jmaurl = ''
jmaofficedata = requests.get(jmaofficeurl)
jmaofficedict = json.loads(jmaofficedata.text)
for mykey in jmaofficedict['offices'].keys():
if jmaofficedict['offices'][mykey]['name'] == custom_jmaarea:
jmaurl = 'https://www.jma.go.jp/bosai/forecast/data/forecast/'+mykey+'.json'
jma_weather = []
jma_dateweather = []
jma_datetemp = []
jma_temp = []
ame=PhotoImage(file=homedir+'/image/ame.png')
yuki=PhotoImage(file=homedir+'/image/yuki.png')
kumori=PhotoImage(file=homedir+'/image/kumori.png')
hare=PhotoImage(file=homedir+'/image/hare.png')
###################################################################################################
# 気象庁予報データ取得
def get_jma_weather():
global jmaurl, jma_weather, jma_dateweather, jma_datetemp, jma_temp, custom_jmasubarea
if jmaurl == '':
return
jma_weather.clear()
jma_dateweather.clear()
jma_datetemp.clear()
jma_temp.clear()
try:
jmadata = requests.get(jmaurl)
jmadict = json.loads(jmadata.text)
areaindex=0
for myarea in jmadict[0]['timeSeries'][0]['areas']:
if myarea['area']['name'] == custom_jmasubarea:
for myweather in jmadict[0]['timeSeries'][0]['areas'][areaindex]['weathers']:
jma_weather.append(myweather)
for mydateweather in jmadict[0]['timeSeries'][0]['timeDefines']:
jma_dateweather.append(mydateweather[8:10])
for mydatetemp in jmadict[0]['timeSeries'][2]['timeDefines']:
jma_datetemp.append(mydatetemp[8:10])
for mytemp in jmadict[0]['timeSeries'][2]['areas'][areaindex]['temps']:
jma_temp.append(mytemp)
areaindex+=1
except Exception as e:
print(e)
return -1
return min(len(jma_weather),len(jma_dateweather))
###################################################################################################
# 気象庁予報データ表示
def set_jma_weather():
global lb_weather, hare, kumori, yuki, ame
if get_jma_weather() >= 2:
datenum=0
for date0, weather0 in zip(jma_dateweather, jma_weather):
lb_weather[datenum].config(text = date0+'日', compound='right')
if weather0.find('雪') >= 0:
lb_weather[datenum].config(image = yuki)
elif weather0.find('雨') >= 0:
lb_weather[datenum].config(image = ame)
elif weather0.find('くもり') >= 0:
lb_weather[datenum].config(image = kumori)
elif weather0.find('晴') >= 0:
lb_weather[datenum].config(image = hare)
tempstr='-'
for date1, temp1 in zip(jma_datetemp, jma_temp):
if date1 == date0:
if len(tempstr) > 1:
tempstr=tempstr+temp1+'℃'
else:
tempstr=temp1+'℃/'
lb_weather[datenum+2].config(text = tempstr)
datenum+=1
if datenum >= 2:
break
###################################################################################################
# Open JTalk実行
# str_speak ... スピーチテキスト
def exe_openjtalk(str_speak):
global voice_select, homedir
m = voice_select
str='echo "'+str_speak+'" | /usr/bin/open_jtalk -g 10 -x /var/lib/mecab/dic/open-jtalk/naist-jdic '
if m == 0:
str+='-m '+homedir+'/Voice/takumi/takumi_normal.htsvoice '
else:
str+='-m '+homedir+'/Voice/mei/mei_normal.htsvoice '
str+='-ow /dev/stdout | /usr/bin/aplay > /dev/null 2>&1'
subprocess.call(str, shell=True)
def speak_time():
nowdate = datetime.datetime.now()
h = nowdate.hour
if h>12:
h=h-12
exe_openjtalk("%d時%d分 %d時%d分" % (h, nowdate.minute, h, nowdate.minute))
###################################################################################################
# Yahoo! RSS News データ取得
def getnews():
global rsstext,caltext,rsspos
url = yahoonewsurl
try:
xmldata = urllib.request.urlopen(url, timeout=5).read()
rsstext = ' '
root = ET.fromstring(xmldata)
for ch1 in root:
if ch1.tag.find('channel') >= 0 :
for ch2 in ch1:
if ch2.tag.find('item') >= 0 :
for ch3 in ch2:
if ch3.tag.find('title') >= 0:
rsstext += ch3.text + ' '
rsstext = caltext + rsstext
rsspos = 0
except Exception as e:
print(e)
###################################################################################################
# Googleカレンダーデータ取得
def getcalendar():
global caltext, homedir
creds = None
if not os.path.exists(homedir+'/credentials.json'):
return
if os.path.exists(homedir+'/token.pickle'):
with open(homedir+'/token.pickle', 'rb') as token:
creds = pickle.load(token)
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
homedir+'/credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
with open(homedir+'/token.pickle', 'wb') as token:
pickle.dump(creds, token)
service = build('calendar', 'v3', credentials=creds)
now = datetime.datetime.utcnow().isoformat() + 'Z' # 'Z' indicates UTC time
events_result = service.events().list(calendarId='primary', timeMin=now,
maxResults=10, singleEvents=True,
orderBy='startTime').execute()
events = events_result.get('items', [])
myweek=['月','火','水','木','金','土','日']
pretext = ''
caltext = ''
for event in events:
start = event['start'].get('dateTime', event['start'].get('date'))
d = datetime.datetime(int(start[0:4]), int(start[5:7]), int(start[8:10]))
addtext = '%2d月%2d日(%s)' % (d.month, d.day, myweek[d.weekday()])
if pretext == addtext: # 複数日期間のイベント
caltext += ' '
else:
caltext += ' '+addtext
caltext += event['summary']
pretext = addtext
###################################################################################################
# Googleカレンダー休日情報取得
hmon=[1,1]
hday=[1,2]
def getholiday():
global hmon, hday, homedir
creds = None
if not os.path.exists(homedir+'/credentials.json'):
return
if os.path.exists(homedir+'/token2.pickle'):
with open(homedir+'/token2.pickle', 'rb') as token:
creds = pickle.load(token)
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
homedir+'/credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
with open(homedir+'/token2.pickle', 'wb') as token:
pickle.dump(creds, token)
service = build('calendar', 'v3', credentials=creds)
now = datetime.datetime.utcnow().isoformat() + 'Z' # 'Z' indicates UTC time
events_result = service.events().list(calendarId='ja.japanese#holiday@group.v.calendar.google.com', timeMin=now,
maxResults=2, singleEvents=True,
orderBy='startTime').execute()
events = events_result.get('items', [])
for event in events:
start = event['start'].get('dateTime', event['start'].get('date'))
hmon.append(int(start[5:7]))
hday.append(int(start[8:10]))
###################################################################################################
# 平日フラグ
# daytomorrow : 今日0 / 明日1
def isweekday(daytomorrow):
dtnow = datetime.datetime.now() + datetime.timedelta(days=daytomorrow)
isweek = True
for hmon0, hday0 in zip(hmon, hday):
if dtnow.month == hmon0 and dtnow.day == hday0:
isweek = False
if dtnow.weekday() >=5:
isweek = False
return isweek
###################################################################################################
# チェックリスト初期化
chklist=[]
def initchklist():
global chklist, list_morning, list_evening, list_morning_holiday, list_evening_holiday, child, childcomplete
chklist.clear()
dtnow = datetime.datetime.now()
sublist=[]
if dtnow.hour > 12: # 午後
if isweekday(0) == True: # 今日平日
for evening in list_evening:
sublist.append(evening)
else: # 今日休日
for evening_holiday in list_evening_holiday:
sublist.append(evening_holiday)
else: # 午前
if isweekday(0) == True: # 今日平日
for morning in list_morning:
sublist.append(morning)
else: # 今日休日
for morning_holiday in list_morning_holiday:
sublist.append(morning_holiday)
for i,child0 in enumerate(child):
chklist.append(sublist)
childcomplete[i] = False
###################################################################################################
# 次のチェックアイテム取得
chknum=[]
for i in child:
chknum.append(0)
def getnextcheck(childnum):
global chknum,chklist
ret='end'
if chknum[childnum] < len(chklist[childnum]):
ret = chklist[childnum][chknum[childnum]]
chknum[childnum]+=1
return ret
###################################################################################################
# チェックボタン初期化
def inittext():
global chknum, button_end, imgbutton, lb_button
initchklist()
for k,chknum0 in enumerate(chknum):
chknum[k]=0
for i,button_end0 in enumerate(button_end):
for j,button_end0sublist in enumerate(button_end0):
button_end[i][j] = False
for i,lb_button0 in enumerate(lb_button):
for j,lb_button0sublist in enumerate(lb_button0):
tempstr = getnextcheck(i)
if tempstr == 'end':
button_end[i][j] = True
lb_button[i][j].config(image = imgbutton[6], text = "")
else:
lb_button[i][j].config(image = imgbutton[0], text = tempstr)
###################################################################################################
# 時刻をしゃべる切替
def togglespeak(self):
global speaktime, lb_clock, mute
if not mute:
if not speaktime:
speaktime = True
lb_clock.config(foreground = 'red')
if threading.active_count()<=2:
th1 = threading.Thread(target=speak_time)
th1.start()
else:
lb_clock.config(foreground = 'black')
speaktime = False
lb_clock.bind('<1>', togglespeak)
###################################################################################################
# ミュート
musicon=PhotoImage(file=homedir+'/image/musicon.png')
musicoff=PhotoImage(file=homedir+'/image/musicoff.png')
def mutetoggle(self):
global mute,lb_clock,speaktime,lb_mute
if mute == False:
mute = True
lb_clock.config(foreground = '#090')
lb_mute.config(image = musicoff)
speaktime = False
else:
lb_clock.config(foreground = 'black')
lb_mute.config(image = musicon)
mute = False
lb_mute = ttk.Label(frame1, font = ('Noto Serif CJK JP', 20), text = ' ', background = 'white', image = musicon, compound='center', cursor='none')
lb_mute.grid(row=0, column=20, columnspan=2, sticky=(S,N))
lb_mute.bind('<1>', mutetoggle)
###################################################################################################
# リセット
def resetbutton(self):
inittext()
lb_rst = ttk.Label(frame1, font = ('Noto Sans Japanese', 20), text = 'リセット', background = 'white', cursor='none')
lb_rst.grid(row=0, column=18, columnspan=2, sticky=(S,N))
lb_rst.bind('<1>', resetbutton)
###################################################################################################
# ウィンドウ全画面
imgfull=PhotoImage(file=homedir+'/image/fullscreen.png')
imgfullexit=PhotoImage(file=homedir+'/image/fullscreen-exit.png')
def togglefullscreen(self):
global root,fullscreen,lb_full
fullscreen = not fullscreen
root.attributes('-fullscreen', fullscreen)
if fullscreen == True:
lb_full.config(image = imgfullexit)
else:
lb_full.config(image = imgfull)
lb_full = ttk.Label(frame1, font = ('Noto Serif CJK JP', 20), text = ' ', background = 'white', image = imgfullexit, compound='center', cursor='none')
lb_full.grid(row=0, column=22, columnspan=2, sticky=(S,N))
lb_full.bind('<1>', togglefullscreen)
###################################################################################################
# メインタイマー処理
main_counter=0
time_diff=False
chkprehour=0
chkpreminute=0
chkpresecond=0
closecount = 0
def main_timer():
global detectsound, homedir, mute,chkprehour, chkpreminute, chkpresecond, lb_clock, time_diff, rss0, displayon, rsstext, rsspos, speaktime, speaktimespan, closecount, camerawake, cameraflag, childcomplete, wakeh, sleepj, wake1h, wake1m, wake2h, wake2m
nowdate = datetime.datetime.now()
string = strftime('%H:%M')
lb_clock.config(text=string)
time_diff=True
allend = True
if nowdate.hour==chkprehour and nowdate.minute==chkpreminute and nowdate.second==chkpresecond:
time_diff=False
if displayon:
#############################################
# RSSテキスト
if len(rsstext) > rsspos+19:
rss0.config(text = rsstext[rsspos:rsspos+19])
elif rsspos < len(rsstext):
rss0.config(text = rsstext[rsspos:len(rsstext)])
rsspos += 1
if rsspos >= len(rsstext):
rsspos = 0
#############################################
# 時刻をしゃべる
if nowdate.minute % speaktimespan == 0 and speaktime and nowdate.second == 0 and time_diff and not mute:
if threading.active_count()<=2:
th1 = threading.Thread(target=speak_time)
th1.start()
for icomplete in childcomplete:
allend = (allend and icomplete)
#############################################
# 監視カメラによるWake時の処理
if camerawake>0:
if camerawake==60:
sleep(7)
subprocess.call('/usr/bin/mpg321 '+homedir+'/sound/'+detectsound+' > /dev/null 2>&1', shell=True)
camerawake=camerawake-1
if camerawake==0:
displayon=False
subprocess.call('/usr/bin/vcgencmd display_power 0', shell=True)
closecount=0
cameraoff()
cameraflag=0
#############################################
# 自動ディスプレイOFF
elif nowdate.hour>=sleeph and allend and displayon:
if closecount <20:
closecount = closecount + 1
else:
displayon=False
subprocess.call('/usr/bin/vcgencmd display_power 0', shell=True)
closecount=0
cameraoff()
cameraflag=0
#############################################
# 自動ディスプレイON + 更新
elif ((nowdate.hour==wake1h and nowdate.minute==wake1m) or (nowdate.hour==wake2h and nowdate.minute==wake2m)) and displayon==False:
displayon=True
closecount=0
subprocess.call('/usr/bin/vcgencmd display_power 1', shell=True)
sleep(3)
getcalendar()
getholiday()
getnews()
getrain()
set_jma_weather()
inittext()
#############################################
# 監視カメラOFF処理
if cameraflag > 0:
cameraflag-=1
if cameraflag == 0:
cameraoff()
#############################################
# 外部スイッチ制御
if GPIO.input(4) == True:
if displayon:
displayon=False
subprocess.call('/usr/bin/vcgencmd display_power 0', shell=True)
sleep(1)
else:
displayon=True
subprocess.call('/usr/bin/vcgencmd display_power 1', shell=True)
sleep(4)
chkprehour = nowdate.hour
chkpreminute = nowdate.minute
chkpresecond = nowdate.second
lb_clock.after(500, main_timer)
###################################################################################################
# メインプログラム
getcalendar()
getholiday()
getnews()
getrain()
set_jma_weather()
inittext()
main_timer()
if homeenable:
th0 = threading.Thread(target=cameralisten)
th0.start()
root.mainloop()
root.destroy()