Протокол на конектора на Sphere
Конекторът на Sphere използва същия плик (envelope), hello, сигнал за
активност и жизнен цикъл на задачите като агентите и конекторите на Axis
(Протокол на агента и конектора) и добавя съобщенията и
задачите за видеонаблюдение. Go типовете в
entrosity-shared-go/proto/sphere са единственият източник на истина;
тази страница описва предназначението и поведението.
Както и останалата част от протокола, тези типове само растат: полета се добавят, никога не се преименуват и не получават ново предназначение, така че конекторите на терен продължават да работят.
Транспорт
- WebSocket:
wss://hub.entrosity.com/sphere/api/connector/v1/wsсAuthorization: Bearer <connector key>. JSON текстови рамки, по един плик в рамка, най-много 1 MiB; сървърът изпраща ping на всеки 30 секунди. - Първото съобщение трябва да е
hello(capabilities: ["video"], плюс"update"от версиите, които инсталират подписани издания, иpending_job_ids); сървърът отговаря сhello.ack, доставя чакащите задачи и може да предложи обновяване (Самообновяване). Всичко предиhelloзатваря сокета (1008). - Конекторът изпраща
heartbeatвсяка минута. Той е онлайн от свояhello, докато сокетът не се затвори, или до 3 минути без съобщение. Минаването офлайн прави видеорекордерите муunknown, а потоците му – не на живо. - Задачите следват
job.assign→job.ack→job.progress→job.result. Задача, изпратена безjob.ackдо 60 секунди, се изпраща отново; задача, незавършена преди изтичането си, ставаtimeout. - Кодове за затваряне:
4003ключът е отменен (конекторът е премахнат, тенантът е спрян);4009по-нова връзка от същата инсталация е поела мястото ѝ;1008нарушение на правилата. - Видеото не минава по тази връзка: то се публикува на медийния сървър на
отделен хост,
media.entrosity.com:8322(Публикуване на медия).
HTTP крайни точки (/api/connector/v1)
| Метод и път | Предназначение |
|---|---|
POST /enroll | {enrollment_token, hostname, domain, version} → {connector_id, connector_key, ws_url}. Токенът трябва да е токен за конектор на Sphere на активен тенант. Същият компютър (тенант, име на хост, домейн), регистриран отново, сменя ключа си и затваря старата връзка. Ограничение за IP (SPHERE_ENROLL_RATE_PER_MINUTE). |
GET /ws | WebSocket. |
POST /heartbeat | HTTP резервен вариант на heartbeat. |
GET /jobs/pending, POST /jobs/{id}/ack, …/progress, …/result | HTTP резервен вариант с периодично запитване за жизнения цикъл на задачите. |
POST /alarms | HTTP резервен вариант на sphere.alarms: тялото е AlarmsChunk (приема се gzip; 4 MiB компресирано, 8 MiB декомпресирано), отговорът е AlarmsAck. |
POST /status | HTTP резервен вариант на sphere.status. |
GET /nvrs/{id}/credentials | {username, password, version} на един от собствените видеорекордери на конектора (Cache-Control: no-store). Всяко взимане се записва в одитния журнал на тенанта като nvr.credentials_fetch. |
Всички освен /enroll изискват ключа на конектора. Всичко, което прави
конекторът, е ограничено до неговия тенант и до собствените му видеорекордери.
Съобщения
Конектор → сървър
| Тип | Данни | Кога |
|---|---|---|
sphere.alarms | {first_seq, alarms: [Alarm]}: 1–500 последователни аларми (alarms[i].seq = first_seq + i) | Винаги когато опашката на конектора има аларми. |
sphere.status | {nvrs: [NVRStatus], streams: [StreamState]} (до 1000 от всеки вид) | Поне на всеки 30 секунди и веднага след промяна. |
NVRStatus е {nvr_id, status, error?, credentials_version?, info?}:
status е online, offline, auth_failed, unsupported или
disconnected; credentials_version е версията, с която влиза
конекторът; info (NVRInfo) се изпраща след (повторно) свързване и
когато се е променил.
StreamState е {nvr_id, channel, profile?, session_id?, path, since, publishing, error?}: един поток, който конекторът публикува или опитва
отново (publishing: false с причината в error).
Сървър → конектор
| Тип | Данни | Кога |
|---|---|---|
sphere.alarms.ack | {acked_seq}, в отговор (reply_to) на sphere.alarms | Всяка аларма до acked_seq е съхранена; конекторът ги премахва от опашката си. |
Какво прави сървърът с отчета за състоянието
- Записва състоянието и грешката на всеки видеорекордер, приложената версия на
идентификационните данни и, с
info, данните и камерите на видеорекордера (новите канали стават камери, каналите над отчетения брой се премахват; часовата зона на видеорекордера се взима само ако в Sphere няма зададена). - Видеорекордер, който конекторът отчита, но тенантът няма (или вече няма), се
премахва от конектора със
sphere.nvr.remove. - Поток, който конекторът публикува, но никой не иска, или който е спрян
(изгубена задача за спиране), се спира отново (
sphere.stream.stopилиsphere.playback.stop). Желан поток на живо, който не е отчитал 45 секунди, се стартира отново, дори ако е бил на живо (например конекторът е рестартиран); възпроизвеждане, което вече не отчита, е стигнало края си и приключва (stream.statestopped).
Задачи
| Задача | Данни | Резултат | Таймаут / изтичане, приоритет | Лента на конектора |
|---|---|---|---|---|
sphere.nvr.apply | {nvr: NVRRef, desired_state, credentials_version} | {} | 2 мин / 7 дни, 10 | config (една по една) |
sphere.nvr.remove | {nvr_id} | {} | 1 мин / 30 дни | config |
sphere.nvr.test | {nvr: NVRRef, credentials_version} | {info: NVRInfo, rtsp_reachable, warnings?} | 1 мин / 5 мин, 50 | probe (2 паралелно) |
sphere.recordings.find | {nvr_id, channel, from, to} (най-много 7 дни) | {segments: [{start, end, type, size_bytes?}], truncated?} (най-много 2000) | 1 мин / 2 мин, 50 | probe |
sphere.nvr.tune_live | LiveTuneJob {nvr_id, channels?, keyframe_seconds} | LiveTuneResult {channels: [ChannelTune]} | 5 мин / 5 мин, 40 | probe |
sphere.stream.start | {nvr_id, channel, profile, target: {url, path}} | {} | 30 с / 30 с, 80 | stream (8 паралелно) |
sphere.stream.stop | {nvr_id, channel, profile} | {} | 30 с / 2 мин, 60 | stream |
sphere.playback.start | {session_id, nvr_id, channel, start, end, target: {url, path}} (най-много 24 часа) | {} | 30 с / 30 с, 80 | stream |
sphere.playback.stop | {session_id} | {} | 30 с / 2 мин, 60 | stream |
sphere.ptz | {nvr_id, channel, code, action, speed?, preset?} | {} | 5 с / 5 с, 100 | ptz (4 паралелно) |
sphere.snapshot | {nvr_id, channel, upload_url} | {} | – | snapshot (2 паралелно) |
update_agent | UpdateJob (по-долу) | UpdateResult | 30 мин / 1 ч, 0 | update (една по една) |
NVRRefе{nvr_id, driver, host, port, https?, rtsp_port, timezone?}: LAN адресът на видеорекордера, HTTP(S) портът на API му, дали използва HTTPS (самоподписаният му сертификат се приема), RTSP портът му и IANA часовата зона, в която върви часовникът му (празно: местната зона на конектора). Никога не съдържа идентификационни данни.NVRInfoе{device_type, serial, firmware, channels, cameras: [{channel, title, ptz?, main_codec?, sub_codec?, online}], timezone?}. Каналите се броят от 1; кодеците саH.264,H.265,MJPEGили празно.sphere.nvr.applyкара конектора да държи видеорекордера вdesired_state:connected(влязъл, следи потока от събития, проверява го на всеки 30 секунди, описва го отново на всеки 5 минути) илиdisconnected(излязъл, всеки поток на видеорекордера е спрян). Когатоcredentials_versionе по-нова от идентификационните данни, които има, конекторът първо ги взима (GET /nvrs/{id}/credentials). Повторното прилагане на същото състояние не променя нищо. Нова задача за прилагане за видеорекордер отменя по-старата му отворена задача. Сървърът я изпраща, когато видеорекордер бъде добавен, свързан или несвързан и когато адресът, часовата зона или идентификационните данни на свързан видеорекордер се променят;nvr.reconcileя изпраща отново, когато конекторът не държи желаното състояние.sphere.nvr.removeизлиза, спира потоците на видеорекордера и забравя видеорекордера и идентификационните му данни; непознат видеорекордер вече е премахнат.sphere.nvr.testвлиза веднъж, независимо от желаното състояние, описва видеорекордера и възпроизвежда подпотока на канал 1, за да зададеrtsp_reachable.warningsпосочват канали H.265 и MJPEG, липса на канали и неизвестна часова зона.sphere.stream.startвзима основния поток (main) или подпотока (sub) на канала от видеорекордера и го публикува наtarget.url+/+target.pathдоsphere.stream.stop; задачата е успешна, щом медийният сървър приеме потока (до 8 секунди). Стартиране на поток, който вече се публикува към същата цел, е успешно веднага, без второ копие. Сървърът отбелязва потока катоliveведнага щом задачата успее, без да чака следващия отчет за състоянието.sphere.nvr.tune_liveкара подпотока на всяка камера да изпраща ключов кадър поне на всекиkeyframe_seconds(1–4): интервалът между ключовите му кадри става кадрова честота ×keyframe_secondsкадъра.channelsсе броят от 1; празно означава всички канали. Интервал, който вече е толкова кратък, не се променя, а основните потоци, които видеорекордерът записва, никога не се променят. ДрайверътdahuaзадаваEncode[i].ExtraFormat[0].Video.GOPсconfigManager.cgi?action=setConfigи след това прочита отново таблицатаEncode. После конекторът проверява всеки канал в подпотока му (RTSP, по четири наведнъж, до 15 с всеки): чете, докато пристигнат три ключови кадъра, и взема разликата между втория и третия от RTP времената им (видеорекордерът започва сесията със собствен ключов кадър). Камерите прилагат предадена от видеорекордера стойност едва след няколко минути, а някои запазват своята (тя се връща към видеорекордера), затова канал, чийто поток запазва по-дълъг интервал, се отчита сchanged: falseиerror: още не е приложено, когато е зададен в това изпълнение (gop_before > gop_after), иначе – запазен от камерата. ВсекиChannelTuneе{channel, fps?, gop_before?, gop_after?, changed, error?, measured_ms?}(интервалите са в кадри;measured_ms: интервалът, измерен в потока, липсва, когато не е могъл да бъде прочетен;changed: falseбезerror: вече е достатъчно кратък). Неуспехът за един канал се съобщава в неговияerror; задачата се проваля само когато видеорекордерът не може да бъде достигнат или настройките му – прочетени. Симулаторът също я изпълнява (подпотоци с 25 кадъра в секунда, интервал 50 до оптимизирането).sphere.playback.startпубликува записа на канала отstart(най-късно доend) към целта; то свършва само заедно със записа. Времената са абсолютни; конекторът ги преобразува с часовата зона на видеорекордера.sphere.ptz:codeе един отUp,Down,Left,Right,LeftUp,RightUp,LeftDown,RightDown,ZoomTele,ZoomWide,FocusNear,FocusFar,IrisLarge,IrisSmall,GotoPreset,SetPreset,ClearPreset;actionеstartилиstop;speedе 0–8 (0: стойността по подразбиране на драйвера);presetе 1–255 за кодовете за предварителни позиции. Изтичането след 5 секунди означава, че камера никога не се движи дълго след заявката.sphere.snapshotправи JPEG на канала и го изпраща сPUTкъмupload_url(предварително подписан адрес на обект; изображенията никога не минават по WebSocket). Конекторът я изпълнява; сървърът още не я изпраща.
Самообновяване (update_agent)
Същият тип задача и същите данни като при агентите и конекторите на Axis
(Протокол), с
component: sphere-connector (proto.UpdateJob, proto.UpdateResult в
entrosity-shared-go/proto). Изпраща се само на конектори, които обявяват
възможността update (sphere.CapabilityUpdate).
{"release_id": "…", "component": "sphere-connector",
"target": {"version": "0.2.0", "url": "https://hub.entrosity.com/sphere/api/releases/v1/connector/…/sphere-connector-0.2.0.msi?t=…",
"sha256": "…", "size_bytes": 9437184, "signature": "…"},
"rollback": {"version": "0.1.4", "url": "…", "sha256": "…", "size_bytes": 9412608, "signature": "…"}}
| Поле | Значение |
|---|---|
release_id | Идентификаторът на предложеното издание в Sphere. |
target | MSI за инсталиране: version, url (връзка за изтегляне, валидна 2 часа, без вход), sha256, size_bytes и signature – подписът Ed25519 (base64) на proto.ReleaseManifest ("rmm-release-v1\n<component>\n<version>\n<sha256>\n<size>\n") с SPHERE_RELEASE_SIGNING_KEY. |
rollback | Същото за версията, с която работи конекторът, ако Sphere още я пази: наблюдението я инсталира отново, ако новата версия не стартира. |
JobResult.Result е UpdateResult: {scheduled, from_version, to_version, rollback} (scheduled: инсталаторът се изпълнява след минута
от планирана задача; rollback: пази се предишен MSI за наблюдението).
Задачата завършва успешно, щом инсталирането е планирано; новата версия
се вижда в следващия hello на конектора. Конекторът няма събитие за
резултата: записва го в журнала при следващото си стартиране.
Sphere предлага обновяване, когато конектор изпрати hello, и на всеки 5
минути (releases.rollout): най-новото издание от
SPHERE_CONNECTOR_UPDATE_CHANNEL (stable: стабилните издания; dev:
dev и стабилните), което е по-ново от версията на конектора, не докато
има отворена задача update_agent на конектора, и същата версия не
отново в рамките на час. Какво прави конекторът:
Конектор на Sphere → Самообновяване.
Кодове на грешки при задачи
job.result.error_code на задачите на Sphere (sphere.ErrCode*, плюс
общите invalid_payload и exec_failed):
| Код | Значение |
|---|---|
nvr_unreachable | Видеорекордерът не отговори, конекторът го държи несвързан или видеорекордерът или медийният сървър не стартираха потока навреме. |
nvr_auth_failed | Видеорекордерът отказа идентификационните данни или конекторът още няма такива. |
channel_invalid | Видеорекордерът няма такъв канал. |
stream_limit | Конекторът вече публикува толкова потоци, колкото е позволено (SPHERE_CONNECTOR_MAX_STREAMS). |
codec_unsupported | Потокът няма видео, което препредаването може да пропусне (напр. MJPEG). |
media_publish_failed | Медийният сървър отказа или прекъсна потока. |
no_recordings | Запазен: няма съвпадащ запис (търсене без записи засега е успешно с празен списък сегменти). |
unsupported | Драйверът или видеорекордерът не може да направи това (напр. PTZ на фиксирана камера). |
unknown_driver | Тази версия на конектора няма драйвер с това име (simulator без --dev). |
unknown_nvr | Конекторът не управлява този видеорекордер. |
invalid_payload | Данните не могат да бъдат декодирани или са невалидни. |
exec_failed | Конекторът е рестартиран, преди задачата да приключи, или друга грешка; при update_agent папката за обновяване или планираната задача не можа да бъде създадена. |
update_unsigned | update_agent: версията няма публичен ключ за изданията и отказва обновявания. |
signature_invalid | update_agent: подписът на издание не е проверен успешно. |
download_failed, hash_mismatch | update_agent: MSI не можа да бъде изтеглен или не съвпада с SHA-256 и размера на изданието. |
Неуспешно или изтекло стартиране на поток или възпроизвеждане маркира
потока като failed за зрителите му (stream.state).
Идентификационни данни
Паролите на видеорекордерите никога не се появяват в данните на задачите,
резултатите, отчетите за състояние, API на портала, събитията или одитния
журнал. Задачите носят само credentials_version; когато тя е по-нова от
това, което има конекторът, той взима {username, password, version} от
GET /api/connector/v1/nvrs/{id}/credentials със собствения си ключ.
Сървърът отговаря само за собствените видеорекордери на конектора, декриптира
паролата (SPHERE_CREDENTIALS_KEY) и записва взимането като
nvr.credentials_fetch. Конекторът пази идентификационните данни в
nvrs.json, защитен с DPAPI на ниво машина, и отчита версията, с която
влиза (credentials_version в NVRStatus).
Аларми
{"seq": 88, "nvr_id": "…", "occurred_at": "2026-09-28T06:00:12Z",
"code": "VideoMotion", "action": "start", "channel": 3,
"data": {"Id": [0], "RegionName": ["Region1"]}}
| Поле | Значение |
|---|---|
seq | Задава се от опашката на конектора: строго нарастващ за всяка инсталация на конектор. |
nvr_id, occurred_at, code, action | Задължителни. code е кодът на събитието в видеорекордера (VideoMotion, VideoLoss, AlarmLocal, CrossLineDetection, …, до 64 знака) или собствените кодове на конектора NVROnline / NVROffline; action е start, stop или pulse. |
channel | 1–256; 0 или липсва за аларми, които не са за канал. |
data | Подробностите от видеорекордера, JSON обект до 4 KiB. |
Конекторът вдига NVROffline (импулс), когато свързан видеорекордер, който е
бил онлайн, спре да отговаря или откаже входа, и NVROnline, когато се
върне.
Точно веднъж: сървърът пази за всеки конектор най-високия пореден
номер, който е съхранил (alarms_last_seq). Порция на или под него е
дубликат и само се потвърждава; частта от порцията над него се съхранява и
курсорът се премества, в една транзакция. Конекторът пази алармите на
диска, докато не бъдат потвърдени, така че изгубено потвърждение,
рестартиране или прекъсване води само до повторно изпращане. По
WebSocket порция без потвърждение до 15 секунди се изпраща отново по
HTTP.
При приемането сървърът определя камерата по видеорекордера и канала и
предава новите аларми на уеб приложението (alarm.new). occurred_at
повече от 24 часа в бъдещето или повече от година в миналото се заменя с
времето на пристигане (часовникът на видеорекордера е грешен).
Публикуване на медия
| URL | target.url + / + target.path: rtsps://media.entrosity.com:8322/t/<tenant>/live/<camera>/<main|sub> или …/t/<tenant>/pb/<session> |
| Потребител, парола | ID на конектора и ключът на конектора (RTSP basic удостоверяване) |
| Транспорт | RTSP ANNOUNCE/RECORD, преплетен по TCP. Конекторът отваря тази връзка (TCP, TLS, OPTIONS), докато видеорекордерът отговаря на неговите DESCRIBE и SETUP, а не след това, така че щом потокът е известен, остават само заявките за публикуване. При rtsps конекторът сам отваря TLS (сертификатът на сървъра се проверява спрямо системните коренни сертификати) и говори чист RTSP в него, без SRTP: Caddy терминира TLS на :8322 със сертификата на медийния хост (конекторът изпраща media.entrosity.com като SNI) и препраща чист RTSP към медийния сървър. rtsp:// се приема за разработка. |
| Медия | RTP се предава непроменен, никога не се прекодира: видео H.264 и H.265, звук G.711 (друг звук се пропуска). |
| Източник | RTSP на видеорекордера, по TCP: rtsp://<host>:<rtsp_port>/cam/realmonitor?channel=N&subtype=0|1 (основен поток, подпоток) и /cam/playback?channel=N&starttime=…&endtime=… за Dahua. |
| Повторно свързване | Поток на живо се свързва отново, когато видеорекордерът не изпраща нищо 10 секунди или някоя от страните прекъсне, с изчакване от 1 до 30 секунди, докато не бъде спрян. |
Медийният сървър пита бекенда за всяко публикуване: потребителят трябва
да е конекторът, чийто ключ е паролата, а пътят трябва да започва с
t/<тенантът на конектора>/. Конекторът никога не може да публикува в
друг тенант (Медийният сървър).
Ограничения
| Ограничение | Стойност |
|---|---|
Аларми в порция sphere.alarms | 500 (конекторът също държи порцията под 256 KiB) |
data на аларма | 4 KiB |
| Видеорекордери и потоци в отчет за състояние | по 1000 |
| Канали на видеорекордер | 256 |
| Период за търсене на записи, сегменти | 7 дни, 2000 |
| Период на възпроизвеждане | 24 часа |
| PTZ скорост, предварителни позиции | 1–8, 1–255 |
| Съобщение по WebSocket | 1 MiB |
| Публикувани потоци на конектор | 64 (SPHERE_CONNECTOR_MAX_STREAMS) |
| Инсталатор на конектора (издание) | 64 MiB; пазят се най-новите десет |