Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,77 @@ GET /api/timeline/play

Streams video for timeline playback at a specified point in time.

### Fleet Query and Selectors

#### Query Cameras

```
POST /api/fleet/cameras/query
```

Returns an authorized, server-paginated camera inventory with optional facets.
The `address` field contains only the source scheme and network authority; paths,
query strings, fragments, and embedded credentials are omitted. Existing
`allowed_tags` restrictions are applied before totals and facet counts are
calculated.

```json
{
"selector": {
"version": 1,
"expression": {
"op": "and",
"children": [
{"op": "location_subtree", "uuid": "location-uuid"},
{"op": "tag_any", "uuids": ["tag-uuid"]},
{"op": "health", "values": ["down", "degraded"]}
]
}
},
"search": "north door",
"page": 1,
"page_size": 50,
"sort_by": "name",
"sort_order": "asc",
"facets": true,
"explain": false
}
```

`page_size` is limited to 200. Supported sort fields are `name`,
`camera_uuid`, `location`, `health`, `enabled`, `recording_mode`, and
`address`. Results use the selected field plus camera UUID as a stable
tie-breaker.

Selector version 1 supports:

- Boolean nodes: `and` with `children`, `or` with `children`, and `not` with
`child`.
- `all`.
- `camera_uuid` with `values`.
- `location_subtree` with `uuid`.
- `tag_any`, `tag_all`, and `tag_none` with tag `uuids`.
- `enabled` with a boolean `value`.
- `recording_mode` with `values` from `off`, `continuous`, and `detection`.
- `vendor` and `model` with case-insensitive `values`. Inventory values are
populated as ONVIF inventory support becomes available.
- `capability_any` and `capability_all` with `values` from `onvif`, `ptz`, and
`backchannel`.
- `health` with `values` from `unknown`, `up`, `degraded`, `down`, and
`disabled`.

Selectors are limited to 8 levels, 64 nodes, and 64 values per node.

#### Preview Selector

```
POST /api/fleet/selectors/preview
```

Accepts the same request as the query endpoint, caps pages at 50 cameras, and
adds `matched_clauses` to each returned camera. An optional `camera_uuid`
restricts the preview to one camera.

### System

#### Get System Information
Expand Down
92 changes: 92 additions & 0 deletions include/core/camera_selector.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#ifndef LIGHTNVR_CAMERA_SELECTOR_H
#define LIGHTNVR_CAMERA_SELECTOR_H

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <cjson/cJSON.h>

#include "core/config.h"

#define FLEET_SELECTOR_VERSION 1
#define FLEET_SELECTOR_MAX_DEPTH 8
#define FLEET_SELECTOR_MAX_NODES 64
#define FLEET_SELECTOR_MAX_VALUES 64
#define FLEET_CAMERA_MAX_TAGS 64
#define FLEET_CAMERA_MAX_LOCATION_DEPTH 32
#define FLEET_LOCATION_PATH_MAX 1024
#define FLEET_INVENTORY_VALUE_MAX 128
#define FLEET_SELECTOR_ERROR_MAX 256
#define FLEET_EXPLANATION_MAX_CLAUSES 64
#define FLEET_EXPLANATION_CLAUSE_MAX 160

typedef enum {
FLEET_HEALTH_UNKNOWN = 0,
FLEET_HEALTH_UP,
FLEET_HEALTH_DEGRADED,
FLEET_HEALTH_DOWN,
FLEET_HEALTH_DISABLED
} fleet_health_state_t;

typedef struct {
char uuid[CAMERA_UUID_STRING_SIZE];
char label[256];
} fleet_camera_tag_t;

/* Credential-free camera inventory record consumed by selectors and fleet APIs. */
typedef struct {
char camera_uuid[CAMERA_UUID_STRING_SIZE];
char name[MAX_STREAM_NAME];
char address[MAX_URL_LENGTH];
char legacy_tags[256];
char location_uuid[CAMERA_UUID_STRING_SIZE];
char location_name[128];
char location_path[FLEET_LOCATION_PATH_MAX];
char location_ancestor_uuids[FLEET_CAMERA_MAX_LOCATION_DEPTH]
[CAMERA_UUID_STRING_SIZE];
int location_depth;
fleet_camera_tag_t tags[FLEET_CAMERA_MAX_TAGS];
int tag_count;
char manufacturer[FLEET_INVENTORY_VALUE_MAX];
char model[FLEET_INVENTORY_VALUE_MAX];
bool enabled;
bool record;
bool detection_based_recording;
bool is_onvif;
bool ptz_enabled;
bool backchannel_enabled;
fleet_health_state_t health;
int64_t last_frame_ts;
double current_fps;
bool recording_active;
} fleet_camera_t;

typedef struct fleet_selector fleet_selector_t;

typedef struct {
char clauses[FLEET_EXPLANATION_MAX_CLAUSES]
[FLEET_EXPLANATION_CLAUSE_MAX];
int clause_count;
} fleet_selector_explanation_t;

/*
* Parse a selector of the form:
* {"version":1,"expression":{"op":"and","children":[...]}}
*
* Leaf operations are all, camera_uuid, location_subtree, tag_any, tag_all,
* tag_none, enabled, recording_mode, vendor, model, capability_any,
* capability_all, and health. Unknown fields are ignored, but every operation's
* required typed fields are validated. The returned selector is immutable.
*/
fleet_selector_t *fleet_selector_parse(const cJSON *json,
char *error, size_t error_size);
void fleet_selector_free(fleet_selector_t *selector);

bool fleet_selector_matches(const fleet_selector_t *selector,
const fleet_camera_t *camera,
fleet_selector_explanation_t *explanation);

const char *fleet_health_state_name(fleet_health_state_t state);
const char *fleet_camera_recording_mode(const fleet_camera_t *camera);

#endif /* LIGHTNVR_CAMERA_SELECTOR_H */
10 changes: 10 additions & 0 deletions include/database/db_fleet_query.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#ifndef LIGHTNVR_DB_FLEET_QUERY_H
#define LIGHTNVR_DB_FLEET_QUERY_H

#include "core/camera_selector.h"

/* Load a slim, credential-free fleet inventory snapshot. The caller owns the
* returned array and must free it. Zero cameras returns success with NULL. */
int db_fleet_camera_load(fleet_camera_t **cameras, int *count);

#endif /* LIGHTNVR_DB_FLEET_QUERY_H */
11 changes: 11 additions & 0 deletions include/web/api_handlers_fleet.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#ifndef LIGHTNVR_API_HANDLERS_FLEET_H
#define LIGHTNVR_API_HANDLERS_FLEET_H

#include "web/request_response.h"

void handle_post_fleet_camera_query(const http_request_t *req,
http_response_t *res);
void handle_post_fleet_selector_preview(const http_request_t *req,
http_response_t *res);

#endif /* LIGHTNVR_API_HANDLERS_FLEET_H */
Loading
Loading