10.8. Obousměrné řízení pomocí WebSockets¶
Server-Sent Events jsou jen pro odesílání směrem ven. Když majitel na dashboardu klepne na „uložit snímek právě teď“ nebo „vynulovat počítadlo spuštění“, musí dashboard poslat zprávu do kamery. To jsou WebSockets – jeden TCP socket, rámcované zprávy proudící oběma směry.
10.8.1. Cesta /control¶
microdot.websocket.with_websocket() provede handshake pro upgrade na WebSocket a předá handleru objekt WebSocket. Handler se točí ve smyčce donekonečna, čte příkazy a odesílá potvrzení:
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() vrací řetězec pro textové rámce a bajty pro binární rámce. WebSocket.send(...) na straně prohlížeče odesílá ve výchozím nastavení text, takže příkazy zakódované v JSON jsou přirozenou volbou.
send() přijímá řetězce, bajty nebo cokoli serializovatelného do JSON – slovník se odešle jako textový rámec JSON.
WebSocketError se vyvolá, když se klient odpojí (čisté uzavření, výpadek sítě nebo chyba protokolu). Handler opustí smyčku a vrátí se; microdot socket uklidí.
10.8.2. Dashboard odesílá příkazy¶
Do index.html vedle posuvníku přibudou dvě tlačítka:
<button id="snap-btn">Save snapshot</button>
<button id="reset-btn">Reset counter</button>
a app.js jednou otevře WebSocket a obě tlačítka napojí na 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));
});
Volba mezi ws:// a wss:// odpovídá volbě mezi http:// a https:// – WebSocket dědí stejné zpracování TLS. Když je HTTPS na místě, dashboard se automaticky připojí přes wss://.
10.8.3. Kdy zvolit SSE a kdy WebSockets¶
SSE použijte, když kamera odesílá a prohlížeč jen naslouchá – oznámení, telemetrie, změny stavu. Po drátě jde čisté HTTP, klientská strana je jeden řádek (new EventSource) a opětovné připojení je automatické.
WebSockets použijte, když musí odesílat i prohlížeč – tlačítka, posuvníky, které posílají aktualizace rychlostí stisků kláves, cokoli, kde by čekání na další požadavek bylo příliš pomalé. Spojení je obousměrné a rámcované, ale API je na obou stranách složitější.
Kamera je nyní interaktivní věc – sledujte ji, odesílejte události ven a přijímejte příkazy dovnitř.