Milestone XProtect
Integrates Milestone XProtect video management (Express+, Professional+, Expert, Corporate) through the API Gateway REST API on the Management Server.
The driver does two things:
- Inventory and configuration state — enumerates cameras and recording servers so each camera can become a GEM zone, and tracks each camera's enabled/disabled state.
- Generic events — sends strings to the XProtect Event Server so a GEM macro, trigger or alarm can fire an XProtect rule (start recording, create a bookmark, raise an alarm in Smart Client).
XProtect does not publish RTSP from the Management Server. For live streams,
point GEM at the camera directly using its own driver
(onvif, vapix, hikvision_isapi, …)
or deploy the XProtect ONVIF Bridge. This driver covers configuration and
events only.
Prerequisites
- XProtect 2020 R3 or newer with the API Gateway installed. The gateway is
an optional component in the Milestone installer and is very often missing
on systems that were upgraded rather than freshly installed. Before doing
anything else, browse to
https://<management-server>/api/from the GEM server. If that 404s, add the API Gateway component from the XProtect installer. - A Milestone basic user. In the Management Client:
Security ▸ Roles ▸ Users and Groups ▸ Add ▸ Basic user. Windows and Active Directory accounts authenticate with a different grant type that this driver does not implement. - Role permissions. The basic user's role needs read access to the cameras
and recording servers you want to expose.
enable_camera/disable_cameraadditionally need Manage permission on cameras. - For generic events:
Tools ▸ Options ▸ Generic Events. Enable a data source — the built-in Compatible source listens on TCP 1234, the International source on 1235 — and add the GEM server's IP to that source's allowed-address list. Events from an address that is not on the list are dropped silently.
Setup steps
-
Verify the gateway. From the GEM server, browse to
https://<management-server>/api/. A response of any kind means the gateway is present; a connection failure or 404 means it is not. -
Create the basic user in the Management Client and assign it a role with read access to the relevant cameras.
-
Add the device. Go to Devices and create a device with driver
milestone_xprotect. Set Management Server Address, Basic User Name and Basic User Password. Leave the port at443for a standard install. -
Enumerate cameras. Run
get_cameras:{"cameras": [{"id": "12345678-1234-1234-1234-123456789abc","name": "Lobby North","enabled": true,"channel": "0","recording_server": "RS-01","hardware": "Axis P3245 (192.168.1.x)"}]} -
Create zones. Add one GEM zone per camera under the Cameras subsystem, with
zone.addressset to the camera'sidGUID. -
Wire up generic events (optional but the main reason to use this driver). In the Management Client,
Rules and Events ▸ Generic Events, create an event whose expression matches a string you choose — for exampleGEM_INTRUSION_LOBBY. Then build an XProtect rule triggered by that event. From GEM, callsend_generic_eventwith the same string.
Attribute reference
Device attributes
| Attribute | Required | Default | Description |
|---|---|---|---|
ip | yes | — | Management Server hostname or IP (the host running the API Gateway). |
username | yes | — | Milestone basic user name. |
password | yes | — | Basic user password. Stored encrypted. |
port | no | 443 | API Gateway port. |
protocol | no | https | https for a standard install. |
event_host | no | (= ip) | Host running the Event Server. Set only when it is a separate machine, which is common on Corporate installs. |
event_port | no | 1234 | TCP port of the generic-event data source. 1234 = Compatible, 1235 = International. |
status_interval | no | 60000 | Inventory poll interval in milliseconds. |
The driver also writes camera_count back onto the device row.
Zone attributes written by the driver
| Attribute | Type | Description |
|---|---|---|
state | string | enabled / disabled — the camera's administrative state in XProtect. Not a health signal; see limitations. |
camera_name | string | Display name configured in XProtect. |
recording_server | string | Recording server that owns the camera. |
hardware | string | Parent hardware device (the physical camera or encoder). |
channel | string | Channel number on multi-channel hardware such as encoders. |
Zone address format
zone.address is the XProtect camera GUID, exactly as returned by
get_cameras:
12345678-1234-1234-1234-123456789abc
GUIDs are stable across renames, so relabelling a camera in the Management Client will not break the GEM zone binding.
Commands
| Command | Arguments | Notes |
|---|---|---|
get_cameras | — | Enumerate cameras. Use the id field as zone.address. |
get_recording_servers | — | Enumerate recording servers. |
get_status | — | Inventory snapshot for cameras bound to a GEM zone. |
enable_camera | address | Enable the camera in the XProtect configuration. Needs Manage permission. |
disable_camera | address | Disable the camera. Needs Manage permission. |
send_generic_event | message | Send a raw string to the Event Server generic-event source. |
trigger_user_defined_event | address | Not implemented. See below. |
refresh | — | Force an immediate inventory poll. |
trigger_user_defined_event is stubbed
It is declared so it is discoverable, but it returns
{"error": "not yet implemented"} rather than guessing at a wire format.
Triggering an XProtect user-defined event goes through the MIP event
endpoint, which was not verified while building this driver. Use
send_generic_event instead — it reaches the same rule engine and is the
integration path Milestone documents for third-party systems.
Known limitations
- Configuration state, not health.
statereflects whether a camera is administratively enabled. A camera XProtect has completely lost contact with still reportsenabled. For reachability, use the camera's own driver or a ping monitor. - No video, no PTZ. Streams and PTZ control are not exposed by the Configuration API.
- No alarm or event ingestion. The driver sends events into XProtect; it does not read XProtect alarms back out. Consuming the XProtect alarm stream would need the events WebSocket, which is not implemented.
- Basic users only. Windows/AD logins are not supported.
- Generic events are fire-and-forget. The Event Server accepts the TCP
connection and returns nothing. A successful
send_generic_eventproves only that the bytes were accepted — not that any rule matched. enable_camera/disable_cameraare the least-verified calls. They issue aPATCHagainst the camera configuration item. Confirm behaviour on the target system before putting either into an unattended macro.- Cameras are fetched unpaged. On a very large Corporate system the first
inventory read can be slow — raise
request_timeoutif it trips.
Troubleshooting
Everything 404s. The API Gateway component is not installed. Re-run the XProtect installer on the Management Server and add it. This is the single most common cause of a failed setup.
authentication failed: invalid_grant.
Wrong username or password, or the account is a Windows/AD user rather than a
Milestone basic user. Create a basic user under
Security ▸ Roles ▸ Users and Groups.
Authentication succeeds but get_cameras returns an empty list.
The basic user's role has no read access to any camera. Grant the role camera
read permission in the Management Client.
http 403 on enable_camera / disable_camera.
The role can read cameras but cannot manage them. Add Manage permission, or
stop using those two commands.
TLS / certificate errors.
Milestone installs a self-signed certificate by default, and the driver accepts
it. If you still see TLS failures, the gateway is probably published on a port
that is not actually serving TLS — try protocol: http against the real
gateway port, or fix the certificate binding on the server.
send_generic_event reports success but nothing happens in XProtect.
Three things to check, in order: (1) the GEM server's IP is on the data
source's allowed-address list; (2) the generic event's expression actually
matches the string you sent — expressions are matched against the raw bytes,
so leading or trailing whitespace matters; (3) a rule is actually bound to that
generic event.
generic event timeout connecting to ….
Nothing is listening on event_port at event_host. On Corporate installs the
Event Server is frequently on a different machine from the Management Server —
set event_host explicitly.
Related
onvif— direct camera streams and eventsaxis_camera_station— AXIS Camera Station VMSsynology_surveillance— Surveillance Stationunifi_protect— UniFi Protect NVR