Skip to content
Open
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
8c05663
Added WIP version of Snaps&Events V2 update
aljazdu Sep 25, 2025
83543c5
Added FileData ImgDetections constructor
aljazdu Sep 26, 2025
c74b28c
Updated EventsManager bindings
aljazdu Sep 26, 2025
ba51384
Updated events manager bindings
aljazdu Sep 28, 2025
bd9471e
Updated FileData constructors
aljazdu Sep 29, 2025
46d164e
Fixed response logging, removed redundant logUploadResults
aljazdu Sep 29, 2025
3aa210a
Updated logging level
aljazdu Sep 29, 2025
903b41b
Updated ImgDetections mimeType, sourceAppId
aljazdu Sep 30, 2025
f3e290a
Added events validation rules
aljazdu Oct 1, 2025
3380fad
Batch preparation and upload of files is now async, added updated pro…
aljazdu Oct 5, 2025
30d403d
Updated the asynchronous groups and files upload, removed queueSize
aljazdu Oct 5, 2025
96f3e24
Updated fetching of configuration limits and preparing of group batch…
aljazdu Oct 7, 2025
f4bca4a
Updated uploadRetryPolicy
aljazdu Oct 7, 2025
c1133d4
Code cleanup
aljazdu Oct 8, 2025
6c6b5cf
Updated events python example
aljazdu Oct 8, 2025
77debeb
Updated events examples, events proto, pythong bindings, ImgDetection…
aljazdu Oct 9, 2025
8146673
Added missing clear for FileGroup class
aljazdu Oct 9, 2025
cb4e7bd
Reverted ImgDetections changes, updated event example
aljazdu Oct 9, 2025
38ed31c
Updated argument order of FileGroup add functions, fileName is now al…
aljazdu Oct 9, 2025
a13351e
Added addImageNNDataPair() to FileGroup
aljazdu Oct 10, 2025
9c293e7
Added sendSnap() overload for the most common use case
aljazdu Oct 10, 2025
3716c1f
Hub Url is now determined using an env setting
aljazdu Oct 10, 2025
007e44f
Merge branch 'develop' into feature/snaps_events_v2_integration
aljazdu Oct 10, 2025
940ca89
Fixed formatting
aljazdu Oct 10, 2025
94fd634
Updated handling of uploadFileBatch futures
aljazdu Oct 13, 2025
529b24c
Merge remote-tracking branch 'origin/develop' into feature/snaps_even…
Oct 13, 2025
0096d24
Minor cleanup
Oct 13, 2025
0cb59a8
Added caching of events
aljazdu Oct 13, 2025
15a8366
PR clean up and fixes
aljazdu Oct 14, 2025
622de45
Updated events examples
aljazdu Oct 14, 2025
f33d957
FileName is now optional when adding to fileGroups or sending snaps
aljazdu Oct 14, 2025
501c5fd
Updated caching of snaps and events
aljazdu Oct 15, 2025
d66489c
DEPTHAI_HUB_URL env variable rename, snap tag fix, minor fixes
aljazdu Oct 21, 2025
d99d0f6
Added missing token check when fetching configuration limits
aljazdu Oct 24, 2025
72bc656
Publish interval setting will no longer be exposed to the end user
aljazdu Nov 6, 2025
0e2aac1
Merge branch 'develop' into feature/snaps_events_v2_integration
aljazdu Nov 14, 2025
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ The following environment variables can be set to alter default behavior of the
| DEPTHAI_CRASHDUMP_TIMEOUT | Specifies the duration in milliseconds to wait for device reboot when obtaining a crash dump. Crash dump retrieval disabled if 0. |
| DEPTHAI_ENABLE_ANALYTICS_COLLECTION | Enables automatic analytics collection (pipeline schemas) used to improve the library |
| DEPTHAI_DISABLE_CRASHDUMP_COLLECTION | Disables automatic crash dump collection used to improve the library |
| DEPTHAI_HUB_URL | URL for the Luxonis Hub |
| DEPTHAI_HUB_API_KEY | API key for the Luxonis Hub |
| DEPTHAI_ZOO_INTERNET_CHECK | (Default) 1 - perform internet check, if available, download the newest model version 0 - skip internet check and use cached model |
| DEPTHAI_ZOO_INTERNET_CHECK_TIMEOUT | (Default) 1000 - timeout in milliseconds for the internet check |
Expand Down
113 changes: 87 additions & 26 deletions bindings/python/src/utility/EventsManagerBindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,53 +21,114 @@ void EventsManagerBindings::bind(pybind11::module& m, void* pCallstack) {

#ifdef DEPTHAI_ENABLE_EVENTS_MANAGER
using namespace dai::utility;
py::class_<EventData, std::shared_ptr<utility::EventData>>(m, "EventData")
.def(py::init<const std::string&, const std::string&, const std::string&>(), py::arg("data"), py::arg("fileName"), py::arg("mimeType"))
.def(py::init<std::string>(), py::arg("fileUrl"))
.def(py::init<const std::shared_ptr<ImgFrame>&, std::string>(), py::arg("imgFrame"), py::arg("fileName"))
.def(py::init<const std::shared_ptr<EncodedFrame>&, std::string>(), py::arg("encodedFrame"), py::arg("fileName"))
.def(py::init<const std::shared_ptr<NNData>&, std::string>(), py::arg("nnData"), py::arg("fileName"));
py::class_<FileGroup, std::shared_ptr<utility::FileGroup>>(m, "FileGroup")
.def(py::init<>())
.def("clearFiles", &FileGroup::clearFiles, DOC(dai, utility, FileGroup, clearFiles))
.def("addFile",
static_cast<void (FileGroup::*)(std::string, std::string, std::string)>(&FileGroup::addFile),
py::arg("fileName"),
py::arg("data"),
py::arg("mimeType"),
DOC(dai, utility, FileGroup, addFile))
.def("addFile",
static_cast<void (FileGroup::*)(std::string, std::string)>(&FileGroup::addFile),
py::arg("fileName"),
py::arg("filePath"),
DOC(dai, utility, FileGroup, addFile))
.def("addFile",
static_cast<void (FileGroup::*)(std::string, const std::shared_ptr<ImgFrame>&)>(&FileGroup::addFile),
py::arg("fileName"),
py::arg("imgFrame"),
DOC(dai, utility, FileGroup, addFile))
.def("addFile",
static_cast<void (FileGroup::*)(std::string, const std::shared_ptr<EncodedFrame>&)>(&FileGroup::addFile),
py::arg("fileName"),
py::arg("encodedFrame"),
DOC(dai, utility, FileGroup, addFile))
.def("addFile",
static_cast<void (FileGroup::*)(std::string, const std::shared_ptr<NNData>&)>(&FileGroup::addFile),
py::arg("fileName"),
py::arg("nnData"),
DOC(dai, utility, FileGroup, addFile))
.def("addFile",
static_cast<void (FileGroup::*)(std::string, const std::shared_ptr<ImgDetections>&)>(&FileGroup::addFile),
py::arg("fileName"),
py::arg("imgDetections"),
DOC(dai, utility, FileGroup, addFile))
.def("addImageDetectionsPair",
static_cast<void (FileGroup::*)(std::string, const std::shared_ptr<ImgFrame>&, const std::shared_ptr<ImgDetections>&)>(
&FileGroup::addImageDetectionsPair),
py::arg("fileName"),
py::arg("imgFrame"),
py::arg("imgDetections"),
DOC(dai, utility, FileGroup, addImageDetectionsPair))
.def("addImageDetectionsPair",
static_cast<void (FileGroup::*)(std::string, const std::shared_ptr<EncodedFrame>&, const std::shared_ptr<ImgDetections>&)>(
&FileGroup::addImageDetectionsPair),
py::arg("fileName"),
py::arg("encodedFrame"),
py::arg("imgDetections"),
DOC(dai, utility, FileGroup, addImageDetectionsPair))
.def("addImageNNDataPair",
static_cast<void (FileGroup::*)(std::string, const std::shared_ptr<ImgFrame>&, const std::shared_ptr<NNData>&)>(&FileGroup::addImageNNDataPair),
py::arg("fileName"),
py::arg("imgFrame"),
py::arg("nnData"),
DOC(dai, utility, FileGroup, addImageNNDataPair))
.def(
"addImageNNDataPair",
static_cast<void (FileGroup::*)(std::string, const std::shared_ptr<EncodedFrame>&, const std::shared_ptr<NNData>&)>(&FileGroup::addImageNNDataPair),
py::arg("fileName"),
py::arg("encodedFrame"),
py::arg("nnData"),
DOC(dai, utility, FileGroup, addImageNNDataPair));

py::class_<EventsManager>(m, "EventsManager")
.def(py::init<>())
.def(py::init<std::string, bool, float>(), py::arg("url"), py::arg("uploadCachedOnStart") = false, py::arg("publishInterval") = 10.0)
.def("setUrl", &EventsManager::setUrl, py::arg("url"), DOC(dai, utility, EventsManager, setUrl))
.def("setSourceAppId", &EventsManager::setSourceAppId, py::arg("sourceAppId"), DOC(dai, utility, EventsManager, setSourceAppId))
.def("setSourceAppIdentifier",
&EventsManager::setSourceAppIdentifier,
py::arg("sourceAppIdentifier"),
DOC(dai, utility, EventsManager, setSourceAppIdentifier))
.def(py::init<bool, float>(), py::arg("uploadCachedOnStart") = false, py::arg("publishInterval") = 10.0)
.def("setToken", &EventsManager::setToken, py::arg("token"), DOC(dai, utility, EventsManager, setToken))
.def("setQueueSize", &EventsManager::setQueueSize, py::arg("queueSize"), DOC(dai, utility, EventsManager, setQueueSize))
.def("setLogResponse", &EventsManager::setLogResponse, py::arg("logResponse"), DOC(dai, utility, EventsManager, setLogResponse))
.def("setDeviceSerialNumber",
&EventsManager::setDeviceSerialNumber,
py::arg("deviceSerialNumber"),
DOC(dai, utility, EventsManager, setDeviceSerialNumber))
.def("setVerifySsl", &EventsManager::setVerifySsl, py::arg("verifySsl"), DOC(dai, utility, EventsManager, setVerifySsl))
.def("setCacheDir", &EventsManager::setCacheDir, py::arg("cacheDir"), DOC(dai, utility, EventsManager, setCacheDir))
.def("setCacheIfCannotSend",
&EventsManager::setCacheIfCannotSend,
py::arg("cacheIfCannotUpload"),
DOC(dai, utility, EventsManager, setCacheIfCannotSend))
.def("checkConnection", &EventsManager::checkConnection, DOC(dai, utility, EventsManager, checkConnection))
.def("uploadCachedData", &EventsManager::uploadCachedData, DOC(dai, utility, EventsManager, uploadCachedData))
.def("sendEvent",
&EventsManager::sendEvent,
py::arg("name"),
py::arg("imgFrame").none(true) = nullptr,
py::arg("data") = std::vector<std::shared_ptr<EventData>>(),
py::arg("tags") = std::vector<std::string>(),
py::arg("extraData") = std::unordered_map<std::string, std::string>(),
py::arg("extras") = std::unordered_map<std::string, std::string>(),
py::arg("deviceSerialNo") = "",
py::arg("associateFiles") = std::vector<std::string>(),
DOC(dai, utility, EventsManager, sendEvent))
.def("sendSnap",
&EventsManager::sendSnap,
static_cast<bool (EventsManager::*)(const std::string&,
const std::shared_ptr<FileGroup>,
const std::vector<std::string>&,
const std::unordered_map<std::string, std::string>&,
const std::string&)>(&EventsManager::sendSnap),
py::arg("name"),
py::arg("fileGroup") = std::shared_ptr<FileGroup>(),
py::arg("tags") = std::vector<std::string>(),
py::arg("extras") = std::unordered_map<std::string, std::string>(),
py::arg("deviceSerialNo") = "",
DOC(dai, utility, EventsManager, sendSnap))
.def("sendSnap",
static_cast<bool (EventsManager::*)(const std::string&,
const std::string&,
const std::shared_ptr<ImgFrame>,
const std::shared_ptr<ImgDetections>,
const std::vector<std::string>&,
const std::unordered_map<std::string, std::string>&,
const std::string&)>(&EventsManager::sendSnap),
py::arg("name"),
py::arg("imgFrame").none(true) = nullptr,
py::arg("data") = std::vector<std::shared_ptr<EventData>>(),
py::arg("fileName"),
py::arg("imgFrame"),
py::arg("imgDetections"),
py::arg("tags") = std::vector<std::string>(),
py::arg("extraData") = std::unordered_map<std::string, std::string>(),
py::arg("extras") = std::unordered_map<std::string, std::string>(),
py::arg("deviceSerialNo") = "",
DOC(dai, utility, EventsManager, sendSnap));
#endif
Expand Down
100 changes: 78 additions & 22 deletions examples/cpp/Events/events.cpp
Original file line number Diff line number Diff line change
@@ -1,44 +1,100 @@
#include <chrono>
#include <iostream>
#include <opencv2/opencv.hpp>
#include <string>

#include "depthai/depthai.hpp"
#include "depthai/utility/EventsManager.hpp"

int main(int argc, char* argv[]) {
// Helper function to normalize frame coordinates
cv::Rect frameNorm(const cv::Mat& frame, const dai::Point2f& topLeft, const dai::Point2f& bottomRight) {
float width = frame.cols, height = frame.rows;
return cv::Rect(cv::Point(topLeft.x * width, topLeft.y * height), cv::Point(bottomRight.x * width, bottomRight.y * height));
}

int main() {
dai::Pipeline pipeline(true);

// Enter you hub team's api-key
auto eventsManager = std::make_shared<dai::utility::EventsManager>();
eventsManager->setLogResponse(true);
// Color camera node
eventsManager->setToken("");
eventsManager->setLogResponse(false);
auto fileGroup = std::make_shared<dai::utility::FileGroup>();

auto camRgb = pipeline.create<dai::node::Camera>()->build();
auto* preview = camRgb->requestOutput(std::make_pair(256, 256));
auto detectionNetwork = pipeline.create<dai::node::DetectionNetwork>();

auto previewQ = preview->createOutputQueue();
dai::NNModelDescription modelDescription;
modelDescription.model = "yolov6-nano";
detectionNetwork->build(camRgb, modelDescription);
auto labelMap = detectionNetwork->getClasses();

pipeline.start();
bool sent = false;
eventsManager->sendEvent("test", nullptr, {}, {"tag1", "tag2"}, {{"key1", "value1"}});
// Create output queues
auto qRgb = detectionNetwork->passthrough.createOutputQueue();
auto qDet = detectionNetwork->out.createOutputQueue();

std::this_thread::sleep_for(std::chrono::milliseconds(7000));
pipeline.start();

auto fileData = std::make_shared<dai::utility::EventData>("abc", "test_bin.txt", "text/plain");
std::vector<std::shared_ptr<dai::utility::EventData>> data;
data.emplace_back(fileData);
eventsManager->sendEvent("testdata", nullptr, data, {"tag3", "tag4"}, {{"key8", "value8"}});
int counter = 0;
while(pipeline.isRunning()) {
auto rgb = previewQ->get<dai::ImgFrame>();
auto inRgb = qRgb->get<dai::ImgFrame>();
auto inDet = qDet->get<dai::ImgDetections>();
if(inRgb == nullptr || inDet == nullptr) {
continue;
}

// Display the video stream and detections
cv::Mat frame = inRgb->getCvFrame();
if(!frame.empty()) {
// Display detections
for(const auto& detection : inDet->detections) {
auto bbox = frameNorm(frame, dai::Point2f(detection.xmin, detection.ymin), dai::Point2f(detection.xmax, detection.ymax));

// Draw label
cv::putText(
frame, labelMap.value()[detection.label], cv::Point(bbox.x + 10, bbox.y + 20), cv::FONT_HERSHEY_TRIPLEX, 0.5, cv::Scalar(255, 255, 255));

// Draw confidence
cv::putText(frame,
std::to_string(static_cast<int>(detection.confidence * 100)) + "%",
cv::Point(bbox.x + 10, bbox.y + 40),
cv::FONT_HERSHEY_TRIPLEX,
0.5,
cv::Scalar(255, 255, 255));

// Do something with the data
// ...
// Draw rectangle
cv::rectangle(frame, bbox, cv::Scalar(255, 0, 0), 2);
}

if(!sent) {
eventsManager->sendSnap("rgb", rgb, {}, {"tag11", "tag12"}, {{"key", "value"}});
sent = true;
// Show the frame
cv::imshow("rgb", frame);
}

// Suppose we are only interested in the detections with confidence between 50% and 60%
auto borderDetections = std::make_shared<dai::ImgDetections>();
for(const auto& detection : inDet->detections) {
if(detection.confidence > 0.5f && detection.confidence < 0.6f) {
borderDetections->detections.emplace_back(detection);
}
}

// Are there any border detections
if(borderDetections->detections.size() > 0) {
std::string fileName = "ImageDetection_";
std::stringstream ss;
ss << fileName << counter;

fileGroup->clearFiles();
fileGroup->addImageDetectionsPair(ss.str(), inRgb, borderDetections);
eventsManager->sendSnap("ImageDetection", fileGroup, {"EventsExample", "C++"}, {{"key_0", "value_0"}, {"key_1", "value_1"}}, "");

counter++;
}

if(cv::waitKey(1) == 'q') {
break;
}
//
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}

return EXIT_SUCCESS;
}
}
85 changes: 66 additions & 19 deletions examples/python/Events/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,37 +8,84 @@

# Create pipeline
with dai.Pipeline() as pipeline:
# Define sources and outputs
camRgb = pipeline.create(dai.node.Camera).build()
# Properties
# Enter you hub team's api-key
eventMan = dai.EventsManager()
eventMan.setToken("")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needed?

Copy link
Contributor Author

@aljazdu aljazdu Oct 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left it here to "alert" the user that the token must be set - specifically in the peripheral mode. I can remove it, if this doesn't make sense

eventMan.setLogResponse(False)
fileGroup = dai.FileGroup()

qRgb = camRgb.requestOutput((256,256)).createOutputQueue()
cameraNode = pipeline.create(dai.node.Camera).build()
detectionNetwork = pipeline.create(dai.node.DetectionNetwork).build(cameraNode, dai.NNModelDescription("yolov6-nano"))
labelMap = detectionNetwork.getClasses()

eventMan = dai.EventsManager()
eventMan.setLogResponse(True)
# Create output queues
qRgb = detectionNetwork.passthrough.createOutputQueue()
qDet = detectionNetwork.out.createOutputQueue()

eventMan.sendEvent("test1", None, [], ["tag1", "tag2"], {"key1": "value1"})
time.sleep(2)
fileData = dai.EventData(b'Hello, world!', "hello.txt", "text/plain")
eventMan.sendEvent("test2", None, [fileData], ["tag1", "tag2"], {"key1": "value1"})
pipeline.start()

frame = None
counter = 0

# nn data, being the bounding box locations, are in <0..1> range - they need to be normalized with frame width/height
def frameNorm(frame, bbox):
normVals = np.full(len(bbox), frame.shape[0])
normVals[::2] = frame.shape[1]
return (np.clip(np.array(bbox), 0, 1) * normVals).astype(int)


eventSent = False
counter = 0
while pipeline.isRunning():
inRgb: dai.ImgFrame = qRgb.get()
if inRgb is not None:
frame = inRgb.getCvFrame()
if not eventSent:
eventMan.sendSnap("rgb", inRgb, [], ["tag1", "tag2"], {"key1": "value1"})
eventSent = True
inDet: dai.ImgDetections = qDet.get()
if inRgb is None or inDet is None:
continue

# Display the video stream and detections
color = (255, 0, 0)
frame = inRgb.getCvFrame()
if frame is not None:
for detection in inDet.detections:
bbox = frameNorm(
frame,
(detection.xmin, detection.ymin, detection.xmax, detection.ymax),
)
cv2.putText(
frame,
labelMap[detection.label],
(bbox[0] + 10, bbox[1] + 20),
cv2.FONT_HERSHEY_TRIPLEX,
0.5,
255,
)
cv2.putText(
frame,
f"{int(detection.confidence * 100)}%",
(bbox[0] + 10, bbox[1] + 40),
cv2.FONT_HERSHEY_TRIPLEX,
0.5,
255,
)
cv2.rectangle(frame, (bbox[0], bbox[1]), (bbox[2], bbox[3]), color, 2)
# Show the frame
cv2.imshow("rgb", frame)

# Suppose we are only interested in the detections with confidence between 50% and 60%
borderDetectionsList = []
for detection in inDet.detections:
if detection.confidence > 0.5 and detection.confidence < 0.6:
borderDetectionsList.append(detection)

# Are there any border detections
if len(borderDetectionsList) > 0:
borderDetections = dai.ImgDetections()
borderDetections.detections = borderDetectionsList
fileName = f"ImageDetection_{counter}"

fileGroup.clearFiles();
fileGroup.addImageDetectionsPair(fileName, inRgb, borderDetections)
eventMan.sendSnap("ImageDetection", fileGroup, ["EventsExample", "Python"], {"key_0" : "value_0", "key_1" : "value_1", "key_2" : "value_2"}, "")

counter += 1

if cv2.waitKey(1) == ord("q"):
pipeline.stop()
break
break
Loading
Loading