【ラズパイで猫ちゃんカメラ】 その8 テスト撮影用のスイッチ動作(GPIO)と全体

前回 前回はプログラムの自動起動について書きました 今回 GPIOを使って、スイッチの状態を読み取りテスト撮影などの機能を盛り込みます また、これまでの内容を総括した全体プログラムを紹介します 現在、猫カメラとして実働しているものになります お出かけには必須になりました 作ってよかった😁 間が結構飛んでいるかもしれませんが、大きな心でご容赦ください笑 スイッチの機能 カメラの設置場所を決めるために撮影用のスイッチをつけます 以下のような機能をつけます 短押し(5秒以下)でテスト撮影 長押し(5秒以上)でラズパイシャットダウン 撮ったらLINEします スイッチの状態を読み取ってみる スイッチは秋月電子でこれを買いました↓ プッシュスイッチは電源(3.3V)ースイッチーラズパイ のように接続します GPIO26に接続し、プルダウンに設定しています ですので何もしなければ0Vで、スイッチを押せばhigh(3.3V)となります whileループ内でスイッチの状態を読み取ります プルダウンとして設定しているので、何もしなければGPIO26の電圧はGNDレベル=0Vでありaの値をprintすると0となります スイッチを押すとGPIO26の電位がHigh=3.3Vとなるのでa=1となります ctrl + Cでwhileループから抜けて終了です import RPi.GPIO as GPIO button = 26 GPIO.setmode(GPIO.BCM) GPIO.setup(button,GPIO.IN,pull_up_down = GPIO.PUD_DOWN) # プルダウン try: while True: a = GPIO.input(button) print(a) except KeyboardInterrupt: # ctrl + C print('\nbreak') GPIO.cleanup(button) 短押し、長押し判定 割り込みは使っていません whileループ中に50msec毎にスイッチを見に行っています 50msecはチャタリングを考慮しての時間です ボタンを最初に押してから離すまでの時間を測り、5秒以下なら短押しと判定、5秒以上なら長押しと判定しています 長押しでシャットダウン import osをして sudo shutdown -h now を実行するだけです 長押し時の処理にこれを実行します 実働プログラム レビジョン7まで上げてやっと安定しました 今のところ問題なく動いているプログラムです 繰り返しの記述があるので、簡素化したいところです が、まあ動いているのでやる気が出たら手を入れます笑 """ にゃんCAM 更新履歴 2021/09/26 : rev0 new 2021/09/29 : rev1 撮影時のみカメラ起動、日本語化 2021/10/08 : rev2 リトライ処理実装(おもにwifiが無くなったことを想定) 2021/10/09 : rev3 カメラ2台対応(1台用とは別にする 1台用はrev2),カメラ自動認識(1台でも認識はするがエラーになる。あくまで2台用) 2021/10/11 : rev4 カメラ2台用 リトライ処理削除(リトライはsystemdのRestart=alwaysが行うため) 2021/10/13 : rev6 カメラ2台用(ベース:rev4) 起動時にまず撮影 2021/10/13 : rev7 いじり respをそとに resp間にsleep(resp_time) 送る間隔が早すぎてエラーになってたかも(ネットが低速で画像が送れない) 自動起動 sudo nano /lib/systemd/system/nyancam.service sudo systemctl start nyancam.service sudo systemctl stop nyancam.service sudo systemctl enable nyancam.service sudo systemctl disable nyancam.service /etc/systemd/system.conf #DefaultStartLimitInterval=10s #DefaultStartLimitBurst=5 systemdは10秒の間に5回まで再起動が行われ、それを超えると再起動をやめてしまう ので、そうならないように起動時にtime.sleep(initwait)する initwait = 3 10秒では3秒*3回=9秒より、3回しか再起動できないため、再起動しつづける動きとなる 再起動 1日4回 0,6,12,18時 systemdで実装 saikidou.service saikidou.timer ← enable 機能 ・ラズパイ起動時に自動起動、撮影開始 ・プログラムが失敗(wifi消失などによる)したときはsystemdにより自動再起動する 再試行中はカメラの青LEDが点灯(接続カメラチェックによる)するためわかる 再試行周期はinitwait+5秒くらい ・短押し : テスト撮影(カメラ設置場所決定のため) ・長押し(5秒以上) : shutdown エラー時(wifi消失) wifi消失によりエラーとなるのはAPIで通信する resp = requests.post(api,headers = headers,data = data) がある以下3箇所 1.### にゃんCAM 起動 ### 2.ボタン短押し(テスト撮影) 3.定期撮影(30分ごととか) エラーパターン1 : 最初からwifiがない 1.でエラーになる エラーパターン2 : 途中でwifiがなくなる 1.はきっと通過している 2.や3.でエラーとなる wifiが復帰したら、最初からやり直しなのでLINEで### にゃんCAM 起動 ###がまた来る """ import cv2,time,requests,os import RPi.GPIO as GPIO import datetime import sys initwait = 3 time.sleep(initwait) ######## Variables ######## rev = 7 # プログラムレビジョン token = 'あなたのトークン' # LINE Notifyトークン init = True # 初回のみフラグ Snapshot_period = 1800 # 撮影周期 sec 30分=60秒*30=1800秒 Longpush_period = 5 # 長押し時間 sec 5秒 shutter_time = 5 # 露光?時間 sec elecomピンクが性能悪いので5秒 resp_time = 1 # カメラ2台 resp間ウエイト(画像が重い) # リトライ関連 # デバッグ時は短く設定 エラー行数が出ないのが惜しい MAXTRY = 0 # トライ回数 20回 Try_period = 1 # トライ周期 sec 1分 Trycnt = 0 # トライカウント ######## 接続カメラチェック ######## true_cam = [] # 配列用意 for camera_num in range(10): # カメラ番号を0~9まで変えて、COM_PORTに認識されているカメラを探す cap = cv2.VideoCapture(camera_num) ret, frame = cap.read() if ret == True: # capture.read()に画像が格納されていたら=画像が取得できたら=カメラが接続されていたら true_cam.append(camera_num) #print('camera number {} Find!'.format(camera_num)) else: # ここでワーニング #print('camera number {} None'.format(camera_num)) pass print('接続されているカメラは {} 台です'.format(len(true_cam))) for i in range(len(true_cam)): # カメラ番号調べ print('カメラ{} : {} 番'.format(i,true_cam[i])) CamNum0 = true_cam[0] # 接続されたカメラの番号 CamNum1 = true_cam[1] print('CamNum0 : {}'.format(CamNum0)) print('CamNum1 : {}'.format(CamNum1)) ################################# # Setup working directory os.chdir('/home/pi/Desktop') # Setup GPIO button = 26 # VDD(3.3V) - button - GPIO26 GPIO.setmode(GPIO.BCM) GPIO.setup(button,GPIO.IN,pull_up_down = GPIO.PUD_DOWN) # pull down button_old = time.time() trig = False val = False long = False flag = False # Setup LINE Notify api = 'https://notify-api.line.me/api/notify' headers = {'Authorization': 'Bearer' + ' ' + token} filename0 = 'cat0.jpeg' images0 = '/home/pi/Desktop/' + filename0 filename1 = 'cat1.jpeg' images1 = '/home/pi/Desktop/' + filename1 # Setup OpenCV windowsize = (1024,768) # 4:3 LINE Notifyの送信可能な最大解像度 ImageNum = 0 old = time.time() # 初回時間 try: while(True): # にゃんCAM main loop if init == True: # 初回のみ message = '\n\n### にゃんCAM 起動 ###\nrev:{}\n\n{}分ごとに撮影しまーす!\n接続カメラ : {} 台'.format(rev,Snapshot_period/60,len(true_cam)) data = {'message': message} resp = requests.post(api,headers = headers,data = data) print(message) print(datetime.datetime.now()) # 初回にまず撮影する shutter_t0 = time.time() cap0 = cv2.VideoCapture(CamNum0) cap1 = cv2.VideoCapture(CamNum1) while True: ret, frame0 = cap0.read() # capture(too slow!!) ret, frame1 = cap1.read() # capture(too slow!!) shutter_t1 = time.time() if(shutter_t1 - shutter_t0) > shutter_time: # shutter time frame0 = cv2.resize(frame0, windowsize) # resize the window frame1 = cv2.resize(frame1, windowsize) # resize the window cv2.imwrite(filename0,frame0) # save cv2.imwrite(filename1,frame1) # save cap0.release() cap1.release() break # break while loop ratelimit_image = resp.headers.get("X-RateLimit-ImageLimit") # max image upload at 1hour ratelimit_image_remaining = resp.headers.get("X-RateLimit-ImageRemaining") # image upload remaining # for LINE Notify # カメラ0 message0 = '\nカメラ0\nうp残 : {} / {}'.format(ratelimit_image_remaining,ratelimit_image) data0 = {'message': message0} files0 = {'imageFile': open(images0,'rb')} requests.post(api,headers = headers,data = data0,files = files0) time.sleep(resp_time) # カメラ1 message1 = '\nカメラ1' data1 = {'message': message1} files1 = {'imageFile': open(images1,'rb')} requests.post(api,headers = headers,data = data1,files = files1) # for debug print(message0) print(message1) init = False # 初回のみより、initのTrueへの復帰は不要 debug0 = time.time() # for measure time of main loop # button detect button_t0 = time.time() # now # snapshot t0 = time.time() # now debug1 = time.time() # for debug # button detect if button_t0 - button_old > 50/1000: # 50msecごと(以上)にボタンの状態を監視 button_state = GPIO.input(button) # check button state if button_state == 1: # button pushed if trig == False: # first pushed firstpush = time.time() # for debug trig = True val = True else: # button released if val == True and long == False: # short pushed( < 5sec ) : Test shot shortpush = time.time() # for debug shutter_t0 = time.time() cap0 = cv2.VideoCapture(CamNum0) cap1 = cv2.VideoCapture(CamNum1) while True: ret, frame0 = cap0.read() # capture(too slow!!) ret, frame1 = cap1.read() # capture(too slow!!) shutter_t1 = time.time() if(shutter_t1 - shutter_t0) > shutter_time: # shutter time frame0 = cv2.resize(frame0, windowsize) # resize the window frame1 = cv2.resize(frame1, windowsize) # resize the window cv2.imwrite(filename0,frame0) # save cv2.imwrite(filename1,frame1) # save cap0.release() cap1.release() break # break while loop ratelimit_image = resp.headers.get("X-RateLimit-ImageLimit") # max image upload at 1hour ratelimit_image_remaining = resp.headers.get("X-RateLimit-ImageRemaining") # image upload remaining # for LINE Notify # カメラ0 # message0 = '\nカメラ0\nテスト撮影\n短押し : {} sec\nうp残 : {} / {}\nエラー時リトライ回数 : {} / {}\nリトライ周期 : {} sec'.format(round((shortpush - firstpush),2),ratelimit_image_remaining,ratelimit_image,Trycnt,MAXTRY,Try_period) message0 = '\nカメラ0\nテスト撮影\n短押し : {} sec\nうp残 : {} / {}'.format(round((shortpush - firstpush),2),ratelimit_image_remaining,ratelimit_image) data0 = {'message': message0} files0 = {'imageFile': open(images0,'rb')} requests.post(api,headers = headers,data = data0,files = files0) time.sleep(resp_time) # カメラ1 message1 = '\nカメラ1\nテスト撮影' data1 = {'message': message1} files1 = {'imageFile': open(images1,'rb')} requests.post(api,headers = headers,data = data1,files = files1) # for debug print(message0) print(message1) print('main loop : {} sec'.format(debug1 - debug0)) val = False trig = False flag = False long = False if val == True: # button pushed longpush = time.time() # for debug if (longpush - firstpush) > Longpush_period: # long pushed ( > 5sec ) : shutdown if flag == False: # once count print('\n長押し : {} sec'.format(round((longpush - firstpush),2))) # for debug message0 = '\n\n### にゃんCAM シャットダウン ###\n\nばーい!' data = {'message': message0} requests.post(api,headers = headers,data = data) print(message0) # for debug time.sleep(3) # シャットダウンするまで少し待ち # os.system('sudo reboot') os.system('sudo shutdown -h now') flag = True long = True trig = False button_old = button_t0 # 定期撮影 if t0 - old > Snapshot_period: shutter_t0 = time.time() # initial time cap0 = cv2.VideoCapture(CamNum0) # VideoCapture cap1 = cv2.VideoCapture(CamNum1) # VideoCapture while True: ret, frame0 = cap0.read() # capture(too slow!!) ret, frame1 = cap1.read() # capture(too slow!!) shutter_t1 = time.time() if(shutter_t1 - shutter_t0) > shutter_time: # shutter time frame0 = cv2.resize(frame0, windowsize) # resize the window frame1 = cv2.resize(frame1, windowsize) # resize the window cv2.imwrite(filename0,frame0) # save cv2.imwrite(filename1,frame1) # save cap0.release() cap1.release() break # break while loop ratelimit_image = resp.headers.get("X-RateLimit-ImageLimit") # max image upload at 1hour ratelimit_image_remaining = resp.headers.get("X-RateLimit-ImageRemaining") # image upload remaining #カメラ0 files0 = {'imageFile': open(images0,'rb')} # message0 = '\nカメラ0\nImage_{}\nうp残 : {} / {}\n撮影周期 : {} 分\nエラー時リトライ回数 : {} / {}\nリトライ周期 : {} sec'.format(ImageNum,ratelimit_image_remaining,ratelimit_image,round((t0-old)/60,2),Trycnt,MAXTRY,Try_period) message0 = '\nカメラ0\nImage_{}\nうp残 : {} / {}\n撮影周期 : {} 分'.format(ImageNum,ratelimit_image_remaining,ratelimit_image,round((t0-old)/60,2)) data0 = {'message': message0} requests.post(api,headers = headers,data = data0,files = files0) time.sleep(resp_time) #カメラ1 files1 = {'imageFile': open(images1,'rb')} message1 = '\nカメラ1\nImage_{}'.format(ImageNum) data1 = {'message': message1} requests.post(api,headers = headers,data = data1,files = files1) # for debug print(message0) print(message1) print(datetime.datetime.now()) print('main loop : {} sec'.format(debug1 - debug0)) # メインループ実行時間 old = t0 ImageNum = ImageNum + 1 Trycnt = 0 # プログラムに復帰したらトライカウントをリセット except KeyboardInterrupt: print('\nプログラム終了!!') sys.exit() 実働で調整したこと(関係ないことも含む。つまり何でもあり) 実際の現場で動かさないと見えてこないことが沢山ありました ...

2022/06/28 · Last updated on 2023/09/23 · 7 min · 1371 words

【ラズパイで猫ちゃんカメラ】 その7 ラズパイ起動時にプログラムを自動起動する

前回 だいぶ日が空いてしまいましたが・・・ 前回はLINE NotifyのAPI制限を確認しました 今回 systemd で自動起動 ラズパイを起動してからプログラムを手動で実行するのは実用的じゃないですね ですので、ラズパイ起動時にプログラムを自動起動します 自動起動には systemd を使います こちらの記事を参照ください↓ systemd の良いところ ちょっと本旨とずれますが、プログラムがエラーで停止しても、systemdがサービスを再起動してくれます ラズパイ自体の再起動はcronでも良いですが、自作のプログラムはバグ取りが不十分なのでエラー停止でサービスを再起動してくれるのは助かります(エラー対策の話につながります) エラー対策 今回はLINEに送信するため、ネットワークを利用します 実際動かしてみて、wi-fi関連処理がうまく行かずエラー停止が結構ありました 自分ではどうしようもない問題でした また、USBが死ぬこともよくあり、これは再起動で復活しますので、定期再起動もサービスにしました 手動でプログラムを再実行できれば対策不要ですが、猫カメラは不在時に使用します なので、手動で再実行はできず、エラー対策は必須です 私も猫カメラ開発でエラー対策が必要だと痛感しました (カメラ止まった!猫の様子がわからない!どうしよう!は飼い主には耐えられないです) 実装の候補は以下2つ Python中にリトライ処理をいれる systemdが勝手に再起動してくれる ← こっちにした リトライ処理も実装してみましたが、よりお手軽な systemd にしました .service の作成 猫カメラなので、名前はnyancam.serviceにしました(何でもOKです) ここでは/lib以下に作っています(上の記事でもあるように、/etc以下に作る方法もあります) sudo nano /lib/systemd/system/nyancam.service nyancam.serviceの中身↓ [Unit] Description = test [Service] ExecStart=/usr/bin/python3 /home/pi/Desktop/nyancam.py Restart=always Type=simple [Install] WantedBy=multi-user.target ExecStart = XXXはプログラムの起動についての記述です /home/pi/Desktop/nyancam.pyをpython3で起動します Restart=alwaysでnayncam.pyがエラー時にサービスを再起動してくれます systemd コマンド sudo systemctl start nyancam.service <--- サービスを起動するコマンド sudo systemctl stop nyancam.service <--- サービスを停止するコマンド sudo systemctl enable nyancam.service <--- サービスを自動起動するコマンド sudo systemctl disable nyancam.service <--- サービスを自動停止するコマンド start は起動(ラズパイを再起動したら停止してしまう) ...

2022/06/27 · Last updated on 2023/09/23 · 1 min · 164 words

ラズパイ(ubuntu 20.04.4 LTS)に Tailscale を構築

ラズパイにtailscaleを構築した。 tailscaleはWireGuard(VPN)を扱いやすくしたものである(ざっくり) 素のWireGuardも検討したが、マンション住まいのため、上位のルーターの構成が不明で、かつポートを開けたり(知識不足)を下手にやってセキュリティガバガバになるのは嫌だったのでtailscaleにした 環境 ラズパイ 3B+ ubuntu 20.04.4 LTS 32GBのSDカード Tailscale のアカウント作成 何はともあれ、まずはアカウントを作る 参考: https://blog.tsukumijima.net/article/tailscale-vpn/#toc3 githubアカウントでログインした その端末の認証鍵の有効期限を無効にすることができます。 → 無効化した ラズパイに構築する前に、SynologyのNAS(DS118)がTailscaleに対応していたので、先に入れてみた すぐにアクセスできた 簡単すぎてビビった ラズパイのセットアップ まず、OSをSDカードに書き込み 書き込みには Raspberry Pi Imager を使用 https://www.raspberrypi.com/software/ 空だけど、何となく erase してから書き込む こちらを参考(↓)にしてwolサーバとしたかったのでubuntuにした https://macoshita.me/posts/wi-fi-router-and-tailscale/ ubuntu はラズパイ 3B+ なので、32bit armhf lts安定版 とした 初回セットアップはこちら(↓)を参考 https://takaken.tokyo/dev/raspberrypi/raspi_ubuntu_setup2004/ 習って、rootパスワード変更まで実施 有線だけで使うため、無線LANは無し(コメントアウトした) Tailscale のインストール 参考: https://tailscale.com/download/linux/ubuntu-2004 tailscale ip -4 か ip addr show tailscale0 で、ipがわかる でもipアドレスはtailscaleのページ(https://login.tailscale.com/admin/machines)でわかるから、コマンドを打たなくてもいいか ラズパイを再起動して、tailscaleが自動起動することを確認した 確認は、上記のtailscaleのページでactive(緑色の丸)になっていることからわかる exitnode の設定 ずっと電源入れっぱなしにしても大丈夫なラズパイをexitnodeに設定する VPNを野良Wi-Fiをセキュアにするために使いたいなら exitnode の設定が必要 exitnode経由にしてなかった時は、接続先がいなくてもVPN接続ができていた おやっ、VPNって2拠点間を繋ぐものなのになんで接続ができて(VPNがactiveになる)るんだ?って思ってグローバルipを確認したら、VPN接続前後でグローバルipが変わってなかった global ipの確認: https://www.cman.jp/network/support/go_access.cgi つまり、インターネットには保護なしで出ていた ...

2022/06/26 · Last updated on 2023/09/22 · 1 min · 123 words

ラズパイを WOLサーバ にする

こちらの記事で紹介したようにtailscaleを使ってラズパイをVPNサーバとして使っていますが、VPNできたら当然自宅NASにもアクセスしたくなりますよね でも、以前書いたようにNAS(DS118)は常時電源を入れていないので、使うときには起こす必要があります ですので、必要な時にNASを起こせるようにラズパイをwolサーバに仕立てます NAS(DS118)のwolを有効にする 使っているNAS↓ まず、NASがwol信号を受け付けられるように、wolを有効にします https://support.ask-corp.jp/hc/ja/articles/360051678474-Wake-on-LAN-を設定したい ラズパイに WOLサーバ を建てる 参考(大感謝!) 構成は Flask + uWSGI + Nginx となっています ラズパイのCPU温度とクロック周波数を追加していますが、すべてそのまま使わせていただきました 便利に使えており、大変感謝しております! 構築に関して、自分のつまずいた点などを書いています 出来上がり見た目↓ pip install -r requirements.txt のインストール方法 https://note.nkmk.me/python-pip-install-requirements/ 文字化けしたがインストールできたっぽい たぶんteratermの設定のせいで文字化けした 起動など ファイルは大体をPCで作って、FTPソフト(filezillaを使用)でラズパイに転送し、手直しはSSH接続して直接ファイルを弄った ubuntu@ubuntu:~$ pwd /home/ubuntu /home/ubuntu の下にファイルを作って配置する サービス起動 sudo systemctl start wakeonlan-server サービス自動起動(起動時) sudo systemctl enable wakeonlan-server 実行したのでbootで自動実行される サービスのステータス確認 sudo systemctl status wakeonlan-server active(running)になっているのでおけ プログラム(wol.py)のエラー箇所もわかる ValueError(“Incorrect MAC address format”) が出ていることがわかった たぶんwol.htmlから変数(MACアドレス)をwol.pyに渡す時におかしくなってるのだが、検証できなかった なので、MACアドレスを直書きすることにした(当面は同じNASしか使わないし) MACアドレスは:で区切る # wol.py 一部抜粋 def wol(): # addr = request.args.get('addr', '') addr = "XX:XX:XX:XX:XX:XX" # NASのMACアドレス直書き message = "" if request.method == 'POST': send_magic_packet(addr) message = 'Sent magic packet' return render_template('wol.html', message=message, addr=addr) WOLの方法 ubuntuのアドレス/wol ...

2022/06/26 · Last updated on 2023/09/22 · 1 min · 113 words

HUGO robust に Google AdSense の広告を設定

自動広告 Google AdSense - 「広告」 - 「サマリー」 の 「サイトごと」タブ ドメイン名(atooshi-note.com)右横の鉛筆マーク(編集)をクリック 広告設定のプレビューが出るので、好みになるように調整 私は広告のフォーマットは「ページ内広告」のみにした 「コードを取得」を押してコードをコピー これ themes\hugo_theme_robust-master\layouts\partials\meta.html 場所変更↓(2022/09/08) layouts\partials\meta.html にそのまま貼り付ける 固定広告 固定広告も設定 Google AdSense - 「広告」 - 「サマリー」 の 「広告ユニットごと」タブ、「ディスプレイ広告」を選択 プレビューをみて広告タイプを決めたら作成してコードをコピー 広告を表示する場所は、記事の本文前後(参考サイトさんそのまま)とする コピーしたコードを以下に貼り付ける themes\hugo_theme_robust-master\layouts\_default\summary.html 場所変更↓(2022/09/08) layouts\_default\summary.html 参考 ありがとうございます! https://www.ame-kuma.com/post/site02/ ads.txt 以下のような注意がでましたので対策しました 要注意 - 収益に重大な影響が出ないよう、ads.txt ファイルの問題を修正してください。 ads.txt をダウンロードし、 static 以下に配置します 参考 ありがとうございます!

2022/06/25 · Last updated on 2023/09/22 · 1 min · 49 words

[Python]AmazonアソシエイトをHUGOブログに貼るのを少し簡単に

HUGOの記事にamazonアソシエイトリンクを貼る際に、取得したリンクを加工するのが面倒だったので、pythonで書きました tkinterでGUIにしています なんてことはない、中身は文字列を切って貼ってをしているだけです コマンドで起動するのは面倒なので、バッチファイルで起動するようにしています 作ろうとしているリンク こんな見た目 古風だけど自分はこれが好き 実際のリンクはこれ↓(リンク切れていたらスミマセン) 使い方 「Amazonアソシエイトツールバー」の「テキストと画像」タブを開いて、生成されたリンクをコピー(下画像の「これをコピー」部分) pythonプログラムを起動するとこれが現れる↓ コピーしたリンクをそのまま貼り付ける エンターキーを押すと、クリップボードにコピーされる 記事内に貼り付ける コピーされた文字列例↓(表示の都合上、先頭の{は全角になっていますが、実際は半角{です) {{< Amazon src="//rcm-fe.amazon-adsystem.com/e/cm?lt1=_blank&bc1=000000&IS2=1&bg1=FFFFFF&fc1=000000&lc1=0000FF&t=アソシエイトID&language=ja_JP&o=9&p=8&l=as4&m=amazon&f=ifr&ref=as_ss_li_til&asins=B088PGN64G&linkId=2ef7d1928670c9537cf1976fdc5a5b37" >}} クリアボタンを押すと消える 2回目以降は「クリップボードにコピーしました!」は消えないので注意 プログラム クリップボードを扱うライブラリー(pyclip)はインストールが必要です 表示の都合上、一部の{は全角になっていますが、実際は半角{です。使用の際は半角{に直してお使いください # -*- coding: utf-8 -*- """ アマゾンの商品リンクをhugoに貼る形に変換する エンターで変換 クリップボードにコピーされる """ import tkinter as tk import pyclip root = tk.Tk() root.geometry('500x90') root.title('Amazon affiリンク変換(hugo用)') def func(event): global label1 text1 = entry1.get() # amazon アソシエイトツールバー テキストと画像のリンク target = 'src="' # src="より後ろを抽出 idx = text1.find(target) r = text1[idx + len(target):] r = r[:-10] # 後ろの ></iframe> を削除 r = 'src="' + str(r) # 整形 r = '{{< Amazon ' + r + ' >}}' # 整形 pyclip.copy(r) # copy data to the clipboard label1 = tk.Label(text='クリップボードにコピーしました!') label1.place(x=10,y=60) def buttonclk(): global label1 entry1.delete(0,tk.END) label1.destroy() entry1 = tk.Entry(width = 80) entry1.place(x = 10 , y = 40) entry1.bind('<Return>', func) button = tk.Button(root,text='クリア',command=buttonclk) button.place(x=10,y=10) root.mainloop() 参考 エンターキー入力を拾う方法 ...

2022/06/23 · Last updated on 2023/09/23 · 1 min · 135 words

HUGO ROBUST 設定

HUGOの設定を掲載します。 自分の備忘録要素が強いのでHUGO以外の周辺設定も含みます。 2023/08/03 変更 githubに公開しました 2023/06/25 変更 googleAnalyticsのGA4対応 やっと対応 config.tomlを弄るだけで済みました disqus無効化 config.toml全容 githubでご確認ください wordpressからの移行 もともと、HUGO導入はlifehack.jpのこちら↓の記事がきっかけです https://lifehacking.jp/2020/05/goodbye-wordpress-hello-hugo/ wordpress to hugo exporter を使ってざっくり変換し、微調整はpythonでスクリプトを書いて対応しました テーマ robust テーマはrobustを利用させていただいてます git clone ではなく、ダウンロードしています。 最初はgit clone でテーマを引っ張ってきましたが、コミットできず、エラーがでたのでやめました。 そもそもgitを使い慣れていませんので、zipをダウンロードして置き直しました。 これでコミットできたのでそのままとしています。 robustのサムネイル画像 static/images に配置 記事のサムネイルは、フロントマターの thumbnail で指定できます。 指定しないと、デフォルトのサムネイルが設定されます。画像ファイルは、 static/images/default.jpg に配置してください。 デフォルトのサムネイルがない場合、テーマで用意されているデフォルトサムネイルが設定されます。 テーマ選別 robust以前に試したいくつかを紹介します ananke hugoのquick startはanankeが使われています 見た目が好きじゃない サムネイル画像の指定が、frontmatterでfeatured_imageでいけ、記事と同じフォルダに置くことができる Tranquilpeak https://zenn.dev/harachan/articles/a043e9a756cae4 デザインは一番好き 解説ページが多い サムネイル表示できない featured imageだとだめ HUGO バージョン windows10,11で使用 ポータブル版 binフォルダはpathを通す # hugo version hugo v0.92.0-B3549403+extended windows/amd64 BuildDate=2022-01-12T08:23:18Z VendorInfo=gohugoio ローカルで確認 # draft記事も表示 以下のいずれか hugo server -D hugo -D server # draft記事は非表示(公開した状態と同じ) hugo server コマンドを実行したあと以下にアクセス ...

2022/04/13 · Last updated on 2025/02/06 · 4 min · 753 words

Synology DS118 購入

MR2200acの簡易NAS機能を使い、NASを使用していたが、Synologyの1ベイNAS DS118を購入した。 買ったもの、用意したもの NAS本体 DS118 2万円 購入 HDD 海門 CMR 2TB 5900rmp 7,500円 購入 USB3.0外付けHDDのケース 3,000円 手持ち 外付けHDDケースの中のHDD 2TB 手持ち BuffaloNASのHDDを流用 用途・要求 パソコン外の大容量データ保管庫 Windows,Mac,Linuxがある どのデバイスからも接続可能とする iPhoneの写真をバックアップ 劣化なしで google photo有料化の代用 Synology photoがgoogleフォトっぽくて良い https://www.synology.com/ja-jp/DSM70/SynologyPhotos 消費電力をできるだけ減らす 寿命を伸ばす NASの運用方法 1ベイNAS + USB外付けHDDにバックアップ DSMのHyper Backupでバックアップ 1時間ごと https://www.Synology.com/ja-jp/dsm/feature/hyper_backup 不在時は電源OFF 帰宅時に電源ON 消費電力を減らす、寿命を伸ばすため 電源ボタン長押しで切れるのでそんなに面倒ではない デスクのすぐ横に置いてあるので、アクセスは簡単 遠くに置いてある人なら面倒かも バックアップが1時間ごとなので、それが動作していないことを見計らって実施 この構成のデメリット バックアップ(1時間毎)までの間にNASのHDDがお亡くなりになった場合にデータ欠損が生じる しょうがない 諦める デメリットの解消方法 → 不採用 2ベイでRAID1 + 外付けHDDにバックアップ HDDのロットを変えれば(購入時期をずらせば)、RAIDのHDDの同時故障は回避可能 消費電力が増えるし、場所も取るし、そこまでの構成は不要と判断 不採用 検討(時系列) 2ベイでRAID1(ミラーリング) 1台がお亡くなりになってももう一台が生き残ってデータ欠損なし つまりバックアップをイメージしていた(この時点で) DS220+ (2ベイ)を購入 SynologyのhybridRAID(RAID1相当)を想定 Synologyを選んだのは情報が多いことと、Synology製のMR2200ac をすでに使っているから RAIDはバックアップにならないことを知る RAIDは冗長化技術 システムを止めない技術 バックアップではない RAIDはやめた DS220+ でBasic設定(RAIDではなく)とし、別々のドライブとして使うことを検討 せっかく DS220+ を買ったので もくろみ 一つはメイン、もう一台はバックアップ用 ハイバーネーション機能で不使用時はスピンダウンさせHDDをスリープ 消費電力、寿命的、音の低減のため バックアップ用HDDはバックアップ時だけ起きてほしく、普段はスリープ(スピンダウン)してほしかった しかし DS220+ は2台HDDを個別にスピンアップ、ダウンができない 同時スピンアップ、ダウンのみ USB接続HDDなら別でスピンアップ、ダウンできる 参考 : https://tomo256.blog.fc2.com/blog-entry-136.html 結構相性があるらしい GW3.5AA-SUP3 が使える 1ベイ+ 外付けHDDにバックアップの構成で検討 DS220+ + 外付けHDDにバックアップ でも可能だが、消費電力が更に増加 と、上記の理由で却下 DS220+ は要らない 返品した 1ベイのDS118を購入 外付けHDDは必要なときだけ起動可能 1ベイはそもそもラインナップが少ない 需要少ない? 2機種あり CPUが良さそうなDS118とした ハイバネーションしない 使用するアプリ(Synology photos)の影響(多分)でハイバーネーションしないので、不要な時は電源オフ ...

2022/01/06 · Last updated on 2023/09/25 · 1 min · 154 words

【Python】pandas DataReaderでFREDから現在の為替を取得

Pandas DataReaderを用いてFREDから為替データを取得します。 例として豪ドル円を取得します。 FREDからは米ドル円しか取得できない FREDからは円との通貨ペアは米ドル円しか取得できません。 FREDはこちら↓ では豪ドル円が欲しい場合はどうしましょう? 答えは簡単で、米ドル円と豪ドル米ドルの2つを取得して計算するだけです。 米ドルとの通貨ペアはFREDで取得できるので、同様にどの通貨情報も得られます。 米ドル円 : DEXJPUS (円/米ドル) 豪ドル米ドル : DEXUSAL (米ドル/豪ドル) 豪ドル円 : DEXJPUS (円/米ドル) × DEXUSAL (米ドル/豪ドル) = (円/豪ドル) ・・・1豪ドル○○円 プログラム 為替取得は本日までとしています。 しかしFREDの最新データが本日分まで出ていないとエラーになります。 ですので、本日から15日前までの値を取得して、その中で最新値を見ています。 """ FREDから現在価格を取得 """ import pandas_datareader.data as pdr import datetime as dt # 計算したいペア 任意の名前 showpare = 'JPY_AUD' dateend = dt.date.today() # 取得範囲のエンドは本日まで datestart = dateend - dt.timedelta(days = 15) tickerlist = ['DEXJPUS','DEXUSAL'] # 取得するtickerのlist # DEXJPUS : JPY/USD # DEXUSAL : USD/AUD # DEXUSNZ : USD/NZD # DEXCAUS : CAD/USD # Fredからレートを取得 FREDdf = pdr.DataReader(tickerlist,'fred',datestart,dateend) # 日次データ FREDdf['JPY_AUD'] = FREDdf['DEXJPUS'] * FREDdf['DEXUSAL'] # JPY/USD * USD/AUD = JPY/AUD print(FREDdf) # 価格表示 # 最終行のデータを取得 # つまり、最新に最も近い JPYUSD = FREDdf.iloc[-1]['DEXJPUS'] # JPYUSDの最終行の値 AUDUSD = FREDdf.iloc[-1]['DEXUSAL'] # AUDUSDの最終行の値 SHOW = FREDdf.iloc[-1][showpare] # 知りたい通貨ペアの最終行の値 print('本日 {}'.format(dateend)) print('現在 JPY_USD {} 円/米ドル'.format(JPYUSD)) print('現在 USD_AUD {} 米ドル/豪ドル'.format(AUDUSD)) print('現在 {} : {} 円/豪ドル'.format(showpare,round(SHOW,2))) ...

2021/10/24 · Last updated on 2021/10/24 · 1 min · 137 words

【ラズパイで猫ちゃんカメラ】その6 LINE NotifyのAPI制限を確認する

前回 前回はpythonで定期的な動作を実装しました。 これで例えば10分毎に撮影し、画像をLINEすることができます。 今回 LINE NotifyのAPI制限を確認します。 画像のアップロード枚数制限やAPIのコール回数制限があります。 テストで画像を送りまくっていると制限にかかり、送れなくなります。 突然送れなくなるので、注意が必要です。 APIコール回数は1000回ですのでそうそう消費しないと思います。 ですが、画像のアップロード枚数制限は50枚(1時間あたり)です。 案外すぐ達してしまうのでモニターすることをオススメします。 ドキュメントのAPI Rate Limitに記載があります。 各サービスごとに1時間にAPIをcallできる回数の上限が設定されています。 デフォルトは1000回に設定されています。 上限はaccess tokenごとに設定されています。 API Rate Limitのstatusは、APIの以下のresponse headerで確認することができます。 API Rate Limitの確認方法 こちらを参考にさせていただきました。ありがとうございます(^^) # line-notify-limit.py import requests token = 'あなたの取得したトークン' api = 'https://notify-api.line.me/api/notify' message = '\n\nLIMIT CHECK' headers = {'Authorization': 'Bearer'+' '+token} data = {'message': message} resp = requests.post(api,headers=headers,data=data) print(message) # APIコール残 ratelimit = resp.headers.get("X-RateLimit-Limit") # max API call ratelimit_remaining = resp.headers.get("X-RateLimit-Remaining") # API call remaining print('API call remaining : {} / {}'.format(ratelimit_remaining,ratelimit)) # 画像うp残 ratelimit_image = resp.headers.get("X-RateLimit-ImageLimit") # max image upload at 1hour ratelimit_image_remaining = resp.headers.get("X-RateLimit-ImageRemaining") # image upload remaining print('Image upload remaining : {} / {} by an hour'.format(ratelimit_image_remaining,ratelimit_image)) # リセット時間 ratelimit_reset = resp.headers.get("X-RateLimit-Reset") # reset time UTC print('Reset time UTC : {}'.format(ratelimit_reset)) # ステータスコード print('HTTP status code : {}'.format(resp.status_code)) # HTTP status code ...

2021/10/19 · Last updated on 2023/09/23 · 1 min · 131 words