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=2becomes amicrodot.MultiDictyou can read withrequest.args.get('foo').form– an HTML form posted asapplication/x-www-form-urlencoded. SameMultiDicttype.
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.