10.8. WebSocketによる双方向制御¶
Server-Sent Eventsはプッシュ専用です。所有者がダッシュボードで「今すぐスナップショットを保存」や「トリガーカウンターをリセット」をタップすると、ダッシュボードはカメラへメッセージを送信しなければなりません。それがWebSocketです。1つのTCPソケットで、フレーム化されたメッセージが双方向に流れます。
10.8.1. /control ルート¶
microdot.websocket.with_websocket() はWebSocketのアップグレードハンドシェイクを実行し、ハンドラに WebSocket オブジェクトを渡します。ハンドラは無限にループし、コマンドを読み取って確認応答を送信します。
from microdot.websocket import with_websocket
from microdot.websocket import WebSocketError
import json
@app.get('/control')
@with_websocket
async def control(request, ws):
while True:
try:
msg = await ws.receive()
except WebSocketError:
break
try:
cmd = json.loads(msg)
except ValueError:
await ws.send({'error': 'bad json'})
continue
if cmd.get('cmd') == 'snapshot_now':
if latest_jpeg:
path = '/sdcard/snaps/manual-{}.jpg'.format(
int(time.time()))
with open(path, 'wb') as f:
f.write(latest_jpeg)
await ws.send({'ok': True, 'saved': path})
else:
await ws.send({'ok': False, 'reason': 'no frame yet'})
elif cmd.get('cmd') == 'reset':
state['trigger_count'] = 0
await ws.send({'ok': True, 'counters': 'reset'})
else:
await ws.send({'error': 'unknown command'})
receive() はテキストフレームに対しては文字列を、バイナリフレームに対してはバイト列を返します。ブラウザ側の WebSocket.send(...) はデフォルトでテキストを送信するため、JSONエンコードされたコマンドが自然な選択です。
send() は文字列、バイト列、またはJSONシリアライズ可能な任意のものを受け付けます。辞書はJSONテキストフレームとして送信されます。
WebSocketError は、クライアントが切断したとき(正常なクローズ、ネットワーク切断、またはプロトコルエラー)に送出されます。ハンドラはループを抜けて戻り、microdotがソケットを後始末します。
10.8.2. ダッシュボードからコマンドを送信する¶
スライダーの隣に2つのボタンを index.html に追加します。
<button id="snap-btn">Save snapshot</button>
<button id="reset-btn">Reset counter</button>
そして app.js はWebSocketを一度開き、両方のボタンを send に接続します。
const proto = location.protocol === 'https:' ? 'wss://' : 'ws://';
const ws = new WebSocket(proto + location.host + '/control');
document.getElementById('snap-btn').addEventListener('click', () =>
ws.send(JSON.stringify({cmd: 'snapshot_now'})));
document.getElementById('reset-btn').addEventListener('click', () =>
ws.send(JSON.stringify({cmd: 'reset'})));
ws.addEventListener('message', (e) => {
console.log('cam:', JSON.parse(e.data));
});
ws:// と wss:// の選択は http:// と https:// の選択を反映しています。WebSocketは同じTLS処理を継承します。HTTPSが導入されていれば、ダッシュボードは自動的に wss:// 経由で接続します。
10.8.3. SSEとWebSocketのどちらを選ぶか¶
カメラがプッシュし、ブラウザは受信するだけの場合はSSEを使います。通知、テレメトリ、ステータス変更などです。通信は素のHTTPで、クライアント側は1行(new EventSource)で済み、再接続は自動です。
ブラウザもプッシュする必要がある場合はWebSocketを使います。ボタン、キー入力レートで更新を送るスライダー、次のリクエストを待っていては遅すぎるものなどです。接続は双方向でフレーム化されますが、APIは両側でより複雑になります。
カメラは今やインタラクティブなものになりました。監視し、イベントを外へプッシュし、コマンドを受け付けます。