mirror of
https://github.com/scottlamb/moonfire-nvr.git
synced 2024-12-26 07:05:56 -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
|
All requests for JSON data should be sent with the header
|
||||||
`Accept: application/json` (exactly).
|
`Accept: application/json` (exactly).
|
||||||
|
|
||||||
### `/api/login`
|
### `POST /api/login`
|
||||||
|
|
||||||
A `POST` request on this URL should have an `application/x-www-form-urlencoded`
|
The request should have an `application/x-www-form-urlencoded` body containing
|
||||||
body containing `username` and `password` parameters.
|
`username` and `password` parameters.
|
||||||
|
|
||||||
On successful authentication, the server will return an HTTP 204 (no content)
|
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
|
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;
|
(forbidden) response. Currently the body will be a `text/plain` error message;
|
||||||
future versions will likely be more sophisticated.
|
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`
|
The request should have an `application/x-www-form-urlencoded` body containing
|
||||||
body containing a `csrf` parameter copied from the `session.csrf` of the
|
a `csrf` parameter copied from the `session.csrf` of the
|
||||||
top-level API request.
|
top-level API request.
|
||||||
|
|
||||||
On success, returns an HTTP 204 (no content) responses. On failure, returns a
|
On success, returns an HTTP 204 (no content) responses. On failure, returns a
|
||||||
4xx response with `text/plain` error message.
|
4xx response with `text/plain` error message.
|
||||||
|
|
||||||
### `/api/`
|
### `GET /api/`
|
||||||
|
|
||||||
A `GET` request on this URL returns basic information about the server,
|
Returns basic information about the server, including all cameras. Valid
|
||||||
including all cameras. Valid request parameters:
|
request parameters:
|
||||||
|
|
||||||
* `days`: a boolean indicating if the days parameter described below
|
* `days`: a boolean indicating if the days parameter described below
|
||||||
should be included.
|
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.
|
`direct` or `indirect`. See `db/schema.sql` for more description.
|
||||||
* `type`: a UUID, expected to match one of `signalTypes`.
|
* `type`: a UUID, expected to match one of `signalTypes`.
|
||||||
* `days`: as in `cameras.streams.days` above.
|
* `days`: as in `cameras.streams.days` above.
|
||||||
|
**status: unimplemented**
|
||||||
* `signalTypes`: a list of all known signal types.
|
* `signalTypes`: a list of all known signal types.
|
||||||
* `uuid`: in text format.
|
* `uuid`: in text format.
|
||||||
* `states`: a map of all possible states of the enumeration to more
|
* `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:
|
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:
|
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
|
Returns a `.mp4` file, with an etag and support for range requests. The MIME
|
||||||
MIME type will be `video/mp4`, with a `codecs` parameter as specified in [RFC
|
type will be `video/mp4`, with a `codecs` parameter as specified in
|
||||||
6381][rfc-6381].
|
[RFC 6381][rfc-6381].
|
||||||
|
|
||||||
Expected query parameters:
|
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
|
TODO: error behavior on missing segment. It should be a 404, likely with an
|
||||||
`application/json` body describing what portion if any (still) exists.
|
`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.
|
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
|
media segment][media-segment]. The MIME type will be `video/mp4`, with a
|
||||||
`codecs` parameter as specified in [RFC 6381][rfc-6381].
|
`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
|
than one video sample entry, so a `.m4s` that uses more than one video
|
||||||
sample entry can't be used.
|
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
|
Returns a `text/plain` debugging string for the `.mp4` generated by the same
|
||||||
same URL minus the `.txt` suffix.
|
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
|
Returns a `multipart/mixed` sequence of parts. An extra top-level header,
|
||||||
header, `X-Open-Id`, contains the `openId` which is assigned to all recordings
|
`X-Open-Id`, contains the `openId` which is assigned to all recordings in this
|
||||||
in this live stream.
|
live stream.
|
||||||
|
|
||||||
Each part is a `.mp4` media segment that starts with a key frame and contains
|
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
|
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.0-180002`
|
||||||
* `/api/cameras/fd20f7a2-9d69-4cb3-94ed-d51a20c3edfe/main/view.m4s?s=5681@42.180002-360004`
|
* `/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
|
initialization segment][init-segment]. The MIME type will be `video/mp4`, with
|
||||||
a `codecs` parameter as specified in [RFC 6381][rfc-6381].
|
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.
|
same URL minus the `.txt` suffix.
|
||||||
|
|
||||||
### `/api/signals`
|
### `GET /api/signals`
|
||||||
|
|
||||||
A GET returns an `application/json` response with state of every signal for
|
Returns an `application/json` response with state of every signal for the
|
||||||
the requested timespan.
|
requested timespan.
|
||||||
|
|
||||||
Valid request parameters:
|
Valid request parameters:
|
||||||
|
|
||||||
@ -522,6 +523,130 @@ This represents the following observations:
|
|||||||
2. signal 1 entered state 2 (`on`) at time 130985424000000.
|
2. signal 1 entered state 2 (`on`) at time 130985424000000.
|
||||||
3. signal 1 entered state 1 (`off`) at time 130985418600000.
|
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
|
[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
|
[init-segment]: https://w3c.github.io/media-source/isobmff-byte-stream-format.html#iso-init-segments
|
||||||
[rfc-6381]: https://tools.ietf.org/html/rfc6381
|
[rfc-6381]: https://tools.ietf.org/html/rfc6381
|
||||||
|
Loading…
Reference in New Issue
Block a user