Skip to main content

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).
This driver does not carry video

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_camera additionally 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

  1. 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.

  2. Create the basic user in the Management Client and assign it a role with read access to the relevant cameras.

  3. 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 at 443 for a standard install.

  4. 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)"
    }
    ]
    }
  5. Create zones. Add one GEM zone per camera under the Cameras subsystem, with zone.address set to the camera's id GUID.

  6. 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 example GEM_INTRUSION_LOBBY. Then build an XProtect rule triggered by that event. From GEM, call send_generic_event with the same string.

Attribute reference

Device attributes

AttributeRequiredDefaultDescription
ipyesManagement Server hostname or IP (the host running the API Gateway).
usernameyesMilestone basic user name.
passwordyesBasic user password. Stored encrypted.
portno443API Gateway port.
protocolnohttpshttps for a standard install.
event_hostno(= ip)Host running the Event Server. Set only when it is a separate machine, which is common on Corporate installs.
event_portno1234TCP port of the generic-event data source. 1234 = Compatible, 1235 = International.
status_intervalno60000Inventory poll interval in milliseconds.

The driver also writes camera_count back onto the device row.

Zone attributes written by the driver

AttributeTypeDescription
statestringenabled / disabled — the camera's administrative state in XProtect. Not a health signal; see limitations.
camera_namestringDisplay name configured in XProtect.
recording_serverstringRecording server that owns the camera.
hardwarestringParent hardware device (the physical camera or encoder).
channelstringChannel 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

CommandArgumentsNotes
get_camerasEnumerate cameras. Use the id field as zone.address.
get_recording_serversEnumerate recording servers.
get_statusInventory snapshot for cameras bound to a GEM zone.
enable_cameraaddressEnable the camera in the XProtect configuration. Needs Manage permission.
disable_cameraaddressDisable the camera. Needs Manage permission.
send_generic_eventmessageSend a raw string to the Event Server generic-event source.
trigger_user_defined_eventaddressNot implemented. See below.
refreshForce 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. state reflects whether a camera is administratively enabled. A camera XProtect has completely lost contact with still reports enabled. 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_event proves only that the bytes were accepted — not that any rule matched.
  • enable_camera / disable_camera are the least-verified calls. They issue a PATCH against 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_timeout if 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.