10.13. Getriggerde frames uploaden naar de cloud¶
Wanneer beweging wordt gedetecteerd, licht de cam nu het dashboard op. Dat is genoeg voor live gebruik, maar de eigenaar wil ook een permanent archief van elk getriggerd frame, ergens buiten de cam opgeslagen. Dat is een uitgaande HTTP-aanroep – de cam fungeert als client.
10.13.1. De cam als client¶
De requests-module is de uitgaande HTTP-client van de cam. Het oppervlak ervan is een bewuste kopie van CPython’s requests – dezelfde naar werkwoorden vernoemde modulefuncties, dezelfde files=-, json=-, headers=- en auth=-keyword-argumenten. Als je HTTP-aanroepen vanuit CPython hebt gedaan, ken je de API al:
import requests
import io
ARCHIVE_URL = 'https://api.backyard-cloud.com/frames'
ARCHIVE_TOKEN = load_archive_token()
async def archive_frame(jpeg, ts):
try:
r = requests.post(
ARCHIVE_URL,
files={'image': (
'frame-{}.jpg'.format(ts),
io.BytesIO(jpeg),
)},
headers={'Authorization': 'Bearer ' + ARCHIVE_TOKEN},
)
except OSError as e:
print('upload failed:', e)
return False
if r.status_code >= 400:
print('archive rejected:', r.status_code, r.reason)
return False
return True
requests.post() opent een TCP-verbinding, verstuurt het verzoek en geeft een Response terug met status_code, reason, headers, content, json() en de rest van de bekende eigenschappen.
files={...} bouwt een multipart/form-data-body. De waarde is een (filename, file-like)-tuple; requests.post() leest het file-like object in stukken, zodat de hele JPEG niet eerst opnieuw tot een string hoeft te worden gebufferd. io.BytesIO omhult de reeds in het geheugen aanwezige JPEG-bytes, zodat ze een read-as-file interface bieden.
headers={...} is een gewone dict die als verzoekheaders wordt verstuurd – hier een bearer-token op de standaardpositie Authorization. De archiefprovider documenteert welk tokenformaat hij wil; het voorbeeld is de meest gangbare vorm.
10.13.2. Het koppelen aan de bewegingsdetector¶
De eerder geïntroduceerde bewegingsdetector-coroutine draait al bij elk nieuw frame en wordt geactiveerd wanneer change > state['threshold']. Voeg de upload daar toe, maar start hem als een achtergrondtaak zodat de detector niet stopt met kijken terwijl de upload onderweg is:
async def motion_detector():
global last_motion
prev = None
while True:
await new_frame.wait()
change = compute_change(prev, latest_jpeg)
if change > state['threshold']:
state['trigger_count'] += 1
ts = int(time.time())
last_motion = {'ts': ts,
'count': state['trigger_count'],
'change': change}
motion_event.set()
asyncio.create_task(archive_frame(latest_jpeg, ts))
prev = latest_jpeg
await asyncio.sleep_ms(50)
asyncio.create_task() plant de upload-coroutine in en keert onmiddellijk terug. De detector blijft frames pakken; de upload draait er parallel aan; de cam loopt nooit vast.
10.13.3. Faalmodi¶
Netwerkcode faalt. De cam kan offline zijn, het archief kan plat liggen, de bearer-token kan verlopen zijn. De categorieën die het waard zijn om af te vangen:
OSError– de TCP-verbinding kon niet worden geopend of werd halverwege de overdracht gesloten. DNS-fout, geen route, verbinding gereset.requestswerpt precies deze uitzondering op.status_code >= 400– de server heeft het verzoek ontvangen en geweigerd. 401 voor een verlopen token, 403 voor een ingetrokken token, 413 voor een te grote body, 5xx voor een ongezond archief.Stille time-out –
requestsgebruikt een standaard socket-time-out (een paar seconden); daarna werpt het eenOSErrorop meterrno.ETIMEDOUT.
Voor een archief dat er echt toe doet, zou je geweigerde frames in de wachtrij zetten in /sdcard/pending/ en opnieuw proberen op een tragere lus – dat zijn een paar regels extra per geval, bovenop wat hier getoond wordt.
10.13.4. Wat requests niet doet¶
De MicroPython-port is bewust klein. Een paar dingen die de CPython-versie van requests wel doet en deze niet:
Connection pooling. Elke aanroep opent een nieuwe TCP-verbinding.
Automatische nieuwe pogingen bij tijdelijke fouten. Wikkel de aanroep zelf in.
Streaming responses.
r.contentwordt volledig in het RAM ingelezen; er is geen equivalent vanstream=True.Automatische decompressie van gzip-responses. Stel de
Accept-Encoding-header alleen expliciet in als de server daarvoor is geconfigureerd.
Zie requests — HTTP-client voor de volledige lijst met methoden en wat wel en niet onder de reikwijdte valt.
HTTPS werkt out of the box – het URL-schema bepaalt het, en de standaard SSL-context wordt ter plekke aangemaakt. Zie de sectie als client van Een openbare server verifiëren (camera als client) voor het verifiëren van het certificaat van het archief tegen een CA-bundel die je op de cam hebt geladen.
De app is volledig opgeleverd: live preview, bewegingsdetectie, dashboard met login, HTTPS, CORS/CSRF en cloudarchief.