diff --git a/event/stream/topics.go b/event/stream/topics.go index 9071dad..1345233 100644 --- a/event/stream/topics.go +++ b/event/stream/topics.go @@ -79,10 +79,19 @@ var topicRules = []struct { // https://developer.axis.com/vapix/network-video/event-and-action-services/ {"MotionRegionDetector/Motion", KindMotion}, - // AXIS Guard suite — vendor analytics apps with CameraProfile - // suffixes. Treated as motion so they can drive motion-triggered - // recording on cameras using these apps instead of basic VMD. + // tns1:RuleEngine/tnsaxis:VMD3/vmd3_video_ — AXIS VMD 3, the + // firmware-builtin predecessor of the VMD 4 ACAP. It lives under + // RuleEngine, not CameraApplicationPlatform, so the VMD rule below + // does not cover it. Still shipping on deployed cameras. + // https://developer.axis.com/vapix/network-video/event-and-action-services/ + {"RuleEngine/VMD3/", KindMotion}, + + // AXIS ACAP motion apps with CameraProfile suffixes. VMD 4 is + // the stock app shipped on the camera; the Guard suite are the + // paid analytics products. Prefix-match because of the suffix. + // https://developer.axis.com/vapix/applications/vmd4 // https://developer.axis.com/vapix/applications/motion-guard + {"CameraApplicationPlatform/VMD/", KindMotion}, {"CameraApplicationPlatform/MotionGuard/", KindMotion}, {"CameraApplicationPlatform/FenceGuard/", KindMotion}, {"CameraApplicationPlatform/LoiteringGuard/", KindMotion}, diff --git a/event/stream/topics_test.go b/event/stream/topics_test.go index 2bc2a34..71a06f4 100644 --- a/event/stream/topics_test.go +++ b/event/stream/topics_test.go @@ -25,6 +25,18 @@ func TestClassifyTopic(t *testing.T) { {"axis_fence_guard", "tnsaxis:CameraApplicationPlatform/FenceGuard/Camera1ProfileANY", KindMotion}, {"axis_loitering_guard", "tnsaxis:CameraApplicationPlatform/LoiteringGuard/Camera1ProfileANY", KindMotion}, + // AXIS VMD 4 — the stock motion app, and the one an installer + // reaches for before any Guard product. The profile suffix + // varies with the configured VMD profile. + {"axis_vmd4_profile_any", "tnsaxis:CameraApplicationPlatform/VMD/Camera1ProfileANY", KindMotion}, + {"axis_vmd4_profile_numbered", "tnsaxis:CameraApplicationPlatform/VMD/Camera1Profile1", KindMotion}, + + // AXIS VMD 3 — the firmware-builtin predecessor, published under + // RuleEngine rather than CameraApplicationPlatform. Still + // shipping on deployed cameras. + {"axis_vmd3_video_1", "tns1:RuleEngine/tnsaxis:VMD3/vmd3_video_1", KindMotion}, + {"axis_vmd3_video_2", "tns1:RuleEngine/tnsaxis:VMD3/vmd3_video_2", KindMotion}, + // --- Tampering -------------------------------------------------- {"tamper_detector", "tns1:RuleEngine/TamperDetector/Tamper", KindTampering}, @@ -88,6 +100,16 @@ func TestClassifyTopic(t *testing.T) { {"relay_failure_not_digital_output", "tns1:Device/HardwareFailure/RelayFailure", KindUnknown}, {"digital_input_config_not_digital_input", "tns1:Device/IO/DigitalInputConfiguration", KindUnknown}, {"tamper_detector_log_not_tampering", "tns1:Device/Diagnostics/TamperDetectorLog", KindUnknown}, + + // The two AXIS VMD needles carry a trailing slash so they match a + // whole path segment. Without it, any sibling app or rule whose + // name merely starts with VMD / VMD3 would classify as motion and + // drive recording. + {"vmd_statistics_app_not_motion", "tnsaxis:CameraApplicationPlatform/VMDStatistics/Camera1", KindUnknown}, + {"vmd3_config_rule_not_motion", "tns1:RuleEngine/tnsaxis:VMD3Config/Changed", KindUnknown}, + // VMD3 is scoped to RuleEngine; the same name under another + // container is a different thing. + {"vmd3_outside_rule_engine_not_motion", "tnsaxis:Storage/VMD3/Status", KindUnknown}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) {