【新規AIYのSDカードを作る場合】 1)MicroSDの初期化 PCにてExplorerがすべて閉じた状態でディスク管理を開いて SDドライブのパーティションをすべてボリューム削除する 2)Win32DiskImager等でMicroSDに最新AIY-SDデータを書き込む https://github.com/google/aiyprojects-raspbian/blob/aiyprojects/HACKING.md#install-our-pre-build-aiy-projects-image 3)bootのconfig.txtに次の追記をする dtoverlay=googlevoicehat-soundcard 4)RasPiにMicroSDを入れ、電源をオンにして起動する 5)RaspberryPiの設定誘導に対し、Password,Locale,Time zoneを適切に変更し、   WiFiのPassKeyを設定する 6)更に、累積updateを実施する   sudo apt-get update   sudo apt-get -y upgrade   sudo reboot する 7)AIYプロジェクト内の日本語対応が不完全なところを修正する。    vi +214 /home/pi/.local/lib/python3.7/site-packages/google/assistant/library/assistant.py    の.encode('ASCII') を.encode('UTF-8') にする。   AIYボードの初期化のために、命令を追加する。    vi +284 /home/pi/AIY-voice-kit-python/src/aiy/board.py    に、GPIO.setwarnings(False) を行追加する。 この行末カッコ内の最後のパラメタ',-1'を削除する vi +75 /home/pi/AIY-projects-python/src/aiy/assistant/auth_helpers.py 8)Segmentation Fault防止のため、1.0.1を推奨するerrorを無視して1.0.0をPre設置 sudo pip3 uninstall google-assistant-library    pip3 install google-assistant-library==1.0.0   pip3 install google-assistant-library    pip3 install google-auth-oauthlib 9)下記14)から実施する 【既存Raspbianに書き込む場合】 1)MicroSDの初期化 PCにてExplorerがすべて閉じた状態でディスク管理を開いて SDドライブのパーティションをすべてボリューム削除する 2)Win32DiskImager等でMicroSDに最新RaspbianOSを書き込む https://downloads.raspberrypi.org/raspbian_full_latest   2020-02-05-raspbian-buster-full.img(Busterバージョン) 3)bootのconfig.txtに次の追記をする dtoverlay=googlevoicehat-soundcard   また次をコメントアウトする   #dtparam=audio=on なお、ミニLCDを使う場合は、次の追記も必要になることが多い。   7Inch 1024x600だと、config.txtに次の追加を指示されている。 max_usb_current=1 hdmi_force_hotplug=1 config_hdmi_boost=7 hdmi_group=2 hdmi_mode=87 hdmi_cvt 1024 600 60 6 0 0 0 4)bootのトップにsshダミーファイルをおく 5)RasPiにMicroSDを入れ、電源をオンにして起動する   (もしRasPi4だった場合は、まだgooglevoicehat-soundcardのボードをドッキング    させずに電源を入れる) 6)RaspberryPiの設定誘導に対し、Password,Locale,Time zoneを適切に変更し、   WiFiのPassKeyを設定する   (スマホのGoogleツールでデバイス設定するとき、同一AP配下となる様に考慮)   updateはここではskipする 7)PCからSSHツールにてraspberrypi.localに接続する SSHで時々の休止が気になるときは、次のコマンドで省エネを停止できる sudo iw dev wlan0 set power_save off 8)ダウンロードリストを拡張して、UPGRADEを実施する   echo "deb https://packages.cloud.google.com/apt aiyprojects-stable main" | sudo tee /etc/apt/sources.list.d/aiyprojects.list   curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -   sudo apt-get update   sudo apt-get -y upgrade   sudo reboot する   (もしRasPi4だった場合は、ここで画面出力を2Kに確定し、Shutdownして    googlevoicehat-soundcardのボードをドッキングさせる) 9)AIY V1の追加ドライバーのインストールをする sudo apt-get install -y leds-ktd202x-dkms sudo apt-get install -y aiy-voicebonnet-soundcard-dkms sudo apt-get install -y aiy-python-wheels 10)AIYのadcとi2cの変更   処理不要になりました sudo reboot する 11)Audioのインストール   sudo apt-get install -y pulseaudio pavucontrol sudo mkdir -p /etc/pulse/daemon.conf.d/ echo "default-sample-rate = 48000" | sudo tee /etc/pulse/daemon.conf.d/aiy.conf   タイムアウトだったら、上手く行くまでinstallを再試行する。   また、VLCのHDMIからの音声出力のためには、#dtparam=audio=onのコメントを   外し、デバイスをpavucontrol で入れ替える 12)protobufのインストール 不要になりました 13)実行準備   git clone https://github.com/google/aiyprojects-raspbian.git AIY-projects-python   sudo mv AIY-projects-python /opt/aiy/projects-python   ln -s /opt/aiy/projects-python AIY-projects-python   ln -s /home/pi/AIY-projects-python AIY-voice-kit-python   sudo pip3 install -e AIY-projects-python   sudo cp /opt/aiy/projects-python/src/aiy/assistant/auth_helpers.py \ /opt/aiy/projects-python/src/aiy/assistant/auth_helpers.py.org   sudo vi +75 /opt/aiy/projects-python/src/aiy/assistant/auth_helpers.py    この行末カッコ内の最後のパラメタ',-1'を削除する (vi +75 /home/pi/AIY-projects-python/src/aiy/assistant/auth_helpers.py)   pip3 install google-assistant-library==1.0.0 #  (Segmentation Fault防止のため、1.0.1を推奨するerrorを無視して1.0.0をPre設置)   pip3 install google-assistant-library   pip3 install google-auth-oauthlib   sudo pip3 install grpcio   AIYプロジェクト内の日本語対応が不完全なところを修正する。    vi +214 /home/pi/.local/lib/python3.7/site-packages/google/assistant/library/assistant.py    の.encode('ASCII') を.encode('utf-8') にする。   AIYボードの初期化のために、命令を追加する。    vi +284 /home/pi/AIY-voice-kit-python/src/aiy/board.py    に、GPIO.setwarnings(False) を行追加する。 14)次の確認を実施する   amixer sset Master 50% で、音量を半分にしておく   export PYTHONPATH="/home/pi/AIY-voice-kit-python/src" /home/pi/AIY-voice-kit-python/checkpoints/check_audio.py 15)次のURLに基づき、アカウント登録をする https://tech.nikkeibp.co.jp/atcl/nxt/column/18/00255/042600011/?P=4   assistant.json ファイルを /home/piに置く 16)サンプルコードの実行   export PYTHONPATH="/home/pi/AIY-voice-kit-python/src"   $PYTHONPATH/examples/voice/assistant_library_with_button_demo.py    初回のスクリプト実行時にURLが表示され、ブラウザでアクセスすることで認証文字列が    返される。それをスクリプト実行中の質問に貼り付ける。 17)デバイスの登録   サンプルコードの実行でしばらく放置し、スマホのHomeアプリの「アシスタント」に出現した   新たなデバイス(Voice Kit)の言語を日本語に設定し、属性参照を許可する。 以下は、SmartTV(PX-W3U4,PX-Q3U4使用)に変身させるときの追加作業 18)TVチューナーサポートシステムのインストール   curl --silent http://pmp-jp.com/test/TVget.sh | sh 19)N2TTS,youtube-dl,mojimojiのインストール このURLに従う:https://support.kddi-research.jp/n2/service.html (wget -O n2-linux-foc.tgz https://support.kddi-research.jp/n2/download.php?type=5)   sudo -H pip3 install youtube-dl jq pip3 install mojimoji pip3 install oauth2client   pip3 install --upgrade google-api-python-client 【RasPi4に備えて、multiTV.shから aiyTV.shを用意】   MPEG2に対するプレーヤーとして、OMXからVLCメディアプレーヤに切り替える。   <>   ツール→設定→インターフェイス設定⇒インターフェイスにビデオを統合 のチェックを外す   ツール→設定→インターフェイス設定⇒単一インスタンスで実行 のチェックを外す   ツール→設定→オーディオ設定⇒出力モジュール を ALSAオーディオ出力 に変える   ツール→設定→オーディオ設定⇒デバイス を Playback/recording~ とする   ツール→設定→ホットキーの設定⇒スケーリング係数の増加 を Page Up に変える   ツール→設定→ホットキーの設定⇒スケーリング係数の減少 を Page Down に変える   「保存」をclickする   <sayJ関数の追加>   vi $PYTHONPATH/aiy/voice/tts.py def sayJ(text, asynch=0, lang='ja-JP', volume=60, pitch=130, speed=100, device='sysdefault:CARD=ALSA'): # logging.info(text) with tempfile.NamedTemporaryFile(suffix='.wav', dir=RUN_DIR) as f: cmd = 'n2tts -p VOLUME=%s -o %s "%s" && aplay -q -D %s %s' % \ (volume, f.name, text, device, f.name) if asynch==1: subprocess.Popen(cmd, shell=True) else: subprocess.call (cmd, shell=True) 20)TV用コマンド追加サンプルコード  (下のサンプルassistant_library_with_remocon.pyを追加する) 開始コマンドは、「/home/pi/aiyTV/aiySP.sh」となる                                        以上 #!/usr/bin/env python3 # Copyright 2017 Google Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Run a recognizer using the Google Assistant Library with button support. The Google Assistant Library has direct access to the audio API, so this Python code doesn't need to record audio. Hot word detection "OK, Google" is supported. It is available for Raspberry Pi 2/3/4 only; Pi Zero is not supported. """ import tkinter as tk from tkinter.constants import RIGHT, LEFT, Y, BOTH, END import tkinter.scrolledtext import re, logging import platform import os, sys, subprocess, time import threading from datetime import datetime, timedelta from time import sleep import requests, re, mojimoji from bs4 import BeautifulSoup HOME = os.environ["HOME"]+'/aiyTV' os.environ["DISPLAY"] = ":0.0" WIN_ID = ["",""] _ASCII_re = re.compile(r'\A[\x00-\x7f]*\Z') def is_ascii_str(text): return isinstance(text, str) and _ASCII_re.match(text) getTe = 'curl https://api.nature.global/1/devices -H "Authorization: Bearer ' Token = "y-3u16mkVVPT55wPA3CdVC76pCevaO2QNXWuniXlweo.........................." getTail = '" 2>/dev/null | jq .[].newest_events.te.val | tail -1' ipad = subprocess.check_output(['hostname -I | cut -d " " -f 1'],shell=True) ipad = ipad.decode().strip() buttonFlag = False events = None from google.assistant.library.event import Event, EventType from aiy.assistant import auth_helpers from aiy.assistant.library import Assistant from aiy.board import Board, Led from aiy.voice import tts thread = None frm = None player = None caster = None postReq = "" volcon = re.compile(r'(見たい)|(流して)|(に変えて)|(に切り替えて)|(チラ見)|(テレビを消して)|(小さく)|(下げて)|(を低く)|(大きく)|(上げて)|(を高く)|(表裏を入替)|(入れ替え)|(裏番組を取り消して)|(裏を消して)|(音声出力先を)|(スピーカーを)|(番組表を)|(画面操作)') def aoutChng(): indx = subprocess.check_output(['amixer -c 0 cget numid=3 | tail -1 | cut -d = -f 2'],shell=True).strip().decode() indx1 = str(int(indx) + 1) subprocess.call (['amixer -c 0 cset numid=3 '+indx1],shell=True) indx = subprocess.check_output(['amixer -c 0 cget numid=3 | tail -1 | cut -d = -f 2'],shell=True).strip().decode() if indx < indx1: subprocess.Popen(['amixer -c 0 cset numid=3 1'],shell=True) ttsJ("切り替えましたが、聞こえますか?") def ttsJ(text,sw=1): global frm if sw and frm: frm._buff.set(text) tts.sayJ(text) def btn_click(): global frm, thread, events # text = subprocess.check_output([HOME+"/gold.sh"],shell=True).decode().strip() text = frm._eline.get() print (len(text)) if text!="": frm._buff.set(text) print('Assistant: %s' % text) if events: events.offer(Event(EventType.ON_RECOGNIZING_SPEECH_FINISHED,{'text':'Return:'+text})) else: breakCk("YouTube") frm._text.destroy() frm._text= None def iconify_window(TUNER_NO,ch): oldlen = 0 movec = 0 while movec<4: for i in subprocess.Popen(['xprop','-root'], stdout=subprocess.PIPE).stdout: if '_NET_CLIENT_LIST(WINDOW):' in i.decode(): client = i.decode().strip().split(', ') if oldlen==0: oldlen = len(client) + 1 if len(client) > oldlen: movec = 4 else: time.sleep(4) movec += 1 break subprocess.call ([HOME+'/vlcvol.sh','set',ch,TUNER_NO]) movec = 0 for id in reversed(client): if id[0:2]=="0x": for j in subprocess.Popen(['xprop','-id', id], stdout=subprocess.PIPE).stdout: if 'WM_WINDOW_ROLE(STRING) = "vlc-video"' in j.decode(): movec += 1 WIN_ID.insert(0,id) if movec==1: print("videoID="+id+' '+str(movec)) if (TUNER_NO=="3" or TUNER_NO=="1"): subprocess.call (['xwit -move '+str(width-570)+' '+str(width/2-260)+' -id ' + id], shell=True) else: print ('xwit -resize '+ str(width)+' '+str(int(width*9/16))+' -id ' + id) subprocess.call (['xwit -resize '+ str(width)+' '+str(int(width*9/16))+' -id ' + id], shell=True) break elif 'WM_WINDOW_ROLE(STRING) = "vlc-main"' in j.decode(): subprocess.call (['xwit -iconify -id ' + id], shell=True) if movec==2: print ("WIN_ID:"+WIN_ID[0]+','+WIN_ID[1]) return break def TVkeyPress(ev): global frm, thread print (ev.keysym,"入力") if ev.keysym == "Return": btn_click() elif ev.keysym == "Up": subprocess.Popen(['xwit -rmove 0 -20 -names テキスト入力'],shell=True) elif ev.keysym == "Down": subprocess.Popen(['xwit -rmove 0 20 -names テキスト入力'],shell=True) elif ev.keysym == "Left": subprocess.Popen(['xwit -rmove -20 0 -names テキスト入力'],shell=True) elif ev.keysym == "Right": subprocess.Popen(['xwit -rmove 20 0 -names テキスト入力'],shell=True) class CHTBL(threading.Thread): def __init__(self, f = True): threading.Thread.__init__(self) self._running = f def start(self,tbl): global frm view = tkinter.Tk() self.view = view view.geometry('300x300+0+'+str(frm._height-400)) if tbl[1::2][0]==0: view.title('衛星放送CH番号') elif tbl[1::2][0]==1: view.title('ラジオ局名') view.bind("",self.view_scroll) btn = tkinter.Button(view, text='終了', command=self.view.destroy) btn.place(x=110,y=270) st = tkinter.scrolledtext.ScrolledText(view, width=30, height=15, font=( u'IPAゴシック',12)) st.focus_set() st.pack() for index, item in enumerate(tbl[1::2]): if item>1: st.insert(END, str(item)+' '+tbl[0::2][index]+"\n") st.config(state='disabled') self._st = st view.after(1000, self._check_to_quit) view.mainloop() del self._st del self.view def _check_to_quit(self): if self._running: self.view.after(1000, self._check_to_quit) else: self.view.destroy() def view_scroll(self,ev): self.view.focus() if ev.keysym == "Return" or ev.keysym == "XF86Back": self.view.destroy() elif ev.keysym == "Up": self._st.yview('scroll',-10,'unit') elif ev.keysym == "Down": self._st.yview('scroll',10,'unit') viewT = None def view_kick(self,tbl): global viewT viewT = CHTBL() viewT.start(tbl) class MyFrame(tk.Frame): def __init__(self, master=None): tk.Frame.__init__(self, master) self.master.title('ボタン入力') # self.master.attributes('-topmost', 'true') MyFrame._buff = tk.StringVar() MyFrame._text = None MyFrame._mode = ["","0.6","0"] label = tk.Label(root, textvariable = MyFrame._buff, font=(u'IPAゴシック 14')) label.pack() MyFrame._buff.set("セッション確立中・・・・") label.focus_set() def keyPress(self,ev): global frm, thread print (ev.keysym,"入力") if ev.keysym == "Return": if frm._text: btn_click() elif thread and thread._can_start_conversation: if frm._text==None: frm._text = tk.Tk() frm._text.geometry('300x70+0+'+str(frm._height-160)) frm._text.title('テキスト入力') frm._eline = tk.Entry(frm._text,width=280) frm._eline.focus_set() frm._eline.pack() btn = tk.Button(frm._text, text='質問', command=btn_click) btn.place(x=110,y=40) frm._text.bind("", TVkeyPress) frm._buff.set("お呼びですか?") elif ev.keysym == "XF86Back" or ev.keysym == "Escape": subprocess.Popen([HOME+'/TVoff.sh']) ttsJ('お疲れ様でした。') frm._led.state = Led.OFF subprocess.Popen(["pkill","python"]) sys.exit(0) elif ev.keysym == "XF86Search": if thread and thread._can_start_conversation: thread._assistant.start_conversation() else: breakCk("YouTube") elif ev.keysym == "Up": subprocess.Popen(['xwit -rmove 0 -20 -names ボタン入力'],shell=True) elif ev.keysym == "Down": subprocess.Popen(['xwit -rmove 0 20 -names ボタン入力'],shell=True) elif ev.keysym == "Left": subprocess.Popen(['xwit -rmove -20 0 -names ボタン入力'],shell=True) elif ev.keysym == "Right": subprocess.Popen(['xwit -rmove 20 0 -names ボタン入力'],shell=True) def youtube(self,words,cast=False,same=False): global thread, player, caster, ipad, buttonFlag, postReq self._assistant.stop_conversation() if words == "": postReq = "YT" ttsJ("何を見たいですか?") return buttonFlag = True breakCk("YouTube") thread = self titleB = subprocess.check_output(["python3 ./youtubeView.py --q '"+words+ "'"],shell=True).decode() ttlid = titleB[8:19] title = titleB[20:] if title=="":return(False) print("タイトルは、",title,ttlid) theme = ttlid+' '+title n = title.find('/') if n!=-1 and is_ascii_str(title[:n]):title = title[n+1:] title = title.replace(' ','、') n = title.find('20') while n!=-1: try: date = datetime.strptime(title[n:n+8], '%Y%m%d') title = title[:n]+date.strftime('%Y年%m月%d日')+title[n+8:] if title[n+5]=="0":title=title[:n+5] + " " + title[n+6:] if title[n+8]=="0":title=title[:n+8] + " " + title[n+9:] n = title.find('20',n+11) except ValueError:n=title.find('20',n+2) ttsJ(title,1) print("適切な画質のコンテンツを選択しています") if cast: now = None if caster:caster.poll() if not caster or caster.returncode: now = datetime.now() caster = subprocess.Popen(["bash -lc 'python3 ./notifier.py'"],shell=True,stdin=subprocess.PIPE,stdout=subprocess.DEVNULL) htmlB = subprocess.check_output(["youtube-dl -J 'https://www.youtube.com/watch?v="+ ttlid +"'"+ " | jq -r '.formats| .[] | select( .acodec | contains(\"mp4\") ) | select( .width >= 800 ) | .url '"],shell=True) if len(htmlB)==0: print("画質を下げて探してみます") # ttsJ("画質を下げて探してみます") htmlB = subprocess.check_output(["youtube-dl -J 'https://www.youtube.com/watch?v="+ ttlid +"'"+ " | jq -r '.formats| .[] | select( .acodec | contains(\"mp4\") ) | select( .width >= 640 ) | .url '"],shell=True) if len(htmlB)==0: print("携帯用のコンテンツも探してみます") htmlB = subprocess.check_output(["youtube-dl -J 'https://www.youtube.com/watch?v="+ ttlid +"'"+ " | jq -r '.formats| .[] | select( .acodec | contains(\"mp4\") ) | select( .width >= 320 ) | .url '"],shell=True) if len(htmlB)==0: ttsJ("済みません。再生できません。") return(False) html = htmlB.decode('utf-8').split()[0] if cast: if now: past = (datetime.now()-now).total_seconds() if past<12:sleep (12-past) cmdl = "curl -G --data-urlencode url='"+html+"' http://"+ipad+":5000/play/" # print(cmdl) subprocess.Popen([cmdl],shell=True) breakCk("Cast") return (True) else: # subprocess.call('tvservice -p', shell=True) cmdl = "omxplayer --win 0,0,1024,560 -o alsa --vol -1400 '" +html+ "'" # alsa:plughw:0,0 # print(cmdl) player = subprocess.Popen([cmdl],shell=True,stdin=subprocess.PIPE,stdout=subprocess.DEVNULL) breakCk("YouTube") return (True) def singyo(): global player player = subprocess.Popen(["omxplayer",'-o','alsa',"--vol","-1400","http://soto-tokai.net/mp/hannyashingyou.mp3"],stdin=subprocess.PIPE,stdout=subprocess.DEVNULL) breakCk("お経") return (True) def kouka(): global player player = subprocess.Popen(["omxplayer",'-o','alsa',"--vol","-1400","http://pmp-jp.com/blog/fujii/hakua.m4a"],stdin=subprocess.PIPE,stdout=subprocess.DEVNULL) breakCk("校歌") return (True) def breakCk(ex): global thread, player, caster, ipad, buttonFlag, events if ex=="YouTube":subprocess.call(["xset -display :0.0 dpms 0 0 0 -dpms s off"],shell=True) eventC = events.qsize() while buttonFlag==False and events.qsize()eventC:events.get(False) buttonFlag = False if ex=="YouTube": subprocess.call(["xset -display :0.0 dpms 600 600 600 s on"],shell=True) if thread: thread._can_start_conversation = True def power_off_pi(): ttsJ('ごきげんよう') subprocess.call('sudo shutdown now', shell=True) def reboot_pi(): ttsJ('またね!') subprocess.call('sudo reboot', shell=True) def say_ip(): ip_address = subprocess.check_output("hostname -I | cut -d' ' -f1", shell=True) ttsJ('この IP アドレスは %s です' % ip_address.decode().strip()) def say_Te(): temperature= subprocess.check_output(getTe+Token+getTail, shell=True) ttsJ('現在の室温は、%s ℃です' % temperature.decode().strip()) def TVview(chName, tira, assistant): global HOME,buttonFlag,postReq,frm assistant.stop_conversation() if chName == "テレビ": postReq = "TV" ttsJ("どのチャンネルを見たいですか?") return ch = chName.replace("テレビを","") sw = "N" if ch[-5:]=="チャンネル": ch = ch[:-5] if ch!="サブ": sw = "n" ch = ch.strip() if sw=="n": ch = int(ch) subch = 0 buttonFlag = True breakCk("YouTube") chTable = ["サブ",0,0,1, "NHK",27,1,1024, "NHK教育",26,2,1032,"NHK教育",26,2,1032, "日テレ",25,4,1040, "テレ朝",24,5, 1064,"テレビ朝日",24,5,1064, "TBS",22,6,1048,"TBS",22,6,1048, "テレ東",23,7,1072, "テレビ東京",23,7,1072, "フジテレビ",21,8,1056, "東京 MX",16,9,23608,"東京MX",16,9,23608, "TVK",31,3,24632,"TVK2",32,3,24633] rzTable = [ "ラジオ局",1, "NHK: JOAK",51, "NHK-FM: JOAK-FM",52, "TBSラジオ: TBS",53, "文化放送: QRR",54, "日本放送: LFR",55, "Radio Nippon: JORF",56, "Radio Nikkei1: RN1",57, "Radio Nikkei2: RN2",58, "Inter FM: INT",59, "Tokyo FM: FMT",60, "J-WAVE: FMJ",61, "bayfm 78.0MHz: BAYFM78",62, "NACK5: NACK5",63, "FM yokohama 84.7: YFM",64, "放送大学: HOUSOU-DAIGAKU",65] bsTable = ["チャンネル番号",0, "曲番",0, "ラジオ局",1, "NHK BS",101, "NHK BS 1",101, "NHK BS プレミアム",103, "NHK bsプレミアム",103, "BS 日テレ",141, "BS 朝日",151, "BS TBS",161, "BS テレ東",171, "BS テレ東 2",172, "BS テレ東 3",173, "BS フジ",181, "WOWOW プライム",191, "プライム",191, "WOWOW ライブ",192, "ライブ",192, "WOWOW シネマ",193, "シネマ",193, "スターチャンネル1",200, "スターチャンネル2",201, "スターチャンネル3",202, "BS イレブン",211, "BS トゥエルビ",222, "グリーンチャンネル",234, "BS アニマックス",236, "FOX スポーツエンタ",238, "BS スカパー",241, "J スポーツ1",242, "J スポーツ2",243, "J スポーツ3",244, "J スポーツ4",245, "BS 釣りビジョン",251, "シネフィル WOWOW",252, "日本映画専門",255, "ディズニー",256, "ディーライフ",258, "Dlife",258, "QVC",161, "東映チャンネル",218, "衛星劇場",219, "映画・chNECO",223, "ザ・シネマ",227, "ムービープラス",240, "スカイA",250, "GAORA",254, "日テレジータス",257, "ゴルフネットワーク",262, "SKY STAGE",290, "時代劇専門ch",292, "ファミリー劇場",293, "ホームドラマCH",294, "MONDO TV",295, "日テレプラス",300, "エンタメ~テレ",301, "フジテレビONE",307, "フジテレビTWO",308, "フジテレビNEXT",309, "スーパー!ドラマTV",310, "AXN 海外ドラマ",311, "FOX",312, "女性ch/LaLa",314, "AXNミステリー",316, "KBS World",317, "Mnet",318, "スペシャプラス",321, "スペースシャワーTV",322, "MTV",323, "ミュージック・エア",324, "エムオン!",325, "歌謡ポップス",329, "キッズステーション",330, "カートゥーン",331, "AT-X",333, "ディズニージュニア",339, "ディスカバリー",340, "アニマルプラネット",341, "ヒストリーチャンネル",342, "ナショジオ",343, "日テレNEWS24",349, "TBS NEWS",351, "BBCワールド",353, "CNNj",354, "囲碁・将棋チャンネル",363] if ch in chTable: if sw=="N": chn = chTable[1::4][chTable[0::4].index(ch)] else: chn = chTable[1::4][chTable[2::4].index(ch)] ch = chTable[0::4][chTable[2::4].index(ch)] if chn==0: if os.path.isfile(HOME+"/tmp_file"): f = open(HOME+"/tmp_file") data=f.read() frm._mode=data.split(",") f.close() chn = int(frm._mode[0]) subch = int(frm._mode[2])+ 1 ch = chTable[0::4][chTable[1::4].index(chn)] + " SubCH" else: subch = chTable[3::4][chTable[1::4].index(chn)] tuner = str(2+tira) elif ch in bsTable: chn = ch tuner = str(tira) if sw=="N": chn = bsTable[1::2][bsTable[::2].index(ch)] if chn==0: view_kick(assistant,bsTable) return elif chn==1: view_kick(assistant,rzTable) return else: ttsJ('切替先は、%sチャンネルですね' % chn,1) else: ch = bsTable[::2][bsTable[1::2].index(chn)] ttsJ('%sですね' % ch,1) ch = '"'+ch+'"' elif ch in rzTable: chn = ch ch = rzTable[::2][rzTable[1::2].index(chn)] chname, chID = ch.split(':') ttsJ('%sですね' % chname,1) subprocess.Popen([HOME+'/radiko.sh',chID.strip(), chname]) return else: ttsJ('%sが認識できません' % ch) return subprocess.Popen([HOME+'/aiyTV.sh',str(chn), ch, tuner, str(subch)]) print ( "%s,%s,%s,%s" % (HOME+'/aiyTV.sh',chn, ch, subch)) iconify_window(tuner,str(chn)) def news_get(when): # 最新、今朝、今日 global player content = None xmlB= requests.get("https://www.nhk.or.jp/r-news/podcast/nhkradionews.xml") xml = xmlB.text bsObj = BeautifulSoup(xml, "xml") items = bsObj.find_all('item') for item in items: if when=="最新": content = item.enclosure['url'] break if when=="今日" and item.title.text[-8:]=="きょうのニュース": content = item.enclosure['url'] if item.title.text[:5]!=items[0].title.text[:5]:content=None break if when=="今朝" and item.title.text[-7:]=="けさのニュース": content = item.enclosure['url'] break if not content:content = items[0].enclosure['url'] if content: print(content) player = subprocess.Popen(["omxplayer",'-o','alsa',"--vol","-1400",content],stdin=subprocess.PIPE,stdout=subprocess.DEVNULL) breakCk("ニュース") class MyAssistant: """An assistant that runs in the background. The Google Assistant Library event loop blocks the running thread entirely. To support the button trigger, we need to run the event loop in a separate thread. Otherwise, the on_button_pressed() method will never get a chance to be invoked. """ def __init__(self): self._task = threading.Thread(target=self._run_task) self._can_start_conversation = False self._assistant = None self._board = Board() self._board.button.when_pressed = self._on_button_pressed def start(self): """Starts the assistant. Starts the assistant event loop and begin processing events. """ global thread thread = self self._task.start() def _run_task(self): global events, frm try: credentials = auth_helpers.get_assistant_credentials() except: if frm: frm._buff.set('再起動してください') sleep(10) subprocess.Popen(["pkill","python"]) sys.exit(0) with Assistant(credentials) as assistant: self._assistant = assistant assistant._tira = 0 if frm: frm._led = self._board.led events = assistant.start() for event in events: self._process_event(self._assistant,self._board.led,event) def _readline(self): if os.path.exists("/home/pi/aiyIn.txt"): return subprocess.check_output(["head -1 /home/pi/aiyIn.txt"],shell=True).decode() return ("") def _process_event(self, assistant, led, event): global HOME, buttonFlag, postReq, volcon, frm logging.info(event) postFwd = "" if event.type == EventType.ON_START_FINISHED: led.status = Led.BEACON_DARK # Ready. self._can_start_conversation = True # Start the voicehat button trigger. logging.info('「ねぇグーグル」と言うか、またはボタンを押して、話して下さい。 ' '「終了」で終わります。...') frm._buff.set('「ねぇグーグル」と言うか、またはボタンを押して、話して下さい。 ') elif event.type == EventType.ON_CONVERSATION_TURN_STARTED: self._can_start_conversation = False led.state = Led.ON # Listening. frm._buff.set("お呼びですか?") subprocess.Popen(['xwit -focus -names ボタン入力'],shell=True) postFwd = "start" elif (event.type == EventType.ON_CONVERSATION_TURN_TIMEOUT or event.type == EventType.ON_NO_RESPONSE): text = self._readline() if text!="": postFwd = "key" led.state = Led.BEACON_DARK # Ready. self._can_start_conversation = True elif event.type == EventType.ON_RECOGNIZING_SPEECH_FINISHED and event.args: text = event.args['text'] #.lower() print('あなたの入力:', text) if text[:7]=="Return:": text = text[7:] postFwd = "key" frm._buff.set(text) if postReq != "": if postReq=="TV": postReq = "" if text[-2:]=="です": text=text[:-2] text += "に変えて" elif postReq=="YT": postReq = "" if text[-2:]=="です": text=text[:-2] text = "YouTube で"+text+"を見たい" # print('修飾された発言:', text) if text == u'シャットダウンして': power_off_pi() elif text == u'再起動して': reboot_pi() elif text == u'止めて': # assistant.stop_conversation() buttonFlag = True breakCk("YouTube") subprocess.Popen([HOME+'/TVoff.sh']) elif text == u'番組表を見たい': subprocess.Popen([HOME+'/DispTVProg.sh']) elif text[0:2]=='裏で' or text[0:2]=='表で' or text[0:5]=='aiy で' or text[0:5]=='aiy の' \ or text[-4:]=='に変えて' or text[-5:]=='切り替えて': reidx = volcon.search(text) if reidx: reidx = reidx.lastindex tira = 0 if text[0:3]=='aiy': hd = 5 else: hd = 2 if text[0:1]=="裏": tira = 1 if reidx in {1,2}: TVview(text[hd:-4],tira,assistant) elif reidx==3: TVview(text[0:-4],tira,assistant) elif reidx==4: TVview(text[0:-6],tira,assistant) elif reidx==5: TVview(text[hd:-7],1,assistant) if reidx==6: subprocess.Popen([HOME+'/TVoff.sh']) elif reidx in {7,8,9}: subprocess.call(["amixer","sset","Master","10%-"]) elif reidx in {10,11,12}: subprocess.call(["amixer","sset","Master","10%+"]) elif reidx in {13,14}: if WIN_ID[0]!=WIN_ID[1]: assistant._tira ^= 1 subprocess.Popen([HOME+'/vlcvol.sh','exch',str(tira^1),WIN_ID[0],WIN_ID[1]]) elif reidx in {15,16}: if assistant._tira==1: subprocess.call([HOME+'/vlcvol.sh','exch','0',WIN_ID[0],WIN_ID[1]]) subprocess.call ([HOME+'/vlcvol.sh',"only"]) subprocess.call ([HOME+'/vlcKill.sh',"2","1"]) subprocess.Popen([HOME+'/vlcKill.sh',"0","1"]) elif reidx in {17,18}: aoutChng() elif reidx==19: subprocess.Popen([HOME+'/DispTVProg.sh']) elif reidx==20: subprocess.Popen(['xwit -rmove 0 -40 -names ボタン入力'],shell=True) subprocess.Popen(['python3',HOME+'/btnBar.py']) elif text == u'終了': assistant.stop_conversation() subprocess.Popen([HOME+'/TVoff.sh']) ttsJ('お疲れ様でした。') led.state = Led.OFF subprocess.Popen(["pkill","python|python3"]) sys.exit(0) elif 'YouTube で' in text: subprocess.call ([HOME+'/TVoff.sh']) cast = False if "見" in text[-6:]:text=text[:text.find('見')-1] elif "をキャスト" in text[-10:] or "を キャスト" in text[-10:]: text=text[:text.find('を')] cast = True ans = youtube(self,text[9:],cast) elif '画面を小さく' in text: subprocess.Popen([HOME+'/dbuscntl2.sh','0','shrink']) elif '画面を大きく' in text: subprocess.Popen([HOME+'/dbuscntl2.sh','0','normal','h']) elif '一高の校歌を聞' in text: kouka() elif '般若心経を聞' in text: singyo() elif 'ニュース' in text: news_get(text[0:2]) elif u'IP アドレスは' in text: say_ip() elif u'部屋の温度は' in text: say_Te() else: postFwd += "cont" if event.type == EventType.ON_RECOGNIZING_SPEECH_FINISHED and postFwd=="": assistant.stop_conversation() print('Stop conversation') # ウェイクワードなしで対話を継続(TV,YT継続を含む) if postFwd=="keycont": print('Assistant: %s' % text) assistant.send_text_query(text) elif postReq=="TV" or postReq=="YT": # TV,YT assistant.start_conversation() elif event.type == EventType.ON_CONVERSATION_TURN_FINISHED: self._can_start_conversation = True led.state = Led.BEACON_DARK # Ready. if frm._buff.get()=="お呼びですか?": frm._buff.set("") subprocess.Popen(['xwit -focus -names ボタン入力'],shell=True) elif event.type == EventType.ON_END_OF_UTTERANCE: led.state = Led.PULSE_QUICK # Thinking. frm._buff.set( frm._buff.get()+"...只今解析中・・・") elif event.type == EventType.ON_RENDER_RESPONSE: text = event.args['text'] #.lower() # re.sub(r"[^\u0000-\uFFFF]","□□",text) # print (text) try: frm._buff.set(text) except: i = len(text) while ord(text[i-1:i]) > 0xFFFF: i=i-1 print(str(len(text))+" "+ str(i)) frm._buff.set(text[:i]) elif event.type == EventType.ON_ASSISTANT_ERROR and event.args and event.args['is_fatal']: sys.exit(1) def _on_button_pressed(self): # Check if we can start a conversation. 'self._can_start_conversation' # is False when either: # 1. The assistant library is not yet ready; OR # 2. The assistant library is already in a conversation. if self._can_start_conversation: self._assistant.start_conversation() else:on_button_press() def on_button_press(): global buttonFlag, events if buttonFlag==False and events: print('Button pressed') buttonFlag = True breakCk("YouTube") subprocess.Popen([HOME+'/TVoff.sh']) def main(f): global frm logging.basicConfig(level=logging.INFO) subprocess.call(["amixer sset Master 108%"],shell=True,stdout=subprocess.DEVNULL) frm = f MyAssistant().start() if __name__ == '__main__': os.environ["DISPLAY"] = ":0.0" os.chdir(HOME) subprocess.call ([HOME+'/initAout.sh']) p = subprocess.Popen([HOME+'/sizepy.sh'],encoding='utf8',stdout=subprocess.PIPE) width,err = p.communicate() width = int(width.strip()) root=tk.Tk() height = int(width*9/16) root.geometry(str(width)+"x40+0+"+str(height-60)) root.option_add('*Button.font', u'IPAゴシック 12') fm = MyFrame() fm.focus_set() fm.pack(fill='x',side='left') fm._height = height root.bind("", fm.keyPress) main(fm) fm.mainloop()