👻

GASとラズパイで定期監視システムを作ってみた話

2024/12/11に公開

個人でちょっとした開発をして日々遊んでおります。
そのうちの1つとして、GAS,ラズパイ,Linebotを組み合わせた簡単な開発物を作成したのでざーっくり紹介します。
簡単な自己開発経験として。拡張すれば色々な用途に使えるかもしれません。
私もここをベースにして、追加開発を行う予定です。
また、自己解釈等入っている部分もあるかもしれないので、もし修正すべき点等見つけた場合遠慮なく教えてください。感謝します。質問も歓迎します。

自己紹介

  • name -> Tackky
  • work -> 業務系ソフトウェアの開発(?)部門4年目のまだ新しい米
  • suki -> car, alchol, book, technology, game, etc

作ったもの

3Dプリンターで作成した鉢に種を植えて、水耕栽培で植物を育てています。毎日は観察できないので、経過を記録したいと考えました。

  1. ラズパイ:毎日一定時刻に写真を撮影し、
  2. ラズパイ:クラウドへ送信。
  3. GAS:Linebotとして写真を毎日送信する

というものです。

必要なもの

No. 品目
1. ラズパイ(ネット接続可能なもの)一式
2. ラズパイに接続するカメラ(ここではPiCamerav2を使用)

仕組み

開発作業手順

上記画像の1→2→3の順番で実行されるが、ここでは逆順で説明していく。
(紐付け先のアクセスURLがわからなくなりがちなので。)

1. Linebot側の設定

こちらはアカウント作って紐付け先を取ってきます。
https://developers.line.biz/console/

providerの作成

サービスを作成する事業主=providerを作成します。ここでは、個人開発なので自分専用のProviderを作成します。

channelの作成

メッセージを送信するアカウントを作成します。用途ごとに色々作ってみると楽しいと思います。providerが無事作成されると、このようなページに飛ぶので、Line Messaging APIを選択します。

channelの初期設定画面に入るので、ここでLinebotのアイコン・説明等を追加します。
Company regionは日本でいいと思います。
channel icon, channel name, channel descriptionは好きに設定してください。カテゴリーとEmail addressは必須入力なので近しいものを選択します。

channel access tokenの取得

この後のGAS側の設定で使うのでこれをコピーします。これはLine Messaging APIを使ってGASからメッセージを送るためのエンドポイントになっています。

2. GAS側の設定

GASに入る前に

ラズパイで撮影した画像を保存するフォルダーを作成します。
今回はGoogleDriveを使用しました。(相性もよさそうだし…)GASからドライブにアクセスできるものであればなんでも問題ないと思います。
ここで、共有リンクを取得しておきます。この際、リンクを知っている人は誰でも閲覧可能にしておきます。

GAS

以下のリンクにアクセスして、GASの画面を開きます。(ログインが必要かも?)
https://script.google.com/

ここで、画面左上のNew Projectをクリックして、新しいプロジェクトを作成します。

GASでは、プログラムが走ったら、本日日付で撮影された画像を取得し、LinemessagingAPIを経由してLinebotに送信してくれるプログラムを書いていきます。

Script Properties

まず、シークレットキー等の他人に公開できない部分をプロジェクト内のProject settingsからScript Propertiesに設定します。

Script PropertiesはProject settingsの下部にあります。

Property項目 Value項目に入れる内容
CSVFILE_ID 温湿度センサ情報を共有する場合のファイルパス
IMGFOLDER_ID GoogleDriveから取得した共有リンク
LINE_ACCESS_TOKEN Linebotを作成した際に取得したchannel access token
LINE_USER_ID 自分だけ等の特定のユーザーのみに送信したい場合に使用

mainprocess

ここでは、25行目で送信する画像のURLを取得し、26行目で送信する文章を作成し、30行目以降で、送信する画像がある場合、Lineにメッセージを送信するという流れです。
https://github.com/tackky/script-for-monitoring-info-to-LINE/blob/main/Code.gs#L23-L35

getIMGURL

https://github.com/tackky/script-for-monitoring-info-to-LINE/blob/main/Code.gs#L36-L47

var folder = DriveApp.getFolderByID(this.IMGFOLDER_ID);

でGoogleDriveのフォルダの場所をIDから取得し、

var files = folder.getFiles();

でフォルダ内にあるファイルを全取得しています。
40~45行目で、本日日付で作成されたファイルのみをreturnしています。
詳細なリファレンスはここにあります。
https://developers.google.com/apps-script/reference/drive/drive-app?hl=ja

getText

https://github.com/tackky/script-for-monitoring-info-to-LINE/blob/main/Code.gs#L49-L52
画像を送信するだけでは少し物寂しいので、日付も送信するために、送信文字列を作成する関数です。

sendMessageToLine

https://github.com/tackky/script-for-monitoring-info-to-LINE/blob/main/Code.gs#L65-L94
最後に、ここからLinemessagingAPIにメッセージを送信しています。
今回は、作ったBotを知り合いなどにも送信したかったため、Linebotに登録してくれた人全員に一斉送信を行うBROADCASTでメッセージを送信します。messagesの中を{}で区切れば、異なるタイプのメッセージでも同時に送信してくれます。
https://developers.line.biz/en/reference/messaging-api/#send-broadcast-message

参考URL

全体のコードは以下から参照してください。
https://github.com/tackky/script-for-monitoring-info-to-LINE/blob/main/Code.gs

3. ラズパイ側の設定

初期設定は省略します。
実行環境は以下です。

raspberry pi Raspberry Pi 4 Model B
OS Linux raspberrypi 6.1.21
camera Camera v2.1
Python Python 3.9.2

カメラモジュールの設定

クラウドアップロードの設定

ラズパイ側からGoogleDriveにアップロードする際に、1回目はOAuth2の設定をする必要があります。

OAuth2による認証

https://support.google.com/cloud/answer/6158849?hl=ja
このページを参考に、ClientIDを作成し、SecretJSONをダウンロードします。
client_secret.jsonという名称で保存し、ラズパイ側にsshを使って送信します。

定期実行プログラムとタスクの設定

  1. 撮影+GoogleDriveへの送信プログラム

    解説がめんどくさいので貼ります

    # -*- coding: utf-8 -*-
    
     from __future__ import print_function
     import httplib2
     import os
     import sys
     import datetime
     import configparser
    
     from picamera import PiCamera 
     from glob import glob
     from apiclient import discovery
     from apiclient.http import MediaFileUpload
     from oauth2gdrive import get_credentials
     
     # OAuth 2.0で設定するApplication ID
     APPLICATION_NAME = 'Upload files'
     # OAuth 2.0で設定するSCOPE:Google Driveにおいてファイル単位のアクセスを許可
     SCOPES = 'https://www.googleapis.com/auth/drive.file'
     
     # アップロードするファイルが格納されているディレクトリ
     IMG_DIR = 'Pictures/'
     # アップロードするファイルの拡張子
     FILE_EXTENSION = '.jpg'
     # ファイルを格納するGoogle Driveのフォルダ
     DRIVE_DIR = 'SAVED_IMAGE_HYDRO'
     # Google DriveのフォルダのmimeType
     FOLDER_MIME_TYPE = 'application/vnd.google-apps.folder'
     # アップロードするファイルのmimeType
     FILE_MIME_TYPE = 'image/jpeg'
    
     CLIENT_SECRET_FILE = 'client_secret.json'
    
     def upload_file(drive_service, drive_folder_id, upload_file_path):
         ''' Google Driveの指定したフォルダにファイルをアップロードする
         指定のファイルをアップロードする
         Returns: void
         '''
     
         # upload_file_pathがらファイル名を抽出する
         file_name = os.path.split(upload_file_path)[-1]
    
     
         # Google Driveの指定したフォルダへファイルをアップロードする
         media = MediaFileUpload(upload_file_path, mimetype=FILE_MIME_TYPE, resumable=True)
         file_metadata = {
             'mimeType': FILE_MIME_TYPE,
             'name': file_name,
             'parents': [drive_folder_id],
         }
         created_file = drive_service.files().create(body=file_metadata,
                                                     media_body=media,
                                                     fields='id, name').execute()
         print('Upload sucssesful:' + created_file.get('name'))
     
     def uploadfiles():
         ''' 指定したディレクトリ配下の全てのファイルをGoogle Driveへアップロードする
     
         指定するディレクトリは、ホームディレクトリ配下のディレクトリとする
         指定した拡張子を持つファイルのみをアップロードする
     
         Returns: void
         '''
     
         # OAuth 2.0の認証プロセスを実行する
         credentials = get_credentials(APPLICATION_NAME, SCOPES)
         http = credentials.authorize(httplib2.Http())
         drive_service = discovery.build('drive', 'v3', http=http)
     
         # アップロードする全てのファイルパスを取得する
         home_dir = os.path.expanduser('~')
         img_dir = os.path.join(home_dir, IMG_DIR)
         #upload_files_path = glob(os.path.join(img_dir, '*.' + FILE_EXTENSION))
         #if not upload_files_path:
             # 該当するファイルが存在しない場合は終了する
         #    print('File does not exist. It does not upload.')
         #    sys.exit()
    
         dt_now = datetime.datetime.now()
         now = dt_now.strftime('%Y%m%d%H%M%S')
         upload_file_path = img_dir+now+FILE_EXTENSION
         camera=PiCamera()
         camera.resolution = (1920,1080)
         camera.awb_mode = 'incandescent'
         camera.brightness = 55
         camera.contrast = 20
         #camera.framerate=Fraction(1,) #フレームレート設定
    
         camera.capture(upload_file_path)
    
         config_ini = configparser.ConfigParser()
         config_ini.read('config.ini', encoding='utf-8')
         DRIVE_ID = config_ini['default']['Drive_id']
    
         print('Upload file:' + upload_file_path)
         upload_file(drive_service, DRIVE_ID, upload_file_path)
     
     if __name__ == '__main__':
         uploadfiles()
    
    
  2. 定期実行の設定
    cron を使用して設定します。
    上記で設定したプログラムをこれを参考に設定する
    https://www.express.nec.co.jp/linux/distributions/knowledge/system/crond.html

できたもの


これのお陰でレタスが枯れかけていることに気付けました。よかったね。

さいごに

APIリファレンスや言語リファレンス等は公式のものを読め。それが一番だ。

GitHubで編集を提案

Discussion