10.5. API керування камерою

Власникові потрібно налаштовувати чутливість детектора руху звідусіль – у вітряний день вітер рухає деревами більше. Це означає маршрути, з яких панель моніторингу може читати поточні налаштування і публікувати зміни.

Невеликий спільний словник стану на рівні модуля достатній для зберігання параметрів. На наступних сторінках до нього додаються нові ключі; поки що є лише один:

state = {
    'threshold': 12,
    'frame_count': 0,
    'trigger_count': 0,
}

10.5.1. GET для читання, POST для запису

Пара маршрутів – один get, один post – дає панелі моніторингу доступ до state на читання/запис:

from microdot import abort

@app.get('/config')
async def get_config(request):
    return state

@app.post('/config')
async def set_config(request):
    body = request.json
    if not body or 'threshold' not in body:
        abort(400, 'missing threshold')
    try:
        threshold = int(body['threshold'])
    except (TypeError, ValueError):
        abort(400, 'threshold must be an integer')
    if not 0 <= threshold <= 100:
        abort(400, 'threshold out of range')
    state['threshold'] = threshold
    return {'ok': True, 'threshold': threshold}

microdot.Request.json повертає тіло, розібране як JSON, або None, якщо Content-Type не був application/json. Обробник post перевіряє кожен режим відмови – відсутній ключ, неправильний тип, поза діапазоном – і завершує роботу з microdot.abort(), що генерує microdot.HTTPException для короткого замикання обробника з заданим статусом і повідомленням.

10.5.2. GET, POST, PUT, DELETE

get() і post() – це ті два, які ми будемо використовувати найчастіше. put() і delete() існують для випадків, що дотримуються REST-конвенцій – PUT /events/42 для заміни події 42, DELETE /events/42 для її видалення. В іншому обробник ідентичний.

10.5.3. Читання рядків запиту та форм

Панель моніторингу публікує JSON, тому request.json – це те, що нам потрібно. Два інші способи, якими камера може отримувати дані:

  • args – рядок запиту. ?foo=1&bar=2 стає microdot.MultiDict, який можна читати через request.args.get('foo').

  • form – HTML-форма, опублікована як application/x-www-form-urlencoded. Той самий тип MultiDict.

MultiDict схожий на dict, але дозволяє одному ключу мати кілька значень (?tag=cat&tag=dog – це два значення tag); дивіться microdot.MultiDict для повного опису.

10.5.4. Динамічні сегменти URL

Шлях маршруту може оголошувати типізовані заповнювачі, які microdot передає обробнику як додаткові аргументи:

@app.get('/events/<int:event_id>')
async def get_event(request, event_id):
    return {'id': event_id, 'msg': 'placeholder'}

Підтримувані конвертери: <int:>, <re:> для власного регулярного виразу, <path:> для сегмента, що може містити слеші, і стандартний (без префіксу) для «відповідності будь-чому до наступного слеша». <int:event_id> приймає /events/42 і відхиляє /events/abc – відхилення стає 404 без запуску обробника.

10.5.5. Нестандартні відповіді на помилки

Стандартне повідомлення 404 від microdot – це простий текст Not found. Панель моніторингу очікує JSON для кожної відповіді; замініть обробник 404, щоб він також повертав JSON:

@app.errorhandler(404)
async def not_found(request):
    return {'error': 'not found', 'path': request.path}, 404

errorhandler() приймає або код статусу (перехоплює всі помилки з цим статусом), або клас виключення (перехоплює кожен обробник, що згенерував це виключення). Кортеж (body, status) скорочує відповідь без конструювання Response.

Камера тепер відкриває свій стан і приймає зміни.