GAミント至上主義

Web Monomaniacal Developer.

Raspberry PiでPython3を使ってFirestoreにクエリする

Raspberry PiでFirebaseのFirestoreを介して外部と通信するために、最初はNode.jsのライブラリでやろうとがんばってたけど、1日やっても下記エラーが出てダメそうなので、LED部分と同じくPythonでやる。

Node.jsでのエラー

Node.jsはaptで入れて、nでv10を入れたもの、公式からLinux Binaries (ARM) ARMv6を入れて動かしたけど同じだった。認証部分でコケてしまうがまったく同一のファイルでPixelbookのDebian上で動いた。

(node:873) UnhandledPromiseRejectionWarning: FetchError: request to https://www.googleapis.com/oauth2/v4/token failed, reason: connect ENETUNREACH 172.217.161.42:443 - Local (0.0.0.0:0)
    at ClientRequest.<anonymous> (/home/pi/led8/node_modules/node-fetch/lib/index.js:1455:11)
    at ClientRequest.emit (events.js:198:13)
    at TLSSocket.socketErrorListener (_http_client.js:392:9)
    at TLSSocket.emit (events.js:198:13)
    at emitErrorNT (internal/streams/destroy.js:91:8)
    at emitErrorAndCloseNT (internal/streams/destroy.js:59:3)
    at process._tickCallback (internal/process/next_tick.js:63:19)
(node:873) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:873) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Pythonの準備

pipのインストールは省略。
公式通り入れて
firebase.google.com

サンプルコードを動かす。キーファイルはjsの時と同じものを利用した。

import firebase_admin
from firebase_admin import credentials

cred = credentials.Certificate("path/to/serviceAccountKey.json")
firebase_admin.initialize_app(cred)

PythonでFirestoreにクエリする

今回は特定のコレクションの最新のドキュメントを1件取得し続けたいので下記のようなコードとなった。

import firebase_admin
from time import sleep 
from firebase_admin import credentials 
from firebase_admin import firestore 
 
cred = credentials.Certificate("accountKey.json")
firebase_admin.initialize_app(cred)
 
client = firestore.client()

# 監視したいコレクション 今回はドキュメントがtimestampを持ってるので並び替えしとく
collection_ref = client.collection('path', 'to', 'parent_document')\
    .order_by('timestamp', direction='DESCENDING')\
    .limit(1)

# 更新があったときのコールバック用関数
def update_callback(docs, changes, read_time): 
    print('changes', changes)
    print('read_time', read_time)
    for doc in docs: 
        print(doc.id, doc.to_dict()) 

# 監視の開始、サブプロセスで動く
watcher = collection_ref.on_snapshot(update_callback)

print(watcher) 

# メインプロセスはとりあえずWhileしとく。sleep()をかませないとCPU利用率が高くなるので適度につけとく。
while True:
    sleep(1) 

実行して、Firebaseのコンソールでドキュメントを追加したところ、ほぼリアルタイムで反応したのを確認できた

pi@raspberrypi:~/led8 $ python3 firestore.py 
<google.cloud.firestore_v1.watch.Watch object at 0xb5fe5610>
changes [<google.cloud.firestore_v1.watch.DocumentChange object at 0xb5674350>]
read_time 2019-07-01 02:40:33+00:00
aXPGdePvZFzc5nA6S4RE {'timestamp': DatetimeWithNanoseconds(2019, 7, 2, 15, 0, tzinfo=<UTC>)}
changes [<google.cloud.firestore_v1.watch.DocumentChange object at 0xb56742f0>, <google.cloud.firestore_v1.watch.DocumentChange object at 0xb5674350>]
read_time 2019-07-01 02:41:05+00:00
yWwK3RmSt49EhZazqICW {'timestamp': DatetimeWithNanoseconds(2019, 7, 3, 15, 0, tzinfo=<UTC>)}