Skip to content

Instantly share code, notes, and snippets.

@curtishall
Last active March 10, 2025 03:20
Show Gist options
  • Save curtishall/27789d7db29818fbc4814f813816932b to your computer and use it in GitHub Desktop.
Save curtishall/27789d7db29818fbc4814f813816932b to your computer and use it in GitHub Desktop.
{
"openapi": "3.1.0",
"info": {
"version": "0.3.6",
"title": "Bluecherry Server API",
"description": "The Bluecherry Server API allows users to use software to create custom integrations with Bluecherry DVR",
"contact": {
"name": "Bluecherry Team",
"email": "[email protected]",
"url": "https://github.com/bluecherrydvr/"
},
"license": {
"name": "AGPL-2.0",
"url": "https://github.com/bluecherrydvr/bluecherry-api/blob/master/LICENSE"
}
},
"servers": [
{
"url": "http://localhost:4000/",
"description": "Bluecherry Server"
}
],
"tags": [
{
"name": "Devices"
},
{
"name": "Events"
},
{
"name": "Media"
},
{
"name": "PTZ"
}
],
"paths": {
"/devices": {
"post": {
"tags": [
"Devices"
],
"summary": "Add a new device",
"operationId": "addDevice",
"security": [
{
"basicAuth": []
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/addDeviceBody"
}
}
}
},
"responses": {
"200": {
"description": "Device created successfully",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"statusCode": {
"type": "number"
},
"message": {
"type": "string"
}
}
}
}
}
},
"400": {
"description": "Invalid request"
}
}
}
},
"/devices/{format}": {
"get": {
"tags": [
"Devices"
],
"summary": "Get all devices",
"operationId": "getDevices",
"security": [
{
"basicAuth": []
}
],
"parameters": [
{
"name": "format",
"in": "path",
"required": true,
"schema": {
"type": "string",
"enum": [
"json",
"xml"
]
}
}
],
"responses": {
"200": {
"description": "List of devices",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/device"
}
}
}
}
}
}
}
},
"/devices/{deviceId}/{format}": {
"get": {
"tags": [
"Devices"
],
"summary": "Get a specific device",
"operationId": "getDevice",
"security": [
{
"basicAuth": []
}
],
"parameters": [
{
"name": "deviceId",
"in": "path",
"required": true,
"schema": {
"type": "integer"
}
},
{
"name": "format",
"in": "path",
"required": true,
"schema": {
"type": "string",
"enum": [
"json",
"xml"
]
}
}
],
"responses": {
"200": {
"description": "Device information",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/device"
}
}
}
},
"404": {
"description": "Device not found"
}
}
}
},
"/devices/{deviceId}": {
"delete": {
"tags": [
"Devices"
],
"summary": "Delete a device",
"operationId": "deleteDevice",
"security": [
{
"basicAuth": []
}
],
"parameters": [
{
"name": "deviceId",
"in": "path",
"required": true,
"schema": {
"type": "integer"
}
}
],
"responses": {
"200": {
"description": "Device deleted successfully"
},
"404": {
"description": "Device not found"
}
}
},
"put": {
"tags": [
"Devices"
],
"summary": "Update a device",
"operationId": "updateDevice",
"security": [
{
"basicAuth": []
}
],
"parameters": [
{
"name": "deviceId",
"in": "path",
"required": true,
"schema": {
"type": "integer"
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/updateDeviceBody"
}
}
}
},
"responses": {
"200": {
"description": "Device updated successfully"
},
"404": {
"description": "Device not found"
}
}
}
},
"/events": {
"get": {
"tags": [
"Events"
],
"summary": "Get all events",
"operationId": "getEvents",
"security": [
{
"basicAuth": []
}
],
"parameters": [
{
"name": "limit",
"in": "query",
"required": false,
"schema": {
"type": "integer"
}
},
{
"name": "device",
"in": "query",
"required": false,
"schema": {
"type": "integer"
}
},
{
"name": "start",
"in": "query",
"required": false,
"schema": {
"type": "integer"
}
},
{
"name": "end",
"in": "query",
"required": false,
"schema": {
"type": "integer"
}
}
],
"responses": {
"200": {
"description": "List of events",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/event"
}
}
}
}
}
}
}
},
"/events/{eventId}": {
"get": {
"tags": [
"Events"
],
"summary": "Get a specific event",
"operationId": "getEvent",
"security": [
{
"basicAuth": []
}
],
"parameters": [
{
"name": "eventId",
"in": "path",
"required": true,
"schema": {
"type": "integer"
}
}
],
"responses": {
"200": {
"description": "Event information",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/event"
}
}
}
},
"404": {
"description": "Event not found"
}
}
}
},
"/media/{mediaId}": {
"get": {
"tags": [
"Media"
],
"summary": "Get media file",
"operationId": "getMedia",
"security": [
{
"basicAuth": []
}
],
"parameters": [
{
"name": "mediaId",
"in": "path",
"required": true,
"schema": {
"type": "integer"
}
}
],
"responses": {
"200": {
"description": "Media file",
"content": {
"video/mp4": {
"schema": {
"type": "string",
"format": "binary"
}
}
}
},
"404": {
"description": "Media not found"
}
}
}
},
"/devices/{deviceId}/ptz/move": {
"post": {
"tags": [
"PTZ"
],
"summary": "Move PTZ camera",
"operationId": "movePTZ",
"security": [
{
"basicAuth": []
}
],
"parameters": [
{
"name": "deviceId",
"in": "path",
"required": true,
"schema": {
"type": "integer"
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ptzMoveBody"
}
}
}
},
"responses": {
"200": {
"description": "PTZ movement command sent successfully"
},
"404": {
"description": "Device not found or PTZ not supported"
}
}
}
},
"/devices/{deviceId}/ptz/stop": {
"post": {
"tags": [
"PTZ"
],
"summary": "Stop PTZ movement",
"operationId": "stopPTZ",
"security": [
{
"basicAuth": []
}
],
"parameters": [
{
"name": "deviceId",
"in": "path",
"required": true,
"schema": {
"type": "integer"
}
}
],
"responses": {
"200": {
"description": "PTZ stop command sent successfully"
},
"404": {
"description": "Device not found or PTZ not supported"
}
}
}
},
"/devices/{deviceId}/ptz/presets": {
"get": {
"tags": [
"PTZ"
],
"summary": "Get PTZ presets",
"operationId": "getPTZPresets",
"security": [
{
"basicAuth": []
}
],
"parameters": [
{
"name": "deviceId",
"in": "path",
"required": true,
"schema": {
"type": "integer"
}
}
],
"responses": {
"200": {
"description": "List of PTZ presets",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/ptzPreset"
}
}
}
}
},
"404": {
"description": "Device not found or PTZ not supported"
}
}
}
}
},
"components": {
"securitySchemes": {
"basicAuth": {
"type": "http",
"scheme": "basic"
}
},
"schemas": {
"addDeviceBody": {
"type": "object",
"required": [
"camName",
"ipAddr",
"username",
"password",
"protocol",
"rtspPath",
"substream",
"substreamPath",
"rtspPort",
"preferTcp"
],
"properties": {
"camName": {
"type": "string",
"examples": [
"Back Door",
"Pool"
]
},
"ipAddress": {
"type": "string",
"examples": [
"192.168.255.255",
"10.2.5.255"
]
},
"username": {
"type": "string",
"description": "The username used to access the camera",
"examples": [
"Admin"
]
},
"password": {
"type": "string",
"description": "The password used to access the camera",
"format": "password",
"examples": [
"Bluech3rryAdm!n_"
]
},
"protocol": {
"type": "string",
"description": "The protocol to be used to access this camera. Can only be \"IP-RTSP\" or \"IP-MJPEG\"",
"examples": [
"IP-RTSP",
"IP-MJPEG"
]
},
"rtspPath": {
"type": "string",
"examples": [
"/"
]
},
"substreamPath": {
"type": "string",
"examples": [
"/cam/realmonitor?channel=1&subtype=1&unicast=true&proto=Onvif"
]
},
"rtspPort": {
"type": "integer",
"examples": [
80,
554
]
},
"preferTcp": {
"type": "boolean",
"examples": [
true,
false
]
},
"substreamMode": {
"type": "integer",
"examples": [
0
]
},
"model": {
"type": "string",
"examples": [
"Generic",
"Lorex"
]
},
"mjpegPath": {
"type": "string",
"examples": [
"/"
]
},
"mjpegPort": {
"type": "integer",
"examples": [
554,
80
]
},
"onvifPort": {
"type": "integer",
"examples": [
80
]
},
"hlsWindowSize": {
"type": "integer",
"examples": [
5
]
},
"hlsSegmentSize": {
"type": "integer"
},
"hlsSegmentDuration": {
"type": "number"
},
"audioEnabled": {
"type": "boolean",
"examples": [
false,
true
]
}
}
},
"updateDeviceBody": {
"type": "object",
"properties": {
"camName": {
"type": "string"
},
"ipAddress": {
"type": "string"
},
"username": {
"type": "string"
},
"password": {
"type": "string"
},
"protocol": {
"type": "string"
},
"rtspPath": {
"type": "string"
},
"substreamPath": {
"type": "string"
},
"rtspPort": {
"type": "integer"
},
"preferTcp": {
"type": "boolean"
},
"substreamMode": {
"type": "boolean"
},
"model": {
"type": "string"
},
"mjpegPath": {
"type": "string"
},
"mjpegPort": {
"type": "integer"
},
"onvifPort": {
"type": "integer"
},
"audioEnabled": {
"type": "boolean"
},
"audioVolume": {
"type": "integer"
},
"brightness": {
"type": "integer"
},
"contrast": {
"type": "integer"
},
"disabled": {
"type": "boolean"
},
"frameDownscaleFactor": {
"type": "number"
},
"hlsSegmentDuration": {
"type": "number"
},
"hlsSegmentSize": {
"type": "integer"
},
"hlsWindowSize": {
"type": "integer"
},
"invert": {
"type": "boolean"
},
"maxMotionArea": {
"type": "integer"
},
"maxMotionFrames": {
"type": "integer"
},
"minMotionFrames": {
"type": "integer"
},
"motionAlgorithm": {
"type": "boolean"
},
"motionAnalysisPercentage": {
"type": "integer"
},
"motionAnalysisSswLength": {
"type": "integer"
},
"motionBlendRatio": {
"type": "integer"
},
"motionDebug": {
"type": "boolean"
},
"onvifEventsEnabled": {
"type": "boolean"
},
"saturation": {
"type": "integer"
},
"scheduleOverrideGlobal": {
"type": "boolean"
},
"signalType": {
"type": "string"
},
"videoInterval": {
"type": "string"
},
"videoQuality": {
"type": "integer"
}
}
},
"device": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"device_name": {
"type": "string"
},
"protocol": {
"type": "string"
},
"device": {
"type": "string"
},
"rtsp_username": {
"type": "string"
},
"rtsp_password": {
"type": "string"
},
"disabled": {
"type": "boolean"
},
"audio_disabled": {
"type": "boolean"
},
"audio_volume": {
"type": "integer"
},
"brightness": {
"type": "integer"
},
"contrast": {
"type": "integer"
},
"saturation": {
"type": "integer"
},
"hue": {
"type": "integer"
},
"debug_level": {
"type": "integer"
},
"frame_downscale_factor": {
"type": "number"
},
"motion_algorithm": {
"type": "boolean"
},
"motion_debug": {
"type": "boolean"
},
"motion_map": {
"type": "string"
},
"onvif_events_enabled": {
"type": "boolean"
},
"onvif_port": {
"type": "integer"
},
"schedule": {
"type": "string"
},
"schedule_override_global": {
"type": "boolean"
}
}
},
"event": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"time": {
"type": "integer"
},
"level_id": {
"type": "string"
},
"device_id": {
"type": "integer"
},
"type_id": {
"type": "string"
},
"length": {
"type": "integer"
},
"archive": {
"type": "boolean"
},
"media_id": {
"type": "integer"
},
"details": {
"type": "string"
}
}
},
"ptzMoveBody": {
"type": "object",
"required": [
"direction"
],
"properties": {
"direction": {
"type": "string",
"enum": [
"left",
"right",
"up",
"down",
"zoomIn",
"zoomOut"
],
"description": "Direction of PTZ movement"
},
"speed": {
"type": "integer",
"minimum": 1,
"maximum": 100,
"description": "Movement speed (1-100)",
"default": 50
},
"duration": {
"type": "number",
"description": "Duration of movement in seconds",
"default": 3,
"examples": [
0.5,
3,
5
]
}
}
},
"ptzPreset": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"description": "Preset ID"
},
"name": {
"type": "string",
"description": "Preset name"
}
}
},
"ptzContinuous": {
"type": "object",
"required": [
"x",
"y",
"zoom"
],
"properties": {
"x": {
"type": "number",
"description": "Pan speed (-1.0 to 1.0)",
"examples": [
-0.5,
0,
1.0
]
},
"y": {
"type": "number",
"description": "Tilt speed (-1.0 to 1.0)",
"examples": [
-0.5,
0,
1.0
]
},
"zoom": {
"type": "number",
"description": "Zoom speed (-1.0 to 1.0)",
"examples": [
-0.5,
0,
1.0
]
},
"duration": {
"type": "number",
"description": "Duration of movement in seconds",
"default": 3,
"examples": [
0.5,
3,
5
]
}
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment