10.5. A control API for the cam#

The owner needs to set the motion detector’s sensitivity from anywhere – the wind moves trees more on a windy day. That means routes the dashboard can read the current settings from and post changes to.

A small shared state dict on the module is enough to hold the knobs. Later pages add more keys to it; for now there’s one:

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

10.5.1. GET to read, POST to write#

A pair of routes – one get, one post – gives the dashboard read/write access to 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 returns the body parsed as JSON, or None if Content-Type wasn’t application/json. The post handler walks each failure mode – missing key, wrong type, out of range – and bails with microdot.abort(), which raises microdot.HTTPException to short-circuit the handler with the given status and message.

10.5.2. GET, POST, PUT, DELETE#

get() and post() are the two we’ll use most. put() and delete() exist for cases that follow REST conventions – a PUT /events/42 to replace event 42, a DELETE /events/42 to drop it. The handler is otherwise identical.

10.5.3. Reading query strings and forms#

The dashboard posts JSON, so request.json is what we want. Two other ways the cam might receive data:

  • args – the query string. ?foo=1&bar=2 becomes a microdot.MultiDict you can read with request.args.get('foo').

  • form – an HTML form posted as application/x-www-form-urlencoded. Same MultiDict type.

The MultiDict is dict-like but lets one key carry multiple values (?tag=cat&tag=dog is two tag values); see microdot.MultiDict for the full surface.

10.5.4. Dynamic URL segments#

A route path can declare typed placeholders that microdot passes to the handler as extra arguments:

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

The supported converters are <int:>, <re:> for a custom regex, <path:> for a segment that can contain slashes, and the default (no prefix) for “match anything up to the next slash.” <int:event_id> accepts /events/42 and rejects /events/abc – the rejection becomes a 404 without the handler running.

10.5.5. Custom error responses#

The default 404 microdot sends is plain Not found. The dashboard expects JSON for every response; override the 404 handler so it returns JSON too:

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

errorhandler() takes either a status code (catches every error of that status) or an exception class (catches every handler that raised that exception). The (body, status) tuple short-circuits the response without constructing a Response.

The cam now exposes its state and accepts edits.