10.13. Nahrávání spuštěných snímků do cloudu

Když se spustí detekce pohybu, kamera nyní rozsvítí dashboard. To stačí pro živé použití, ale majitel chce navíc trvalý archiv každého spuštěného snímku, uložený někde mimo kameru. To je odchozí HTTP volání – kamera vystupuje jako klient.

10.13.1. Kamera jako klient

Modul requests je odchozí HTTP klient kamery. Jeho rozhraní je záměrnou kopií requests z CPythonu – stejné funkce modulu pojmenované podle sloves, stejné klíčové argumenty files=, json=, headers=, auth=. Pokud jste už dělali HTTP volání z CPythonu, toto API už znáte:

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() otevře TCP spojení, odešle požadavek a vrátí Response s status_code, reason, headers, content, json() a ostatními známými vlastnostmi.

files={...} sestaví tělo typu multipart/form-data. Hodnotou je n-tice (filename, file-like); requests.post() čte objekt podobný souboru po částech, takže se celý JPEG nemusí nejdřív znovu načítat do řetězce. io.BytesIO obalí JPEG bajty, které už jsou v paměti, takže zpřístupní rozhraní pro čtení jako ze souboru.

headers={...} je obyčejný slovník, který se odešle jako hlavičky požadavku – zde bearer token na standardní pozici Authorization. Poskytovatel archivu dokumentuje, jaký formát tokenu vyžaduje; tento příklad ukazuje nejběžnější podobu.

10.13.2. Zapojení do detektoru pohybu

Korutina detektoru pohybu představená dříve již běží na každém novém snímku a spustí se, když change > state['threshold']. Přidejte tam nahrávání, ale spusťte jej jako úlohu na pozadí, aby detektor během probíhajícího nahrávání nepřestal sledovat:

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() naplánuje korutinu pro nahrávání a okamžitě se vrátí. Detektor dál snímá obrazy; nahrávání běží souběžně s ním; kamera se nikdy nezastaví.

10.13.3. Režimy selhání

Síťový kód selhává. Kamera může být offline, archiv může být nedostupný, bearer token mohl vypršet. Kategorie, které stojí za odchycení:

  • OSError – TCP spojení nešlo otevřít nebo bylo uzavřeno uprostřed přenosu. Selhání DNS, žádná trasa, reset spojení. requests vyvolá přesně tuto výjimku.

  • status_code >= 400 – server obdržel požadavek a odmítl jej. 401 pro vypršený token, 403 pro odvolaný, 413 pro příliš velké tělo, 5xx pro nezdravý archiv.

  • Tichý timeout – requests používá výchozí timeout socketu (několik sekund); po jeho překročení vyvolá OSError s errno.ETIMEDOUT.

U archivu, na kterém skutečně záleží, byste odmítnuté snímky zařazovali do fronty v /sdcard/pending/ a zkoušeli je znovu v pomalejší smyčce – to je pár dalších řádků na případ, nad rámec toho, co je zde ukázáno.

10.13.4. Co requests nedělá

Port pro MicroPython je záměrně malý. Pár věcí, které requests z CPythonu dělá a tento neumí:

  • Sdílení spojení (connection pooling). Každé volání otevírá nové TCP spojení.

  • Automatické opakování při přechodných chybách. Obalte volání sami.

  • Streamování odpovědí. r.content se celé načte do RAM; neexistuje ekvivalent stream=True.

  • Automatická dekomprese gzipovaných odpovědí. Hlavičku Accept-Encoding nastavte explicitně jen tehdy, pokud je pro to server nakonfigurován.

Úplný seznam metod a co spadá do rozsahu či mimo něj najdete v requests — HTTP klient.

HTTPS funguje hned od začátku – řídí jej schéma URL a výchozí SSL kontext se vytvoří za běhu. Pro ověření certifikátu archivu proti CA bundlu, který jste nahráli do kamery, viz sekci jako klient v Ověřování veřejného serveru (kamera jako klient).

Aplikace je plně dodaná: živý náhled, detekce pohybu, dashboard s přihlášením, HTTPS, CORS/CSRF, cloudový archiv.