Преминете към основното съдържание

Протокол на конектора на 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 /wsWebSocket.
POST /heartbeatHTTP резервен вариант на heartbeat.
GET /jobs/pending, POST /jobs/{id}/ack, …/progress, …/resultHTTP резервен вариант с периодично запитване за жизнения цикъл на задачите.
POST /alarmsHTTP резервен вариант на sphere.alarms: тялото е AlarmsChunk (приема се gzip; 4 MiB компресирано, 8 MiB декомпресирано), отговорът е AlarmsAck.
POST /statusHTTP резервен вариант на 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.state stopped).

Задачи​

ЗадачаДанниРезултатТаймаут / изтичане, приоритетЛента на конектора
sphere.nvr.apply{nvr: NVRRef, desired_state, credentials_version}{}2 мин / 7 дни, 10config (една по една)
sphere.nvr.remove{nvr_id}{}1 мин / 30 дниconfig
sphere.nvr.test{nvr: NVRRef, credentials_version}{info: NVRInfo, rtsp_reachable, warnings?}1 мин / 5 мин, 50probe (2 паралелно)
sphere.recordings.find{nvr_id, channel, from, to} (най-много 7 дни){segments: [{start, end, type, size_bytes?}], truncated?} (най-много 2000)1 мин / 2 мин, 50probe
sphere.nvr.tune_liveLiveTuneJob {nvr_id, channels?, keyframe_seconds}LiveTuneResult {channels: [ChannelTune]}5 мин / 5 мин, 40probe
sphere.stream.start{nvr_id, channel, profile, target: {url, path}}{}30 с / 30 с, 80stream (8 паралелно)
sphere.stream.stop{nvr_id, channel, profile}{}30 с / 2 мин, 60stream
sphere.playback.start{session_id, nvr_id, channel, start, end, target: {url, path}} (най-много 24 часа){}30 с / 30 с, 80stream
sphere.playback.stop{session_id}{}30 с / 2 мин, 60stream
sphere.ptz{nvr_id, channel, code, action, speed?, preset?}{}5 с / 5 с, 100ptz (4 паралелно)
sphere.snapshot{nvr_id, channel, upload_url}{}–snapshot (2 паралелно)
update_agentUpdateJob (по-долу)UpdateResult30 мин / 1 ч, 0update (една по една)
  • 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.
targetMSI за инсталиране: 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_unsignedupdate_agent: версията няма публичен ключ за изданията и отказва обновявания.
signature_invalidupdate_agent: подписът на издание не е проверен успешно.
download_failed, hash_mismatchupdate_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.
channel1–256; 0 или липсва за аларми, които не са за канал.
dataПодробностите от видеорекордера, JSON обект до 4 KiB.

Конекторът вдига NVROffline (импулс), когато свързан видеорекордер, който е бил онлайн, спре да отговаря или откаже входа, и NVROnline, когато се върне.

Точно веднъж: сървърът пази за всеки конектор най-високия пореден номер, който е съхранил (alarms_last_seq). Порция на или под него е дубликат и само се потвърждава; частта от порцията над него се съхранява и курсорът се премества, в една транзакция. Конекторът пази алармите на диска, докато не бъдат потвърдени, така че изгубено потвърждение, рестартиране или прекъсване води само до повторно изпращане. По WebSocket порция без потвърждение до 15 секунди се изпраща отново по HTTP.

При приемането сървърът определя камерата по видеорекордера и канала и предава новите аларми на уеб приложението (alarm.new). occurred_at повече от 24 часа в бъдещето или повече от година в миналото се заменя с времето на пристигане (часовникът на видеорекордера е грешен).

Публикуване на медия​

URLtarget.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.alarms500 (конекторът също държи порцията под 256 KiB)
data на аларма4 KiB
Видеорекордери и потоци в отчет за състояниепо 1000
Канали на видеорекордер256
Период за търсене на записи, сегменти7 дни, 2000
Период на възпроизвеждане24 часа
PTZ скорост, предварителни позиции1–8, 1–255
Съобщение по WebSocket1 MiB
Публикувани потоци на конектор64 (SPHERE_CONNECTOR_MAX_STREAMS)
Инсталатор на конектора (издание)64 MiB; пазят се най-новите десет