10.8. Kétirányú vezérlés WebSocketekkel¶
A Server-Sent Events csak küldésre képes. Amikor a tulajdonos megérinti a „pillanatkép mentése most” vagy a „kiváltási számláló nullázása” gombot az irányítópulton, az irányítópultnak üzenetet kell küldenie a kamerának. Ez a WebSockets – egyetlen TCP-socket, mindkét irányba áramló keretezett üzenetek.
10.8.1. A /control útvonal¶
A microdot.websocket.with_websocket() végrehajtja a WebSocket-frissítési kézfogást, és átad a kezelőnek egy WebSocket objektumot. A kezelő végtelen ciklusban fut, parancsokat olvas és nyugtázásokat küld:
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'})
A receive() szöveges keretek esetén stringet, bináris keretek esetén bájtokat ad vissza. A böngészőoldali WebSocket.send(...) alapértelmezetten szöveget küld, így a JSON-kódolt parancsok a természetes választás.
A send() stringeket, bájtokat vagy bármilyen JSON-serializálható objektumot elfogad – egy dict JSON szöveges keretként kerül elküldésre.
A WebSocketError akkor kerül dobásra, amikor a kliens lekapcsolódik (tiszta lezárás, hálózati megszakadás vagy protokollhiba). A kezelő kilép a ciklusból és visszatér; a microdot rendet rak a socket körül.
10.8.2. Az irányítópult parancsokat küld¶
Két gomb kerül az index.html fájlba a csúszka mellé:
<button id="snap-btn">Save snapshot</button>
<button id="reset-btn">Reset counter</button>
az app.js pedig egyszer megnyitja a WebSocketet, és mindkét gombot a send függvényhez köti:
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));
});
A ws:// és wss:// közötti választás tükrözi a http:// és https:// közöttit – a WebSocket ugyanazt a TLS-kezelést örökli. Ha a HTTPS a helyén van, az irányítópult automatikusan wss:// segítségével kapcsolódik.
10.8.3. Mikor válasszunk SSE-t és mikor WebSocketet¶
Használj SSE-t, amikor a kamera küld, és a böngésző csak figyel – értesítések, telemetria, állapotváltozások. A vezeték egyszerű HTTP, a kliensoldal egyetlen sor (new EventSource), és az újrakapcsolódás automatikus.
Használj WebSocketeket, amikor a böngészőnek is küldenie kell – gombok, billentyűleütés-sebességű frissítéseket küldő csúszkák, bármi, ahol a következő kérésre várás túl lassú lenne. A kapcsolat kétirányú és keretezett, de az API mindkét oldalon több munkát igényel.
A kamera most már interaktív dolog – figyel, eseményeket küld ki, és parancsokat fogad be.