read-only signals support (#28)

This is mostly untested and useless by itself, but it's a starting
point. In particular:

* there's no way to set up signals or add/remove/update events yet
  except by manual changes to the database.
* if you associate a signal with a camera then remove the camera,
  hitting /api/ will error out.
This commit is contained in:
Scott Lamb
2019-06-06 16:18:13 -07:00
parent cb1bb5d810
commit 6f2c63ffac
9 changed files with 833 additions and 47 deletions

View File

@@ -13,6 +13,12 @@ In the future, this is likely to be expanded:
(at least for bootstrapping web authentication)
* mobile interface
## Terminology
*signal:* a timeseries with an enum value. Signals might represent a camera's
motion detection or day/night status. They could also represent an external
input such as a burglar alarm system's zone status.
## Detailed design
All requests for JSON data should be sent with the header
@@ -88,6 +94,21 @@ The `application/json` response will have a dict as follows:
time zone. It is usually 24 hours after the start time. It
might be 23 hours or 25 hours during spring forward or fall
back, respectively.
* `signals`: a map of all signals known to the server. Keys are ids. Values are
dictionaries with the following properties:
* `shortName`: a unique, human-readable description of the signal
* `cameras`: a map of associated cameras' UUIDs to the type of association:
`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.
* `signalTypes`: a list of all known signal types.
* `uuid`: in text format.
* `states`: a map of all possible states of the enumeration to more
information about them:
* `color`: a recommended color to use in UIs to represent this state,
as in the [HTML specification](https://html.spec.whatwg.org/#colours).
* `motion`: if present and true, directly associated cameras will be
considered to have motion when this signal is in this state.
* `session`: if logged in, a dict with the following properties:
* `username`
* `csrf`: a cross-site request forgery token for use in `POST` requests.
@@ -126,9 +147,45 @@ Example response:
},
...
],
"signals": {
1: {
"shortName": "driveway motion",
"cameras": {
"fd20f7a2-9d69-4cb3-94ed-d51a20c3edfe": "direct"
},
"type": "ee66270f-d9c6-4819-8b33-9720d4cbca6b",
"days": {
"2016-05-01": {
"endTime90k": 131595516000000,
"startTime90k": 131587740000000,
"totalDuration90k": 5400000
}
}
}
],
"signalTypes": [
{
"uuid": "ee66270f-d9c6-4819-8b33-9720d4cbca6b",
"states": {
0: {
"name": "unknown",
"color": "#000000"
},
1: {
"name": "off",
"color": "#888888"
},
2: {
"name": "on",
"color": "#ff8888",
"motion": true
}
}
}
],
"session": {
"username": "slamb",
"csrf": "2DivvlnKUQ9JD4ao6YACBJm8XK4bFmOc",
"csrf": "2DivvlnKUQ9JD4ao6YACBJm8XK4bFmOc"
}
}
```
@@ -182,9 +239,6 @@ Valid request parameters:
server should return a `continue` key which is expected to be returned on
following requests.)
TODO(slamb): once we support annotations, should they be included in the same
URI or as a separate `/annotations`?
In the property `recordings`, returns a list of recordings in arbitrary order.
Each recording object has the following properties:
@@ -420,6 +474,54 @@ a `codecs` parameter as specified in [RFC 6381][rfc-6381].
A GET returns a `text/plain` debugging string for the `.mp4` generated by the
same URL minus the `.txt` suffix.
### `/api/signals`
A GET returns an `application/json` response with state of every signal for
the requested timespan.
Valid request parameters:
* `startTime90k` and and `endTime90k` limit the data returned to only
events relevant to the given half-open interval. Either or both
may be absent; they default to the beginning and end of time, respectively.
This will return the current state as of the latest change (to any signal)
before the start time (if any), then all changes in the interval. This
allows the caller to determine the state at every moment during the
selected timespan, as well as observe all events (even instantaneous
ones).
Responses are several parallel arrays for each observation:
* `times90k`: the time of each event. Events are given in ascending order.
* `signalIds`: the id of the relevant signal; expected to match one in the
`signals` field of the `/api/` response.
* `states`: the new state.
Example request URI (with added whitespace between parameters):
```
/api/signals
?startTime90k=130888729442361
&endTime90k=130985466591817
```
Example response:
```json
{
"signalIds": [1, 1, 1],
"states": [1, 2, 1],
"times90k": [130888729440000, 130985424000000, 130985418600000]
}
```
This represents the following observations:
1. time 130888729440000 was the last change before the requested start;
signal 1 (`driveway motion`) was in state 1 (`off`).
2. signal 1 entered state 2 (`on`) at time 130985424000000.
3. signal 1 entered state 1 (`off`) at time 130985418600000.
[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