mirror of
https://github.com/scottlamb/moonfire-nvr.git
synced 2024-12-25 22:55:55 -05:00
document proposed API for updating signals (#28)
This commit is contained in:
parent
6f2c63ffac
commit
d232ca55fa
195
design/api.md
195
design/api.md
@ -24,10 +24,10 @@ input such as a burglar alarm system's zone status.
|
||||
All requests for JSON data should be sent with the header
|
||||
`Accept: application/json` (exactly).
|
||||
|
||||
### `/api/login`
|
||||
### `POST /api/login`
|
||||
|
||||
A `POST` request on this URL should have an `application/x-www-form-urlencoded`
|
||||
body containing `username` and `password` parameters.
|
||||
The request should have an `application/x-www-form-urlencoded` body containing
|
||||
`username` and `password` parameters.
|
||||
|
||||
On successful authentication, the server will return an HTTP 204 (no content)
|
||||
with a `Set-Cookie` header for the `s` cookie, which is an opaque, HttpOnly
|
||||
@ -37,19 +37,19 @@ If authentication or authorization fails, the server will return a HTTP 403
|
||||
(forbidden) response. Currently the body will be a `text/plain` error message;
|
||||
future versions will likely be more sophisticated.
|
||||
|
||||
### `/api/logout`
|
||||
### `POST /api/logout`
|
||||
|
||||
A `POST` request on this URL should have an `application/x-www-form-urlencoded`
|
||||
body containing a `csrf` parameter copied from the `session.csrf` of the
|
||||
The request should have an `application/x-www-form-urlencoded` body containing
|
||||
a `csrf` parameter copied from the `session.csrf` of the
|
||||
top-level API request.
|
||||
|
||||
On success, returns an HTTP 204 (no content) responses. On failure, returns a
|
||||
4xx response with `text/plain` error message.
|
||||
|
||||
### `/api/`
|
||||
### `GET /api/`
|
||||
|
||||
A `GET` request on this URL returns basic information about the server,
|
||||
including all cameras. Valid request parameters:
|
||||
Returns basic information about the server, including all cameras. Valid
|
||||
request parameters:
|
||||
|
||||
* `days`: a boolean indicating if the days parameter described below
|
||||
should be included.
|
||||
@ -101,6 +101,7 @@ The `application/json` response will have a dict as follows:
|
||||
`direct` or `indirect`. See `db/schema.sql` for more description.
|
||||
* `type`: a UUID, expected to match one of `signalTypes`.
|
||||
* `days`: as in `cameras.streams.days` above.
|
||||
**status: unimplemented**
|
||||
* `signalTypes`: a list of all known signal types.
|
||||
* `uuid`: in text format.
|
||||
* `states`: a map of all possible states of the enumeration to more
|
||||
@ -190,9 +191,9 @@ Example response:
|
||||
}
|
||||
```
|
||||
|
||||
### `/api/cameras/<uuid>/`
|
||||
### `GET /api/cameras/<uuid>/`
|
||||
|
||||
A GET returns information for the camera with the given URL. The information
|
||||
Returns information for the camera with the given URL.
|
||||
|
||||
Example response:
|
||||
|
||||
@ -224,9 +225,9 @@ Example response:
|
||||
}
|
||||
```
|
||||
|
||||
### `/api/cameras/<uuid>/<stream>/recordings`
|
||||
### `GET /api/cameras/<uuid>/<stream>/recordings`
|
||||
|
||||
A GET returns information about recordings, in descending order.
|
||||
Returns information about recordings, in descending order.
|
||||
|
||||
Valid request parameters:
|
||||
|
||||
@ -308,11 +309,11 @@ Example response:
|
||||
}
|
||||
```
|
||||
|
||||
### `/api/cameras/<uuid>/<stream>/view.mp4`
|
||||
### `GET /api/cameras/<uuid>/<stream>/view.mp4`
|
||||
|
||||
A GET returns a `.mp4` file, with an etag and support for range requests. The
|
||||
MIME type will be `video/mp4`, with a `codecs` parameter as specified in [RFC
|
||||
6381][rfc-6381].
|
||||
Returns a `.mp4` file, with an etag and support for range requests. The MIME
|
||||
type will be `video/mp4`, with a `codecs` parameter as specified in
|
||||
[RFC 6381][rfc-6381].
|
||||
|
||||
Expected query parameters:
|
||||
|
||||
@ -356,14 +357,14 @@ Example request URI to retrieve recording id 1, skipping its first 26
|
||||
TODO: error behavior on missing segment. It should be a 404, likely with an
|
||||
`application/json` body describing what portion if any (still) exists.
|
||||
|
||||
### `/api/cameras/<uuid>/<stream>/view.mp4.txt`
|
||||
### `GET /api/cameras/<uuid>/<stream>/view.mp4.txt`
|
||||
|
||||
A GET returns a `text/plain` debugging string for the `.mp4` generated by the
|
||||
Returns a `text/plain` debugging string for the `.mp4` generated by the
|
||||
same URL minus the `.txt` suffix.
|
||||
|
||||
### `/api/cameras/<uuid>/<stream>/view.m4s`
|
||||
### `GET /api/cameras/<uuid>/<stream>/view.m4s`
|
||||
|
||||
A GET returns a `.mp4` suitable for use as a [HTML5 Media Source Extensions
|
||||
Returns a `.mp4` suitable for use as a [HTML5 Media Source Extensions
|
||||
media segment][media-segment]. The MIME type will be `video/mp4`, with a
|
||||
`codecs` parameter as specified in [RFC 6381][rfc-6381].
|
||||
|
||||
@ -387,16 +388,16 @@ recording segment for several reasons:
|
||||
than one video sample entry, so a `.m4s` that uses more than one video
|
||||
sample entry can't be used.
|
||||
|
||||
### `/api/cameras/<uuid>/<stream>/view.m4s.txt`
|
||||
### `GET /api/cameras/<uuid>/<stream>/view.m4s.txt`
|
||||
|
||||
A GET returns a `text/plain` debugging string for the `.mp4` generated by the
|
||||
same URL minus the `.txt` suffix.
|
||||
Returns a `text/plain` debugging string for the `.mp4` generated by the same
|
||||
URL minus the `.txt` suffix.
|
||||
|
||||
### `/api/cameras/<uuid>/<stream>/live.m4s`
|
||||
### `GET /api/cameras/<uuid>/<stream>/live.m4s`
|
||||
|
||||
A GET returns a `multipart/mixed` sequence of parts. An extra top-level
|
||||
header, `X-Open-Id`, contains the `openId` which is assigned to all recordings
|
||||
in this live stream.
|
||||
Returns a `multipart/mixed` sequence of parts. An extra top-level header,
|
||||
`X-Open-Id`, contains the `openId` which is assigned to all recordings in this
|
||||
live stream.
|
||||
|
||||
Each part is a `.mp4` media segment that starts with a key frame and contains
|
||||
all other frames which depend on that key frame. The following part headers
|
||||
@ -463,21 +464,21 @@ following URLs, respectively:
|
||||
* `/api/cameras/fd20f7a2-9d69-4cb3-94ed-d51a20c3edfe/main/view.m4s?s=5681@42.0-180002`
|
||||
* `/api/cameras/fd20f7a2-9d69-4cb3-94ed-d51a20c3edfe/main/view.m4s?s=5681@42.180002-360004`
|
||||
|
||||
### `/api/init/<sha1>.mp4`
|
||||
### `GET /api/init/<sha1>.mp4`
|
||||
|
||||
A GET returns a `.mp4` suitable for use as a [HTML5 Media Source Extensions
|
||||
Returns a `.mp4` suitable for use as a [HTML5 Media Source Extensions
|
||||
initialization segment][init-segment]. The MIME type will be `video/mp4`, with
|
||||
a `codecs` parameter as specified in [RFC 6381][rfc-6381].
|
||||
|
||||
### `/api/init/<sha1>.mp4.txt`
|
||||
### `GET /api/init/<sha1>.mp4.txt`
|
||||
|
||||
A GET returns a `text/plain` debugging string for the `.mp4` generated by the
|
||||
Returns a `text/plain` debugging string for the `.mp4` generated by the
|
||||
same URL minus the `.txt` suffix.
|
||||
|
||||
### `/api/signals`
|
||||
### `GET /api/signals`
|
||||
|
||||
A GET returns an `application/json` response with state of every signal for
|
||||
the requested timespan.
|
||||
Returns an `application/json` response with state of every signal for the
|
||||
requested timespan.
|
||||
|
||||
Valid request parameters:
|
||||
|
||||
@ -522,6 +523,130 @@ This represents the following observations:
|
||||
2. signal 1 entered state 2 (`on`) at time 130985424000000.
|
||||
3. signal 1 entered state 1 (`off`) at time 130985418600000.
|
||||
|
||||
### `POST /api/signals`
|
||||
|
||||
**status: unimplemented**
|
||||
|
||||
Alters the state of a signal.
|
||||
|
||||
Signal state can be broken into two parts: observed history and predicted
|
||||
future. Observed history is written to the database on next flush.
|
||||
Predicted future is only retained in RAM and returned on `GET` requests on the
|
||||
near future. This avoids having to display the "unknown" state when it's very
|
||||
likely that the state has not changed since the most recent update.
|
||||
|
||||
A typical request will add observed history from the time of a recent previous
|
||||
prediction until now, and add another prediction. Following `GET /api/signals`
|
||||
requests will return the new prediction until a pre-determined expiration
|
||||
time. The client typically will send a following request before this
|
||||
expiration time so that history is recorded and the UI never displays
|
||||
`unknown` when the signal is being actively managed.
|
||||
|
||||
Some requests may instead backfill earlier history, such as when a video
|
||||
analytics client starts up and analyzes all video segments recorded since it
|
||||
last ran. These will specify beginning and end times for the observed history
|
||||
and not make a prediction.
|
||||
|
||||
The request should have an `application/json` body describing the change to
|
||||
make. It should be a dict with these attributes:
|
||||
|
||||
* `signalIds`: a list of signal ids to change.
|
||||
* `observedStates`: (optional) a list (one entry per `signalIds` entry) of
|
||||
observed states to set.
|
||||
* `predictedStates`: (optional) a list (one entry per `signalIds` entry)
|
||||
of predictions to make. If absent, assumed to match `observedStates`.
|
||||
Only used if `predictedDur90k` is non-zero.
|
||||
* `observedStartTime90k` (optional): if absent, assumed to be now. Otherwise
|
||||
is typically a time from an earlier response.
|
||||
* `observedEndTime90k` (optional): if absent, assumed to be now.
|
||||
* `predictionDur90k` (optional): (only allowed when `endTime90k` is absent)
|
||||
additional time in which the current state should seem to "linger" if no
|
||||
further updates are received. a `GET /api/signals` request until this time
|
||||
will reflect the curent
|
||||
between the time this request
|
||||
|
||||
The response will be an `application/json` body dict with the following
|
||||
attributes:
|
||||
|
||||
* `time90k`: the current time. When the request's `observedStartTime90k`
|
||||
and/or `observedEndTime90k` were absent, this is needed to later upgrade
|
||||
predictions to history seamlessly.
|
||||
|
||||
Example request sequence:
|
||||
|
||||
#### Request 1
|
||||
|
||||
The client responsible for reporting live driveway motion has just started. It
|
||||
observes motion now. It records no history and predicts there will be motion
|
||||
for the next minute.
|
||||
|
||||
Request:
|
||||
|
||||
```json
|
||||
{
|
||||
'signalIds': [1],
|
||||
'predictedStates': [2],
|
||||
'predictionDur90k': 5400000
|
||||
}
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```json
|
||||
{
|
||||
'time90k': 140067468000000
|
||||
}
|
||||
```
|
||||
|
||||
#### Request 2
|
||||
|
||||
30 seconds later (half the prediction interval), the client still observes
|
||||
motion. It records the previous prediction and predicts the motion will continue.
|
||||
|
||||
Request:
|
||||
|
||||
```json
|
||||
{
|
||||
'signalIds': [1],
|
||||
'observedStates': [2],
|
||||
'observedStartTime90k': 140067468000000,
|
||||
'predictionDur90k': 5400000
|
||||
}
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```json
|
||||
{
|
||||
'time90k': 140067470700000
|
||||
}
|
||||
```
|
||||
|
||||
### Request 3
|
||||
|
||||
5 seconds later, the client observes motion has ended. It records the history
|
||||
and predicts no more motion.
|
||||
|
||||
Request:
|
||||
|
||||
```json
|
||||
{
|
||||
'signalIds': [1],
|
||||
'observedStates': [2],
|
||||
'predictedStates': [1],
|
||||
'observedStartTime90k': 140067470700000,
|
||||
'predictionDur90k': 5400000
|
||||
}
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```json
|
||||
{
|
||||
'time90k': 140067471150000
|
||||
}
|
||||
```
|
||||
|
||||
[media-segment]: https://w3c.github.io/media-source/isobmff-byte-stream-format.html#iso-media-segments
|
||||
[init-segment]: https://w3c.github.io/media-source/isobmff-byte-stream-format.html#iso-init-segments
|
||||
[rfc-6381]: https://tools.ietf.org/html/rfc6381
|
||||
|
Loading…
Reference in New Issue
Block a user