@camera.ui/server 2.1.8 → 2.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (350) hide show
  1. package/dist/interface/assets/{404-vRjJVgl4.js → 404-BEb5gvkW.js} +1 -1
  2. package/dist/interface/assets/{About-CO9cx5Gp.js → About-BKkjuIVJ.js} +1 -1
  3. package/dist/interface/assets/Automation-DA89AEEI.js +3 -0
  4. package/dist/interface/assets/{AutomationImport-DAeFd-SZ.js → AutomationImport-hYr3Lp8F.js} +2 -2
  5. package/dist/interface/assets/{AutomationStore-NyXuw3tI.js → AutomationStore-DKhnExZn.js} +2 -2
  6. package/dist/interface/assets/{AutomationStoreDetail-Dz9Y8j8g.js → AutomationStoreDetail-DmqqEOir.js} +2 -2
  7. package/dist/interface/assets/{Automations-BeGe7iQE.js → Automations-B6WFJyxS.js} +2 -2
  8. package/dist/interface/assets/{Camera-C0sNg85L.js → Camera-DmsSKvpY.js} +2 -2
  9. package/dist/interface/assets/{CameraConsole-S9Il0g0K.js → CameraConsole-DVpyGhx8.js} +1 -1
  10. package/dist/interface/assets/Cameras-KOOphtlx.js +652 -0
  11. package/dist/interface/assets/Camview-BPQIinyP.js +1 -0
  12. package/dist/interface/assets/{Camview-CS7PIBMz.css → Camview-Lp3QU0de.css} +1 -1
  13. package/dist/interface/assets/{Config-B0cy1RIU.js → Config-DeUec_TN.js} +1 -1
  14. package/dist/interface/assets/ConfigActionCameraControl-DWHg-YEU.js +1 -0
  15. package/dist/interface/assets/{ConfigActionDelay-DrzMP_7q.js → ConfigActionDelay-C8nNA7TC.js} +1 -1
  16. package/dist/interface/assets/{ConfigActionHttp-BUANm5zh.js → ConfigActionHttp-Dc97gItH.js} +1 -1
  17. package/dist/interface/assets/{ConfigActionImageInput-CKvnzs_4.js → ConfigActionImageInput-DpOGqlzA.js} +1 -1
  18. package/dist/interface/assets/{ConfigActionMqtt-Cz-fMzXH.js → ConfigActionMqtt-D5DJ21oe.js} +1 -1
  19. package/dist/interface/assets/{ConfigActionNotification-exxG4m-k.js → ConfigActionNotification-4wKfanGB.js} +1 -1
  20. package/dist/interface/assets/{ConfigActionNotificationControl-BMFbm9lJ.js → ConfigActionNotificationControl-jSgxCZmV.js} +1 -1
  21. package/dist/interface/assets/{ConfigActionOutput-js-UUBci.js → ConfigActionOutput-C8Esieni.js} +1 -1
  22. package/dist/interface/assets/{ConfigActionPlugin-B5hrC_B6.js → ConfigActionPlugin-C_cv60d3.js} +1 -1
  23. package/dist/interface/assets/{ConfigActionSensor-BESCtedA.js → ConfigActionSensor-CFIKoJ3-.js} +1 -1
  24. package/dist/interface/assets/{ConfigActionSnapshot-Uxz9dCqh.js → ConfigActionSnapshot-BqaTwtN3.js} +1 -1
  25. package/dist/interface/assets/{ConfigActionVariable-CsiC6bgF.js → ConfigActionVariable-De9RCGU4.js} +1 -1
  26. package/dist/interface/assets/{ConfigConditionIfElse-CiScJB42.js → ConfigConditionIfElse-CeNDIucw.js} +1 -1
  27. package/dist/interface/assets/{ConfigConditionSensorState-CbDfsuOl.js → ConfigConditionSensorState-C_Q9gU23.js} +1 -1
  28. package/dist/interface/assets/{ConfigConditionSwitch-B7E5DAoC.js → ConfigConditionSwitch-DR63IGcD.js} +1 -1
  29. package/dist/interface/assets/{ConfigConditionTime-BmeGYmwH.js → ConfigConditionTime-C9iuSxxe.js} +1 -1
  30. package/dist/interface/assets/{ConfigSensorValueInput-CNTToTaw.js → ConfigSensorValueInput-DnMlFzil.js} +1 -1
  31. package/dist/interface/assets/{ConfigTriggerDetection-ijTkd_SS.js → ConfigTriggerDetection-iThbCKOO.js} +1 -1
  32. package/dist/interface/assets/{ConfigTriggerGeofence-Q7LQpVoM.js → ConfigTriggerGeofence-BeKwVWvS.js} +1 -1
  33. package/dist/interface/assets/{ConfigTriggerManual-Z1_1wUUM.js → ConfigTriggerManual-DPkRlUqD.js} +1 -1
  34. package/dist/interface/assets/{ConfigTriggerMqtt-BBvD0ow9.js → ConfigTriggerMqtt-kd65tGJE.js} +1 -1
  35. package/dist/interface/assets/{ConfigTriggerSchedule-B0rMjsZL.js → ConfigTriggerSchedule-D6MoVDPc.js} +1 -1
  36. package/dist/interface/assets/{ConfigTriggerSensor-B1p15KGD.js → ConfigTriggerSensor-AMQM6f2r.js} +1 -1
  37. package/dist/interface/assets/{ConfigTriggerSystem-Du-AkCKy.js → ConfigTriggerSystem-CNBgtDF1.js} +1 -1
  38. package/dist/interface/assets/{ConfigTriggerWebhook-Debqt-nw.js → ConfigTriggerWebhook-B70DobSi.js} +1 -1
  39. package/dist/interface/assets/{Console-Gu1eu82G.js → Console-N_2Vornl.js} +1 -1
  40. package/dist/interface/assets/{CuiAutomationRunsDialog-CxcWpqrI.js → CuiAutomationRunsDialog-Cp6Apki9.js} +1 -1
  41. package/dist/interface/assets/CuiBottomSheet-CIR_fmSk.js +1 -0
  42. package/dist/interface/assets/{CuiBottomSheet-DECWGdrp.js → CuiBottomSheet-D5N8X1K-.js} +1 -1
  43. package/dist/interface/assets/CuiCameraPipCard-DP6X45oq.js +1 -0
  44. package/dist/interface/assets/CuiCameraRecordings-CRjXsdO4.js +1 -0
  45. package/dist/interface/assets/{CuiCameraShares-D32cM63m.js → CuiCameraShares-DMHwZEf1.js} +1 -1
  46. package/dist/interface/assets/{CuiCameraTable-Bk9N-Oc6.js → CuiCameraTable-AnEPu3XS.js} +1 -1
  47. package/dist/interface/assets/CuiCameraTable-D5ZqTLVt.js +1 -0
  48. package/dist/interface/assets/CuiConsole-CqEvNGe_.js +5 -0
  49. package/dist/interface/assets/{CuiDataTable-P8bWN0ZK.js → CuiDataTable-wA_LbHH8.js} +1 -1
  50. package/dist/interface/assets/{CuiLightControl-D5Z1AqWH.js → CuiLightControl-CB_Tp4tI.js} +1 -1
  51. package/dist/interface/assets/{CuiList-w69dERIq.js → CuiList-DrYuFhK4.js} +1 -1
  52. package/dist/interface/assets/CuiPluginCard-DEuNt5ni.js +2 -0
  53. package/dist/interface/assets/{CuiPluginCategoryChip-CQ2FJafE.js → CuiPluginCategoryChip-BbkbkYj6.js} +1 -1
  54. package/dist/interface/assets/{CuiPluginOAuthButton-CNxj7cuf.js → CuiPluginOAuthButton-vMuRa9Qd.js} +2 -2
  55. package/dist/interface/assets/{CuiPluginTrustBadge-DYsEz6WI.js → CuiPluginTrustBadge-2wsPzYZA.js} +1 -1
  56. package/dist/interface/assets/{CuiQRCode-CZnspFFw.js → CuiQRCode-Dh9EcaVc.js} +1 -1
  57. package/dist/interface/assets/{CuiRecordingsGrid-wEaU6-3c.js → CuiRecordingsGrid-HjZEiSyp.js} +1 -1
  58. package/dist/interface/assets/{CuiSchema-DLV1lIiB.js → CuiSchema-CSFoI6cg.js} +1 -1
  59. package/dist/interface/assets/{CuiTopNavbarItem-7yu9c68R.js → CuiTopNavbarItem-CSdvkKHR.js} +1 -1
  60. package/dist/interface/assets/{Faces-BlPQek1-.js → Faces-CPZ5RA68.js} +1 -1
  61. package/dist/interface/assets/{FirstSteps-C-DKv_P2.js → FirstSteps-CaF8STWH.js} +1 -1
  62. package/dist/interface/assets/{Instances-Bmb-yWM-.js → Instances-Cw7A4I7E.js} +1 -1
  63. package/dist/interface/assets/{Logs-DxHqkMpx.js → Logs-4Mgj7YWu.js} +2 -2
  64. package/dist/interface/assets/{Menu-DceReBlq.js → Menu-T-54ZSSD.js} +1 -1
  65. package/dist/interface/assets/Metrics-CQGKmHev.css +1 -0
  66. package/dist/interface/assets/Metrics-Dgm5qpzh.js +6 -0
  67. package/dist/interface/assets/{OAuthDeviceFlow-CLi6l8jS.js → OAuthDeviceFlow-DGikOkgK.js} +1 -1
  68. package/dist/interface/assets/{Plugin-BBVxbQeT.js → Plugin-YxqbMbUc.js} +1 -1
  69. package/dist/interface/assets/{PluginConsole-xOY_kwz_.js → PluginConsole-ZZvJlgIh.js} +1 -1
  70. package/dist/interface/assets/PluginDetail-B9NgbzkJ.js +2 -0
  71. package/dist/interface/assets/{PluginSchema-DG3ou73v.js → PluginSchema-Cx_LInpM.js} +1 -1
  72. package/dist/interface/assets/PluginSearch-5AgSWlmi.js +1 -0
  73. package/dist/interface/assets/PluginSearch-P274oMvy.js +2 -0
  74. package/dist/interface/assets/{PluginUpdateProgress-Bx0MXwAk.js → PluginUpdateProgress-0nV-8LvI.js} +1 -1
  75. package/dist/interface/assets/{Plugins-COHwVV0A.js → Plugins-Bvru3q2P.js} +1 -1
  76. package/dist/interface/assets/Recordings-BWGWC6Kq.js +1 -0
  77. package/dist/interface/assets/Recordings-cR4_6aGN.css +1 -0
  78. package/dist/interface/assets/{SensorEdit-C-H_3ap8.js → SensorEdit-gEw6HHZs.js} +1 -1
  79. package/dist/interface/assets/{SensorHistory-CTIUwIWN.js → SensorHistory-IgMpuLdC.js} +1 -1
  80. package/dist/interface/assets/{Sensors-lPcOdHJN.js → Sensors-DukA8Xqx.js} +2 -2
  81. package/dist/interface/assets/{Settings-BUoXoFiH.js → Settings-DWpeWPJh.js} +1 -1
  82. package/dist/interface/assets/{SettingsAccount-DUy_W02_.js → SettingsAccount-C1OpCtob.js} +1 -1
  83. package/dist/interface/assets/SettingsAccount-CrLcqzSn.js +1 -0
  84. package/dist/interface/assets/SettingsAppearance-AhUjml5E.js +1 -0
  85. package/dist/interface/assets/{SettingsAppearance-CoTdsIwM.js → SettingsAppearance-C8EEBVZN.js} +1 -1
  86. package/dist/interface/assets/{SettingsBackup-ibb5I5Jv.js → SettingsBackup-BJupyZCJ.js} +1 -1
  87. package/dist/interface/assets/{SettingsList-W5FvbkXm.js → SettingsList-MXYSQMZ3.js} +1 -1
  88. package/dist/interface/assets/{SettingsMqtt-Ct42icAW.js → SettingsMqtt-F3pV1Bvo.js} +1 -1
  89. package/dist/interface/assets/{SettingsNotifications-B4lyMlLR.js → SettingsNotifications-BmaAccX9.js} +1 -1
  90. package/dist/interface/assets/SettingsPermissions-Dd_yfNlk.js +1 -0
  91. package/dist/interface/assets/{SettingsRecordings-COUi9BQg.js → SettingsRecordings-85B2bQIf.js} +1 -1
  92. package/dist/interface/assets/SettingsRemote-C_ikoXW7.js +2 -0
  93. package/dist/interface/assets/SettingsRemote-DzrfpHjw.css +1 -0
  94. package/dist/interface/assets/SettingsSystem-Ct3qms5O.js +2 -0
  95. package/dist/interface/assets/{SettingsUsers-Cjk6mevW.js → SettingsUsers-D36vZ8Zf.js} +1 -1
  96. package/dist/interface/assets/{Terminal-BIdBrL4g.js → Terminal-ltPiB9CB.js} +3 -3
  97. package/dist/interface/assets/{VariableInput-DhAhhfsm.js → VariableInput-7c7Px-DM.js} +1 -1
  98. package/dist/interface/assets/{VariableSuggestions-q0Jd1DVI.js → VariableSuggestions-CrEa2zN-.js} +1 -1
  99. package/dist/interface/assets/{VersionsHandler-XEn3uFy7.js → VersionsHandler-BFuLGk6Y.js} +1 -1
  100. package/dist/interface/assets/{VirtualSensorCreate-_E5E0U7E.js → VirtualSensorCreate-uumfOHgB.js} +1 -1
  101. package/dist/interface/assets/{Workers-DVABUc39.js → Workers-Bowrc1AF.js} +1 -1
  102. package/dist/interface/assets/{api-C7Yv5Scr.js → api-DLVsQBAO.js} +1 -1
  103. package/dist/interface/assets/api-daCggwpn.css +2 -0
  104. package/dist/interface/assets/api-py5HP4SF.js +2 -0
  105. package/dist/interface/assets/{auth-BixzEnBM.js → auth-uQlIia9h.js} +1 -1
  106. package/dist/interface/assets/{automations-K8XPcP3U.js → automations-ChG1qcO7.js} +1 -1
  107. package/dist/interface/assets/cameras.schema-Cv7cjMz3.js +1 -0
  108. package/dist/interface/assets/cloudHandoff-CL4sircA.js +1 -0
  109. package/dist/interface/assets/{config-CURuT8_k.js → config-B1aWtF-N.js} +1 -1
  110. package/dist/interface/assets/content-copy-CLkJTA4w.js +1 -0
  111. package/dist/interface/assets/eventLabels-BsPoPWS9.js +1 -0
  112. package/dist/interface/assets/{flowSchema-szT9B0sA.js → flowSchema-CNnBxcwy.js} +1 -1
  113. package/dist/interface/assets/{intercomService-BjB4QQiX.js → intercomService-DJHBix4X.js} +1 -1
  114. package/dist/interface/assets/main-CLvcwWW7.js +3 -0
  115. package/dist/interface/assets/main-mJXdi_gx.css +1 -0
  116. package/dist/interface/assets/{mqtt-uItF3Vd7.js → mqtt-Cyhv8shC.js} +1 -1
  117. package/dist/interface/assets/{notifications-CuZPzI4-.js → notifications-DniREnpL.js} +1 -1
  118. package/dist/interface/assets/{plugins-QEqfAk0G.js → plugins-5gXj0utE.js} +1 -1
  119. package/dist/interface/assets/round-restart-alt-D3jT5i_T.js +1 -0
  120. package/dist/interface/assets/round-restart-alt-DdIfy7mU.css +1 -0
  121. package/dist/interface/assets/{sensorIcons-BujySw0j.js → sensorIcons-BXauWntX.js} +1 -1
  122. package/dist/interface/assets/{sensors-BxvL01cB.js → sensors-Bc02Z1G9.js} +1 -1
  123. package/dist/interface/assets/{server-CDm6FwHH.js → server-D2KGja0u.js} +1 -1
  124. package/dist/interface/assets/{theme-CRw47tnu.js → theme-vkX_uBgJ.js} +1 -1
  125. package/dist/interface/assets/types-CrvnT4-i.js +1 -0
  126. package/dist/interface/assets/types-D6IQ6ssc.js +1 -0
  127. package/dist/interface/assets/{types-DtdpTPfZ.js → types-fCQkAGfW.js} +1 -1
  128. package/dist/interface/assets/{useAppVersion-BxTTVLKg.js → useAppVersion-9zH_FspP.js} +1 -1
  129. package/dist/interface/assets/{useBackupRestore-DLieDBFC.js → useBackupRestore-Dbf24OqP.js} +1 -1
  130. package/dist/interface/assets/{useCameraOptions-DMC2KjMS.js → useCameraOptions-BIaDXHeq.js} +1 -1
  131. package/dist/interface/assets/{useLogsSocket-CmyalQi8.js → useLogsSocket-BLV7dWSG.js} +1 -1
  132. package/dist/interface/assets/{useNavLayout-BacPxpZv.js → useNavLayout-wq-6JyWK.js} +1 -1
  133. package/dist/interface/assets/{usePermissions-C2wQBWmH.js → usePermissions-BiBlyeQl.js} +1 -1
  134. package/dist/interface/assets/{usePluginUpdates-CyjxFTTi.js → usePluginUpdates-BZJSqnSF.js} +1 -1
  135. package/dist/interface/assets/{usePushRegistration-BPSfvI_w.js → usePushRegistration-CGFiyCuv.js} +1 -1
  136. package/dist/interface/assets/{useServerRestart-x8p2rFqn.js → useServerRestart-CjPAgzoX.js} +1 -1
  137. package/dist/interface/assets/{useStreamStatus-PMpl519O.js → useStreamStatus-BFCF1B3T.js} +1 -1
  138. package/dist/interface/assets/{useUpdateChannel-ChscS5KW.js → useUpdateChannel-F4WpbMJR.js} +1 -1
  139. package/dist/interface/assets/{useUserOptions-YiEj2ox6.js → useUserOptions-GbSC7_qt.js} +1 -1
  140. package/dist/interface/assets/{vendor-cameraui-Cf0XVNQ7.js → vendor-cameraui-0EJfDg8o.js} +56 -56
  141. package/dist/interface/assets/vendor-core-RnPzfwaA.js +1 -0
  142. package/dist/interface/assets/{vendor-primevue-BDu2BpXv.js → vendor-primevue-b9CB9Uh0.js} +1 -1
  143. package/dist/interface/index.html +26 -25
  144. package/dist/interface/sw.js +1 -1
  145. package/dist/server/package.json +6 -6
  146. package/dist/server/requirements.txt +2 -2
  147. package/dist/server/src/_camera_ui_tools/camera_ui_rpc/client.py +8 -0
  148. package/dist/server/src/_camera_ui_tools/camera_ui_rpc/types.py +20 -0
  149. package/dist/server/src/_camera_ui_tools/camera_ui_sdk/__init__.py +4 -2
  150. package/dist/server/src/_camera_ui_tools/camera_ui_sdk/camera/__init__.py +6 -2
  151. package/dist/server/src/_camera_ui_tools/camera_ui_sdk/camera/config.py +3 -0
  152. package/dist/server/src/_camera_ui_tools/camera_ui_sdk/camera/detection.py +22 -8
  153. package/dist/server/src/_camera_ui_tools/camera_ui_sdk/camera/enums.py +0 -7
  154. package/dist/server/src/_camera_ui_tools/camera_ui_sdk/camera/notification.py +28 -0
  155. package/dist/server/src/_camera_ui_tools/camera_ui_sdk/sensor/__init__.py +4 -0
  156. package/dist/server/src/_camera_ui_tools/camera_ui_sdk/sensor/base.py +18 -0
  157. package/dist/server/src/_camera_ui_tools/camera_ui_sdk/sensor/spec.py +27 -3
  158. package/dist/server/src/api/controllers/auth.controller.js +5 -0
  159. package/dist/server/src/api/controllers/auth.controller.js.map +1 -1
  160. package/dist/server/src/api/controllers/frameWorkers.controller.d.ts +14 -0
  161. package/dist/server/src/api/controllers/frameWorkers.controller.js +120 -1
  162. package/dist/server/src/api/controllers/frameWorkers.controller.js.map +1 -1
  163. package/dist/server/src/api/controllers/remote.controller.js +3 -0
  164. package/dist/server/src/api/controllers/remote.controller.js.map +1 -1
  165. package/dist/server/src/api/controllers/server.controller.js +1 -0
  166. package/dist/server/src/api/controllers/server.controller.js.map +1 -1
  167. package/dist/server/src/api/database/constants.d.ts +0 -1
  168. package/dist/server/src/api/database/constants.js +0 -1
  169. package/dist/server/src/api/database/constants.js.map +1 -1
  170. package/dist/server/src/api/database/index.d.ts +2 -3
  171. package/dist/server/src/api/database/index.js +3 -6
  172. package/dist/server/src/api/database/index.js.map +1 -1
  173. package/dist/server/src/api/database/migrations/v2-1-10.js +33 -0
  174. package/dist/server/src/api/database/migrations/v2-1-10.js.map +1 -0
  175. package/dist/server/src/api/database/migrations/v2-1-11.js +31 -0
  176. package/dist/server/src/api/database/migrations/v2-1-11.js.map +1 -0
  177. package/dist/server/src/api/database/migrations/v2-1-12.js +25 -0
  178. package/dist/server/src/api/database/migrations/v2-1-12.js.map +1 -0
  179. package/dist/server/src/api/database/migrations/v2-1-6.js.map +1 -1
  180. package/dist/server/src/api/database/migrations/v2-1-9.d.ts +3 -0
  181. package/dist/server/src/api/database/migrations/v2-1-9.js +38 -0
  182. package/dist/server/src/api/database/migrations/v2-1-9.js.map +1 -0
  183. package/dist/server/src/api/database/record-schemas.d.ts +18 -13
  184. package/dist/server/src/api/database/record-schemas.js +3 -3
  185. package/dist/server/src/api/database/record-schemas.js.map +1 -1
  186. package/dist/server/src/api/database/types.d.ts +3 -11
  187. package/dist/server/src/api/routes/frameWorkers.routes.js +22 -1
  188. package/dist/server/src/api/routes/frameWorkers.routes.js.map +1 -1
  189. package/dist/server/src/api/schemas/cameras.schema.d.ts +127 -28
  190. package/dist/server/src/api/schemas/cameras.schema.js +40 -7
  191. package/dist/server/src/api/schemas/cameras.schema.js.map +1 -1
  192. package/dist/server/src/api/schemas/frameWorkers.schema.d.ts +6 -0
  193. package/dist/server/src/api/schemas/frameWorkers.schema.js +7 -0
  194. package/dist/server/src/api/schemas/frameWorkers.schema.js.map +1 -1
  195. package/dist/server/src/api/schemas/server.schema.d.ts +1 -0
  196. package/dist/server/src/api/schemas/server.schema.js +4 -0
  197. package/dist/server/src/api/schemas/server.schema.js.map +1 -1
  198. package/dist/server/src/api/schemas/users.schema.d.ts +119 -84
  199. package/dist/server/src/api/schemas/users.schema.js +3 -0
  200. package/dist/server/src/api/schemas/users.schema.js.map +1 -1
  201. package/dist/server/src/api/types/index.d.ts +1 -0
  202. package/dist/server/src/api/types/index.js.map +1 -1
  203. package/dist/server/src/api/websocket/nsp/metrics.d.ts +1 -1
  204. package/dist/server/src/api/websocket/nsp/metrics.js +54 -33
  205. package/dist/server/src/api/websocket/nsp/metrics.js.map +1 -1
  206. package/dist/server/src/api/websocket/nsp/server.js +2 -2
  207. package/dist/server/src/api/websocket/nsp/server.js.map +1 -1
  208. package/dist/server/src/api/websocket/types.d.ts +20 -17
  209. package/dist/server/src/camera/controller.js +1 -0
  210. package/dist/server/src/camera/controller.js.map +1 -1
  211. package/dist/server/src/camera/decoder/audio-loop.d.ts +0 -1
  212. package/dist/server/src/camera/decoder/audio-loop.js +38 -68
  213. package/dist/server/src/camera/decoder/audio-loop.js.map +1 -1
  214. package/dist/server/src/camera/decoder/child.d.ts +4 -1
  215. package/dist/server/src/camera/decoder/child.js +27 -0
  216. package/dist/server/src/camera/decoder/child.js.map +1 -1
  217. package/dist/server/src/camera/decoder/detection-coordinator.d.ts +7 -2
  218. package/dist/server/src/camera/decoder/detection-coordinator.js +226 -130
  219. package/dist/server/src/camera/decoder/detection-coordinator.js.map +1 -1
  220. package/dist/server/src/camera/decoder/detection-pipeline.d.ts +2 -2
  221. package/dist/server/src/camera/decoder/detection-pipeline.js +5 -6
  222. package/dist/server/src/camera/decoder/detection-pipeline.js.map +1 -1
  223. package/dist/server/src/camera/decoder/event-thumbnailer.d.ts +3 -3
  224. package/dist/server/src/camera/decoder/event-thumbnailer.js +2 -2
  225. package/dist/server/src/camera/decoder/event-thumbnailer.js.map +1 -1
  226. package/dist/server/src/camera/decoder/frame-scaler.d.ts +2 -1
  227. package/dist/server/src/camera/decoder/frame-scaler.js +25 -13
  228. package/dist/server/src/camera/decoder/frame-scaler.js.map +1 -1
  229. package/dist/server/src/camera/decoder/perf-tracker.d.ts +27 -8
  230. package/dist/server/src/camera/decoder/perf-tracker.js +72 -50
  231. package/dist/server/src/camera/decoder/perf-tracker.js.map +1 -1
  232. package/dist/server/src/camera/decoder/plugin-registry.d.ts +1 -0
  233. package/dist/server/src/camera/decoder/plugin-registry.js +7 -0
  234. package/dist/server/src/camera/decoder/plugin-registry.js.map +1 -1
  235. package/dist/server/src/camera/decoder/secondary-stage.d.ts +4 -1
  236. package/dist/server/src/camera/decoder/secondary-stage.js +26 -3
  237. package/dist/server/src/camera/decoder/secondary-stage.js.map +1 -1
  238. package/dist/server/src/camera/decoder/sources/analysis-source.d.ts +14 -1
  239. package/dist/server/src/camera/decoder/sources/audio-source.d.ts +7 -3
  240. package/dist/server/src/camera/decoder/sources/audio-source.js +89 -78
  241. package/dist/server/src/camera/decoder/sources/audio-source.js.map +1 -1
  242. package/dist/server/src/camera/decoder/sources/buffered-source.d.ts +26 -9
  243. package/dist/server/src/camera/decoder/sources/buffered-source.js +113 -65
  244. package/dist/server/src/camera/decoder/sources/buffered-source.js.map +1 -1
  245. package/dist/server/src/camera/decoder/sources/frame-handle.d.ts +13 -0
  246. package/dist/server/src/camera/decoder/sources/frame-handle.js +106 -0
  247. package/dist/server/src/camera/decoder/sources/frame-handle.js.map +1 -0
  248. package/dist/server/src/camera/decoder/sources/frame-source.d.ts +25 -26
  249. package/dist/server/src/camera/decoder/sources/frame-source.js +213 -268
  250. package/dist/server/src/camera/decoder/sources/frame-source.js.map +1 -1
  251. package/dist/server/src/camera/decoder/sources/reconnect-loop.d.ts +17 -0
  252. package/dist/server/src/camera/decoder/sources/reconnect-loop.js +59 -0
  253. package/dist/server/src/camera/decoder/sources/reconnect-loop.js.map +1 -0
  254. package/dist/server/src/camera/decoder/sources/snapshot-fetcher.d.ts +15 -0
  255. package/dist/server/src/camera/decoder/sources/snapshot-fetcher.js +58 -0
  256. package/dist/server/src/camera/decoder/sources/snapshot-fetcher.js.map +1 -0
  257. package/dist/server/src/camera/decoder/sources/video-input.d.ts +18 -0
  258. package/dist/server/src/camera/decoder/sources/video-input.js +55 -0
  259. package/dist/server/src/camera/decoder/sources/video-input.js.map +1 -0
  260. package/dist/server/src/camera/decoder/types.d.ts +73 -18
  261. package/dist/server/src/camera/decoder/types.js +2 -0
  262. package/dist/server/src/camera/decoder/types.js.map +1 -1
  263. package/dist/server/src/camera/decoder/worker.d.ts +5 -1
  264. package/dist/server/src/camera/decoder/worker.js +35 -5
  265. package/dist/server/src/camera/decoder/worker.js.map +1 -1
  266. package/dist/server/src/camera/index.d.ts +2 -1
  267. package/dist/server/src/camera/index.js +3 -0
  268. package/dist/server/src/camera/index.js.map +1 -1
  269. package/dist/server/src/camera/streaming/fmp4-session.js +1 -1
  270. package/dist/server/src/camera/streaming/fmp4-session.js.map +1 -1
  271. package/dist/server/src/camera/streaming/rtp-session.js +1 -1
  272. package/dist/server/src/camera/streaming/rtp-session.js.map +1 -1
  273. package/dist/server/src/mqtt/bridge.d.ts +0 -1
  274. package/dist/server/src/mqtt/bridge.js +0 -33
  275. package/dist/server/src/mqtt/bridge.js.map +1 -1
  276. package/dist/server/src/plugins/runtime/node/proxy/cameraDevice.js +9 -0
  277. package/dist/server/src/plugins/runtime/node/proxy/cameraDevice.js.map +1 -1
  278. package/dist/server/src/plugins/runtime/node/proxy/sensorManager.js +8 -0
  279. package/dist/server/src/plugins/runtime/node/proxy/sensorManager.js.map +1 -1
  280. package/dist/server/src/plugins/runtime/python/proxy/camera_device.py +14 -0
  281. package/dist/server/src/plugins/runtime/python/proxy/sensor.py +1 -0
  282. package/dist/server/src/plugins/runtime/python/proxy/sensor_manager.py +13 -0
  283. package/dist/server/src/remote/services/cloudflare-managed.d.ts +2 -0
  284. package/dist/server/src/remote/services/cloudflare-managed.js +9 -2
  285. package/dist/server/src/remote/services/cloudflare-managed.js.map +1 -1
  286. package/dist/server/src/remote/services/cloudflare.d.ts +3 -0
  287. package/dist/server/src/remote/services/cloudflare.js +11 -0
  288. package/dist/server/src/remote/services/cloudflare.js.map +1 -1
  289. package/dist/server/src/remote/services/index.d.ts +2 -0
  290. package/dist/server/src/remote/services/index.js +60 -8
  291. package/dist/server/src/remote/services/index.js.map +1 -1
  292. package/dist/server/src/remote/services/tunnelConnections.d.ts +9 -0
  293. package/dist/server/src/remote/services/tunnelConnections.js +45 -0
  294. package/dist/server/src/remote/services/tunnelConnections.js.map +1 -0
  295. package/dist/server/src/remote/services/urlReadiness.d.ts +2 -0
  296. package/dist/server/src/remote/services/urlReadiness.js +7 -4
  297. package/dist/server/src/remote/services/urlReadiness.js.map +1 -1
  298. package/dist/server/src/rpc/interfaces/frameworker.d.ts +4 -1
  299. package/dist/server/src/rpc/interfaces/sensor.d.ts +1 -0
  300. package/dist/server/src/sensors/registry.d.ts +2 -0
  301. package/dist/server/src/sensors/registry.js +16 -0
  302. package/dist/server/src/sensors/registry.js.map +1 -1
  303. package/dist/server/src/utils/plugin-registry/index.js +1 -1
  304. package/dist/server/src/utils/plugin-registry/index.js.map +1 -1
  305. package/dist/server/src/workers/agent.js +1 -1
  306. package/dist/server/src/workers/agent.js.map +1 -1
  307. package/package.json +6 -6
  308. package/dist/interface/assets/Automation-DDNhcjDg.js +0 -3
  309. package/dist/interface/assets/Cameras-lDMBkoUW.js +0 -652
  310. package/dist/interface/assets/Camview-DzMmXQvW.js +0 -1
  311. package/dist/interface/assets/ConfigActionCameraControl-BMlRbdRM.js +0 -1
  312. package/dist/interface/assets/CuiBottomSheet-BkmkKf1u.js +0 -1
  313. package/dist/interface/assets/CuiCameraPipCard-CuZZZ7Cv.js +0 -1
  314. package/dist/interface/assets/CuiCameraRecordings-BxEA_Vhd.js +0 -1
  315. package/dist/interface/assets/CuiCameraTable-67Itb7my.js +0 -1
  316. package/dist/interface/assets/CuiConsole-De9JpSLz.js +0 -5
  317. package/dist/interface/assets/CuiPluginCard-D3x9FPb6.js +0 -2
  318. package/dist/interface/assets/Metrics-BstlZavQ.css +0 -1
  319. package/dist/interface/assets/Metrics-Cw_Z_e0C.js +0 -1
  320. package/dist/interface/assets/PluginDetail-D2RncxpL.js +0 -2
  321. package/dist/interface/assets/PluginSearch-CBhloRm6.js +0 -1
  322. package/dist/interface/assets/PluginSearch-RGhSeM1T.js +0 -2
  323. package/dist/interface/assets/Recordings-H5qOm_rD.js +0 -1
  324. package/dist/interface/assets/Recordings-zdTzwv7C.css +0 -1
  325. package/dist/interface/assets/SettingsAccount-CudWgknL.js +0 -1
  326. package/dist/interface/assets/SettingsAppearance-eZsdWWp3.js +0 -1
  327. package/dist/interface/assets/SettingsPermissions-Bsv293ri.js +0 -1
  328. package/dist/interface/assets/SettingsRemote-DYld6-1F.css +0 -1
  329. package/dist/interface/assets/SettingsRemote-Dq_BD0fk.js +0 -1
  330. package/dist/interface/assets/SettingsSystem-C5xROdA0.js +0 -2
  331. package/dist/interface/assets/api-AVHJeggR.css +0 -2
  332. package/dist/interface/assets/api-CmdQlVDD.js +0 -2
  333. package/dist/interface/assets/cameras.schema-A_6JklLs.js +0 -1
  334. package/dist/interface/assets/cloudHandoff-D0ytFfHK.js +0 -1
  335. package/dist/interface/assets/main-B1ryIyTr.css +0 -1
  336. package/dist/interface/assets/main-BdIdmDhs.js +0 -3
  337. package/dist/interface/assets/round-restart-alt-B_7HR24a.js +0 -1
  338. package/dist/interface/assets/round-restart-alt-CmrjVFat.css +0 -1
  339. package/dist/interface/assets/types-R10eIequ.js +0 -1
  340. package/dist/interface/assets/types-p_uC0rfp.js +0 -1
  341. package/dist/interface/assets/vendor-core-u4xNQ-aF.js +0 -1
  342. package/dist/server/src/api/database/migrations/v2-0-50.js +0 -45
  343. package/dist/server/src/api/database/migrations/v2-0-50.js.map +0 -1
  344. package/dist/server/src/api/database/migrations/v2-0-51.js +0 -43
  345. package/dist/server/src/api/database/migrations/v2-0-51.js.map +0 -1
  346. package/dist/server/src/api/database/migrations/v2-1-0.js +0 -72
  347. package/dist/server/src/api/database/migrations/v2-1-0.js.map +0 -1
  348. /package/dist/server/src/api/database/migrations/{v2-0-50.d.ts → v2-1-10.d.ts} +0 -0
  349. /package/dist/server/src/api/database/migrations/{v2-0-51.d.ts → v2-1-11.d.ts} +0 -0
  350. /package/dist/server/src/api/database/migrations/{v2-1-0.d.ts → v2-1-12.d.ts} +0 -0
@@ -26,12 +26,12 @@ import { FrameScaler } from './frame-scaler.js';
26
26
  import { directionBetween, directionOf, MOMENT_FORMATS, MOMENT_QUALITY, momentWindow, unionBox } from './moment-crop.js';
27
27
  import { PerfTracker } from './perf-tracker.js';
28
28
  import { normalizePlateText } from './plate-vote.js';
29
- import { isVideoInputSpec, PluginRegistry } from './plugin-registry.js';
29
+ import { isVideoInputSpec, modelIdentity, PluginRegistry } from './plugin-registry.js';
30
30
  import { PtzAutotracker } from './ptz/autotracker.js';
31
- import { ReconnectBackoff } from './reconnect-backoff.js';
32
31
  import { SecondaryStage } from './secondary-stage.js';
32
+ import { BufferedSource } from './sources/buffered-source.js';
33
33
  import { FrameSource } from './sources/frame-source.js';
34
- import { DETECT_TIMEOUT_MS, ensureDetectionBoxes, isFullFrameBox, MOTION_WIDTH_MAP } from './types.js';
34
+ import { DETECT_TIMEOUT_MS, DETECTOR_METRIC_TYPES, ensureDetectionBoxes, isFullFrameBox, MOTION_WIDTH_MAP } from './types.js';
35
35
  const MOMENT_EVENTS = new Set(['objectEntered', 'objectWoke', 'objectRecovered', 'bestShotUpdated']);
36
36
  const MOMENT_MOVING_SPEED = 0.05;
37
37
  const MOMENT_ATTRIBUTE_MIN_AREA = 600;
@@ -46,6 +46,7 @@ const ACTIVE_TICK_MS = 100;
46
46
  const MOTION_INTERVAL_MS = 200;
47
47
  const TICK_SLACK_MS = 20;
48
48
  const MAIN_STREAM_HOLD_MS = 5000;
49
+ const EXTERNAL_FRAME_MAX_AGE_MS = 1000;
49
50
  let DetectionCoordinator = class DetectionCoordinator {
50
51
  config;
51
52
  proxy;
@@ -69,9 +70,9 @@ let DetectionCoordinator = class DetectionCoordinator {
69
70
  feedingSensors = new Map();
70
71
  dwell = new DwellManager();
71
72
  privacy;
72
- videoBackoff = new ReconnectBackoff();
73
73
  // debugging
74
74
  perf = new PerfTracker();
75
+ benchmarkRunning = false;
75
76
  hqUpgrade;
76
77
  mainStreamActive = false;
77
78
  idleSince = 0;
@@ -92,28 +93,30 @@ let DetectionCoordinator = class DetectionCoordinator {
92
93
  this.config = config;
93
94
  this.proxy = proxy;
94
95
  this.logger = logger;
95
- const frameSourceConfig = {
96
- streamUrl: config.streamUrl,
97
- snapshotUrl: config.snapshotUrl,
98
- decoder: this.config.frameWorkerSettings.decoder,
99
- };
96
+ const snapshot = { snapshotUrl: config.snapshotUrl };
100
97
  if (config.controllerSnapshotSourceId) {
101
98
  const sourceId = config.controllerSnapshotSourceId;
102
99
  const controllerProxy = this.proxy.createProxy(NamespaceManager.cameraNamespaces(config.cameraId).cameraControllerRpc);
103
- frameSourceConfig.snapshotProvider = async () => {
100
+ snapshot.snapshotProvider = async () => {
104
101
  const jpeg = await controllerProxy.snapshot(sourceId, true, true);
105
102
  if (!jpeg || jpeg.byteLength === 0)
106
103
  return null;
107
104
  return Buffer.from(jpeg);
108
105
  };
109
106
  }
110
- this.frameSource = new FrameSource(frameSourceConfig, logger);
111
107
  this.config.zones = normalizeZones(config.zones);
112
108
  this.privacy = new PrivacyMask(logger);
113
109
  this.privacy.update(this.config.zones);
110
+ const decoder = this.config.frameWorkerSettings.decoder;
111
+ this.frameSource = config.streamHot
112
+ ? new BufferedSource({ url: config.streamUrl, decoder, privacy: this.privacy, snapshot }, logger)
113
+ : new FrameSource({ streamUrl: config.streamUrl, decoder, ...snapshot }, logger);
114
+ // a hot source buffers from the start, the loop only decides what is analysed
115
+ if (config.streamHot)
116
+ this.frameSource.start();
114
117
  this.frameScaler = new FrameScaler(null, logger, this.privacy);
115
118
  this.pipeline = new DetectionPipeline(this.pipelineZones(), config.detectionSettings);
116
- this.secondaries = new SecondaryStage(this, this.plugins, this.pipeline, this.frameScaler, this.proxy, logger);
119
+ this.secondaries = new SecondaryStage(this, this.plugins, this.pipeline, this.frameScaler, this.proxy, this.perf, logger);
117
120
  if (config.zones.lines.length > 0) {
118
121
  this.pipeline.updateLines(config.zones.lines, this.videoAspectRatio);
119
122
  }
@@ -201,7 +204,86 @@ let DetectionCoordinator = class DetectionCoordinator {
201
204
  return this.config.detectionSettings;
202
205
  }
203
206
  getPerfSnapshot() {
204
- return { ...this.perf.snapshot(), mainStreamEnabled: this.mainStreamAvailable, frameAnalysis: this.plugins.shouldVideoBeActive() };
207
+ const { timings, ...counters } = this.perf.snapshot();
208
+ const detectors = {};
209
+ for (const type of DETECTOR_METRIC_TYPES) {
210
+ const plugin = this.plugins.get(type);
211
+ // motion without frames is the camera's own signal, there is no detector to report
212
+ if (!plugin || (type === SensorType.Motion && !plugin.requiresFrames))
213
+ continue;
214
+ const input = plugin.modelSpec?.input;
215
+ detectors[type] = {
216
+ plugin: plugin.pluginId,
217
+ input: isVideoInputSpec(input) ? `${input.width}x${input.height}` : undefined,
218
+ runtime: plugin.modelSpec?.runtime,
219
+ models: plugin.modelSpec?.models,
220
+ ...timings[type],
221
+ };
222
+ }
223
+ return {
224
+ ...counters,
225
+ detectors,
226
+ mainStreamEnabled: this.mainStreamAvailable,
227
+ frameAnalysis: this.plugins.shouldVideoBeActive(),
228
+ };
229
+ }
230
+ resetPerf() {
231
+ this.perf.reset();
232
+ }
233
+ pauseForBenchmark(paused) {
234
+ this.benchmarkRunning = paused;
235
+ }
236
+ async runObjectBenchmark(iterations, concurrency) {
237
+ const plugin = this.plugins.get(SensorType.Object);
238
+ const input = plugin?.modelSpec?.input;
239
+ if (!plugin || !isVideoInputSpec(input))
240
+ return null;
241
+ const channels = input.format === 'gray' ? 1 : input.format === 'nv12' ? 1.5 : 3;
242
+ const frame = {
243
+ id: 'benchmark',
244
+ data: Buffer.alloc(Math.round(input.width * input.height * channels)),
245
+ width: input.width,
246
+ height: input.height,
247
+ format: input.format,
248
+ };
249
+ let handlerMs = 0;
250
+ let completed = 0;
251
+ let failed = 0;
252
+ const timed = this.proxy.createProxy(NamespaceManager.sensorProviderNamespaces(plugin.pluginId, plugin.sensorId).sensorRpc, {
253
+ onTiming: (_method, timing) => {
254
+ handlerMs += timing.handlerMs;
255
+ },
256
+ });
257
+ const startedAt = Date.now();
258
+ const workers = Array.from({ length: Math.max(1, concurrency) }, async () => {
259
+ while (completed + failed < iterations) {
260
+ completed++;
261
+ try {
262
+ await PromiseTimeout(timed.detectObjects(frame), DETECT_TIMEOUT_MS, undefined, `Benchmark call timed out after ${DETECT_TIMEOUT_MS}ms`);
263
+ }
264
+ catch (error) {
265
+ completed--;
266
+ failed++;
267
+ this.logger.debug('Benchmark call failed:', error);
268
+ }
269
+ }
270
+ });
271
+ await Promise.all(workers);
272
+ const totalMs = Date.now() - startedAt;
273
+ return {
274
+ camera: '',
275
+ plugin: plugin.pluginId,
276
+ sensorId: plugin.sensorId,
277
+ runtime: plugin.modelSpec?.runtime,
278
+ models: plugin.modelSpec?.models,
279
+ input: `${input.width}x${input.height}`,
280
+ iterations: completed,
281
+ failed,
282
+ concurrency,
283
+ totalMs,
284
+ perSecond: totalMs > 0 ? Math.round((completed / (totalMs / 1000)) * 10) / 10 : 0,
285
+ handlerMs: completed > 0 ? Math.round((handlerMs / completed) * 10) / 10 : 0,
286
+ };
205
287
  }
206
288
  updateZoneConfig(zones) {
207
289
  zones = normalizeZones(zones);
@@ -234,6 +316,7 @@ let DetectionCoordinator = class DetectionCoordinator {
234
316
  }
235
317
  async dispose() {
236
318
  await this.stopVideoLoop();
319
+ await this.frameSource.stop();
237
320
  await this.audioLoop.stop();
238
321
  await this.thumbnailer.stop();
239
322
  this.ptzAutotracker.dispose();
@@ -293,6 +376,12 @@ let DetectionCoordinator = class DetectionCoordinator {
293
376
  // the registry only announces the plugin assigned for this type, everyone else reports into the void
294
377
  if (!this.feedingSensors.has(sensorId))
295
378
  return;
379
+ // a detector reports its spec again once its models finished loading
380
+ if (properties.modelSpec) {
381
+ this.applyModelSpec(sensorId, sensorType, properties.modelSpec);
382
+ if (Object.keys(properties).length === 1)
383
+ return;
384
+ }
296
385
  if (sensorType === SensorType.Object) {
297
386
  const filtered = this.applyExternalDetectionFilters(sensorType, properties);
298
387
  // presence feed keeps the autotracker's return-home timer alive even while
@@ -322,39 +411,19 @@ let DetectionCoordinator = class DetectionCoordinator {
322
411
  this.processingExternalSecondary = true;
323
412
  try {
324
413
  const rawExternal = filtered.detections ?? [];
325
- const peeked = this.frameSource.peekLatestFrame();
326
- if (peeked) {
327
- try {
328
- const results = { timestamp: Date.now() };
329
- const objects = await this.runObjectAssist(peeked.frame, rawExternal);
330
- this.ingestAssistedObjects(sensorId, objects);
331
- // processExternal only adapts the shape, external boxes get no real tracking
332
- const objectDetections = this.pipeline.processExternal(objects.detections);
333
- await this.runSecondariesAndThumbnails({ frame: peeked.frame, scaler: this.frameScaler, isMainStream: false }, objectDetections, results);
334
- this.ingestResultsForAllSecondaries(results);
335
- const snapshot = this.buildSnapshot();
336
- if (results.thumbnails && results.thumbnails.length > 0) {
337
- snapshot.thumbnails = results.thumbnails;
338
- }
339
- this.eventManager.processResults(snapshot);
340
- }
341
- finally {
342
- await peeked[Symbol.asyncDispose]();
343
- }
344
- return;
345
- }
346
- const fetched = await this.frameSource.fetchSnapshotFrame();
347
- if (!fetched)
414
+ const handle = await this.frameSource.getFrame(EXTERNAL_FRAME_MAX_AGE_MS);
415
+ if (!handle)
348
416
  return;
349
417
  try {
350
418
  const results = { timestamp: Date.now() };
351
- const objects = await this.runObjectAssist(fetched.frame, rawExternal);
419
+ const objects = await this.runObjectAssist(handle.frame, rawExternal);
352
420
  this.ingestAssistedObjects(sensorId, objects);
353
421
  if (objects.assisted) {
354
422
  // real boxes: crop the object like the frame pipeline, so face and
355
423
  // plate secondaries run on the subject instead of the whole scene
424
+ // (processExternal only adapts the shape, external boxes get no real tracking)
356
425
  const objectDetections = this.pipeline.processExternal(objects.detections);
357
- await this.runSecondariesAndThumbnails({ frame: fetched.frame, scaler: this.frameScaler, isMainStream: false }, objectDetections, results);
426
+ await this.runSecondariesAndThumbnails({ frame: handle.frame, scaler: this.frameScaler, isMainStream: false }, objectDetections, results);
358
427
  this.ingestResultsForAllSecondaries(results);
359
428
  const snapshot = this.buildSnapshot();
360
429
  if (results.thumbnails && results.thumbnails.length > 0) {
@@ -363,9 +432,9 @@ let DetectionCoordinator = class DetectionCoordinator {
363
432
  this.eventManager.processResults(snapshot);
364
433
  }
365
434
  else {
366
- const sceneJpeg = await this.secondaries.detectFullFrame(fetched.frame, results);
435
+ const sceneJpeg = await this.secondaries.detectFullFrame(handle.frame, results);
367
436
  try {
368
- results.thumbnails = await this.secondaries.generateThumbnails(fetched.frame, this.frameScaler, results);
437
+ results.thumbnails = await this.secondaries.generateThumbnails(handle.frame, this.frameScaler, results);
369
438
  }
370
439
  catch (error) {
371
440
  this.logger.error('Thumbnail generation error:', error);
@@ -385,7 +454,7 @@ let DetectionCoordinator = class DetectionCoordinator {
385
454
  }
386
455
  }
387
456
  finally {
388
- await fetched[Symbol.asyncDispose]();
457
+ await handle[Symbol.asyncDispose]();
389
458
  }
390
459
  }
391
460
  finally {
@@ -546,7 +615,7 @@ let DetectionCoordinator = class DetectionCoordinator {
546
615
  faceEmbeddingModel: cs.faceEmbeddingModel,
547
616
  plates: cs.licensePlate?.detections ?? [],
548
617
  plateVoting: this.plugins.get(SensorType.LicensePlate)?.requiresFrames === true,
549
- plateMinConfidence: this.config.detectionSettings.licensePlate?.confidence,
618
+ plateMinConfidence: this.config.detectionSettings.licensePlate?.ocrConfidence,
550
619
  plateMinLength: this.config.detectionSettings.licensePlate?.minLength,
551
620
  faceMinConfidence: this.config.detectionSettings.face?.confidence,
552
621
  classifiers: cs.classifiers ? Object.values(cs.classifiers).flatMap((c) => c.detections) : [],
@@ -596,12 +665,12 @@ let DetectionCoordinator = class DetectionCoordinator {
596
665
  let boxlessLabel;
597
666
  switch (sensorType) {
598
667
  case SensorType.Face:
599
- boxlessLabel = 'person';
600
- filtered = this.pipeline.runZoneFilterWithLabel(positioned, 'person');
668
+ boxlessLabel = 'face';
669
+ filtered = this.pipeline.runZoneFilterWithLabel(positioned, 'face');
601
670
  break;
602
671
  case SensorType.LicensePlate:
603
- boxlessLabel = 'vehicle';
604
- filtered = this.pipeline.runZoneFilterWithLabel(positioned, 'vehicle');
672
+ boxlessLabel = 'license_plate';
673
+ filtered = this.pipeline.runZoneFilterWithLabel(positioned, 'license_plate');
605
674
  break;
606
675
  case SensorType.Object:
607
676
  filtered = this.pipeline.runZoneFilter(positioned);
@@ -755,6 +824,18 @@ let DetectionCoordinator = class DetectionCoordinator {
755
824
  break;
756
825
  }
757
826
  }
827
+ applyModelSpec(sensorId, sensorType, modelSpec) {
828
+ const current = this.plugins.get(sensorType);
829
+ if (current?.sensorId !== sensorId)
830
+ return;
831
+ // another model means other numbers, keeping the old average would mislead;
832
+ // load time is not part of the identity, a reload of the same model is not a change
833
+ if (sensorType === SensorType.Object || sensorType === SensorType.Motion) {
834
+ if (modelIdentity(current.modelSpec) !== modelIdentity(modelSpec))
835
+ this.perf.reset();
836
+ }
837
+ this.plugins.updateModelSpec(sensorId, modelSpec);
838
+ }
758
839
  async registerDetectionPluginInternal(sensor) {
759
840
  if (this.videoStopPromise)
760
841
  await this.videoStopPromise;
@@ -762,7 +843,9 @@ let DetectionCoordinator = class DetectionCoordinator {
762
843
  const wasVideoNeeded = this.plugins.shouldVideoBeActive();
763
844
  const wasAudioNeeded = this.plugins.shouldAudioBeActive();
764
845
  const namespaces = NamespaceManager.sensorProviderNamespaces(sensor.pluginId, sensor.sensorId);
765
- const sensorProxy = this.proxy.createProxy(namespaces.sensorRpc);
846
+ const sensorProxy = this.proxy.createProxy(namespaces.sensorRpc, {
847
+ onTiming: (_method, timing) => this.perf.trackTiming(sensor.sensorType, timing.handlerMs, timing.transportMs),
848
+ });
766
849
  const registered = this.plugins.register({
767
850
  pluginId: sensor.pluginId,
768
851
  sensorId: sensor.sensorId,
@@ -773,7 +856,8 @@ let DetectionCoordinator = class DetectionCoordinator {
773
856
  });
774
857
  if (!registered) {
775
858
  // re-push from the registry: the sensor is already live, only the spec may have changed
776
- this.plugins.updateModelSpec(sensor.sensorId, sensor.modelSpec);
859
+ if (sensor.modelSpec)
860
+ this.applyModelSpec(sensor.sensorId, sensor.sensorType, sensor.modelSpec);
777
861
  return;
778
862
  }
779
863
  this.logger.trace(`Plugin registered: ${sensor.pluginId} for ${sensor.sensorType}`);
@@ -854,7 +938,6 @@ let DetectionCoordinator = class DetectionCoordinator {
854
938
  return;
855
939
  this.logger.debug('Starting video detection loop');
856
940
  this.loopRunning = true;
857
- this.videoBackoff.reset();
858
941
  this.loopPromise = this.runDetectionLoop();
859
942
  }
860
943
  async stopVideoLoop() {
@@ -869,7 +952,7 @@ let DetectionCoordinator = class DetectionCoordinator {
869
952
  this.mainStreamActive = false;
870
953
  this.idleSince = 0;
871
954
  const doStop = async () => {
872
- await this.frameSource.stop();
955
+ await this.frameSource.detach();
873
956
  await this.loopPromise;
874
957
  this.loopPromise = undefined;
875
958
  };
@@ -896,81 +979,87 @@ let DetectionCoordinator = class DetectionCoordinator {
896
979
  }
897
980
  }
898
981
  async runDetectionLoop() {
982
+ // the source reconnects on its own, nextFrame simply waits it out
983
+ const startedAt = Date.now();
984
+ await this.frameSource.start();
985
+ let lastFrameId = -1;
986
+ let seenGeneration = -1;
987
+ let firstFrame = true;
899
988
  while (this.loopRunning) {
989
+ const tickStart = Date.now();
990
+ const snap = await this.frameSource.nextFrame(lastFrameId);
991
+ if (!snap)
992
+ break; // source stopped
993
+ if (firstFrame) {
994
+ firstFrame = false;
995
+ this.logger.debug(`Analysing after ${Date.now() - startedAt}ms`);
996
+ }
997
+ lastFrameId = snap.id;
998
+ if (this.benchmarkRunning) {
999
+ // the detector is saturated on purpose, feeding it real frames would
1000
+ // both distort the measurement and queue up behind it
1001
+ snap.frame[Symbol.dispose]?.();
1002
+ await sleep(IDLE_TICK_MS);
1003
+ continue;
1004
+ }
900
1005
  try {
901
- await this.frameSource.start();
902
- this.frameScaler.updateHardwareContext(this.frameSource.hardwareContext);
903
- // the hardware context exists only after start, re-evaluate the HQ session
904
- this.thumbnailer.sync(this.mainStreamAnalysisWanted);
905
- this.logger.debug('Stream connected, processing frames...');
906
- this.videoBackoff.reset();
907
- let frameCount = 0;
908
- let lastFrameId = -1;
909
- while (this.loopRunning) {
910
- const tickStart = Date.now();
911
- const snap = await this.frameSource.nextFrame(lastFrameId);
912
- if (!snap)
913
- break; // source ended (stop or EOF)
914
- lastFrameId = snap.id;
915
- frameCount++;
916
- // motion always reads the low stream: switching its input resolution
917
- // would reset the background model on every transition
918
- const analysis = await this.acquireAnalysisFrame(snap.frame);
919
- // debugging
920
- detectionRecord.setFrame(analysis.isMainStream ? 'main' : 'low', analysis.frame.width, analysis.frame.height);
1006
+ if (this.frameSource.generation !== seenGeneration) {
1007
+ seenGeneration = this.frameSource.generation;
1008
+ // a fresh connection brings its own hardware context and resolution
1009
+ this.frameScaler.updateHardwareContext(this.frameSource.hardwareContext);
1010
+ this.frameScaler.clearCache();
1011
+ this.thumbnailer.sync(this.mainStreamAnalysisWanted);
1012
+ }
1013
+ // motion always reads the low stream: switching its input resolution
1014
+ // would reset the background model on every transition
1015
+ const analysis = await this.acquireAnalysisFrame(snap.frame);
1016
+ // debugging
1017
+ detectionRecord.setFrame(analysis.isMainStream ? 'main' : 'low', analysis.frame.width, analysis.frame.height);
1018
+ try {
1019
+ await this.processRawFrame(analysis, snap.frame);
1020
+ }
1021
+ finally {
921
1022
  try {
922
- await this.processRawFrame(analysis, snap.frame);
923
- }
924
- finally {
925
- try {
926
- if (this.hqUpgrade) {
927
- this.hqUpgrade.frame[Symbol.dispose]?.();
928
- this.hqUpgrade = undefined;
929
- }
930
- if (analysis.frame !== snap.frame)
931
- analysis.frame[Symbol.dispose]?.();
932
- snap.frame[Symbol.dispose]?.();
933
- }
934
- catch {
935
- // best-effort
1023
+ if (this.hqUpgrade) {
1024
+ this.hqUpgrade.frame[Symbol.dispose]?.();
1025
+ this.hqUpgrade = undefined;
936
1026
  }
1027
+ if (analysis.frame !== snap.frame)
1028
+ analysis.frame[Symbol.dispose]?.();
1029
+ }
1030
+ catch {
1031
+ // ignore
937
1032
  }
938
- this.updateStreamState();
939
- // activity, not stream choice: cameras without main-stream analysis still count as active
940
- if (this.worldSpans.size > 0 || this.eventManager.hasActiveSegment())
941
- this.perf.activeTicks++;
942
- else
943
- this.perf.idleTicks++;
944
- this.perf.report(this.logger);
945
- const remaining = (this.mainStreamActive ? ACTIVE_TICK_MS : IDLE_TICK_MS) - (Date.now() - tickStart);
946
- if (remaining > 0)
947
- await sleep(remaining);
948
- this.perf.loopMs += Date.now() - tickStart;
949
- }
950
- const streamError = this.frameSource.lastError;
951
- await this.frameSource.stop();
952
- this.frameScaler.clearCache();
953
- if (this.loopRunning && streamError) {
954
- const delay = this.videoBackoff.nextDelayMs();
955
- this.logger.warn(`Stream ended with read error, reconnecting in ${delay / 1000}s: ${streamError.message}`);
956
- await sleep(delay);
957
- }
958
- else if (this.loopRunning && frameCount === 0) {
959
- this.logger.debug('Stream ended without frames, waiting before reconnect...');
960
- await sleep(this.videoBackoff.idleDelayMs);
961
1033
  }
1034
+ this.updateStreamState();
962
1035
  }
963
1036
  catch (error) {
964
- if (!this.loopRunning)
965
- break;
966
- await this.frameSource.stop();
967
- this.frameScaler.clearCache();
968
- const delay = this.videoBackoff.nextDelayMs();
969
- this.logger.warn(`Stream error, reconnecting in ${delay / 1000}s: ${error.message}`);
970
- await sleep(delay);
1037
+ // the stream keeps running, a broken tick must not end the loop
1038
+ this.logger.error('Detection tick failed:', error);
971
1039
  }
972
- }
973
- await this.frameSource.stop();
1040
+ finally {
1041
+ try {
1042
+ snap.frame[Symbol.dispose]?.();
1043
+ }
1044
+ catch {
1045
+ // ignore
1046
+ }
1047
+ }
1048
+ const decode = this.frameSource.takeDecodeStats();
1049
+ this.perf.decodeMs += decode.ms;
1050
+ this.perf.decodedFrames += decode.frames;
1051
+ // activity, not stream choice: cameras without main-stream analysis still count as active
1052
+ if (this.worldSpans.size > 0 || this.eventManager.hasActiveSegment())
1053
+ this.perf.activeTicks++;
1054
+ else
1055
+ this.perf.idleTicks++;
1056
+ this.perf.report(this.logger);
1057
+ const remaining = (this.mainStreamActive ? ACTIVE_TICK_MS : IDLE_TICK_MS) - (Date.now() - tickStart);
1058
+ if (remaining > 0)
1059
+ await sleep(remaining);
1060
+ this.perf.loopMs += Date.now() - tickStart;
1061
+ }
1062
+ await this.frameSource.detach();
974
1063
  this.frameScaler.clearCache();
975
1064
  this.logger.debug('Detection loop ended');
976
1065
  }
@@ -989,7 +1078,7 @@ let DetectionCoordinator = class DetectionCoordinator {
989
1078
  const main = await this.thumbnailer.acquireHqFrame(0);
990
1079
  if (main) {
991
1080
  this.perf.mainFrames++;
992
- this.perf.decodeMs += Date.now() - t0;
1081
+ this.perf.mainDecodeMs += Date.now() - t0;
993
1082
  return { frame: main.frame, scaler: main.scaler, isMainStream: true };
994
1083
  }
995
1084
  }
@@ -1007,7 +1096,7 @@ let DetectionCoordinator = class DetectionCoordinator {
1007
1096
  const main = await this.thumbnailer.acquireHqFrame(0);
1008
1097
  if (main) {
1009
1098
  this.perf.mainFrames++;
1010
- this.perf.decodeMs += Date.now() - t0;
1099
+ this.perf.mainDecodeMs += Date.now() - t0;
1011
1100
  return { frame: main.frame, scaler: main.scaler, isMainStream: true };
1012
1101
  }
1013
1102
  }
@@ -1074,8 +1163,8 @@ let DetectionCoordinator = class DetectionCoordinator {
1074
1163
  try {
1075
1164
  const motionInferStart = Date.now();
1076
1165
  const result = await motionPlugin.proxy.detectMotion(motionFrame);
1077
- this.perf.inferMs += Date.now() - motionInferStart;
1078
- this.perf.inferCount++;
1166
+ this.perf.motionMs += Date.now() - motionInferStart;
1167
+ this.perf.motionCount++;
1079
1168
  if (!this.loopRunning)
1080
1169
  return;
1081
1170
  if (ptzSuppressed) {
@@ -1126,16 +1215,18 @@ let DetectionCoordinator = class DetectionCoordinator {
1126
1215
  try {
1127
1216
  const inferStart = Date.now();
1128
1217
  const result = await PromiseTimeout(objectPlugin.proxy.detectObjects(objectFrame.model), DETECT_TIMEOUT_MS, undefined, `Object detection timed out after ${DETECT_TIMEOUT_MS}ms`);
1129
- this.perf.inferMs += Date.now() - inferStart;
1130
- this.perf.inferCount++;
1218
+ this.perf.objectMs += Date.now() - inferStart;
1219
+ this.perf.objectCount++;
1131
1220
  if (!this.loopRunning)
1132
1221
  return;
1133
1222
  this.trackDetectionCadence();
1134
1223
  // run the pipeline even on empty frames so Norfair advances its
1135
1224
  // Kalman state; the pose delta keeps predictions stable across pans
1136
1225
  const poseDelta = this.ptzAutotracker.consumePoseDelta();
1226
+ const postStart = Date.now();
1137
1227
  const detected = FrameScaler.undoLetterbox(ensureDetectionBoxes(result.detections), objectFrame.geometry);
1138
1228
  const pipelineResult = this.pipeline.process(detected, poseDelta);
1229
+ this.perf.postMs += Date.now() - postStart;
1139
1230
  // the tick that opens a span analysed the low stream (the switch lands
1140
1231
  // next tick), its picture work is worth a one-off HQ decode
1141
1232
  if (this.updateWorldSpans(pipelineResult) && !analysis.isMainStream) {
@@ -1259,6 +1350,7 @@ let DetectionCoordinator = class DetectionCoordinator {
1259
1350
  if (objectDetections.length === 0)
1260
1351
  return;
1261
1352
  this.perf.objects += objectDetections.length;
1353
+ this.perf.framesWithObjects++;
1262
1354
  const secondaryStart = Date.now();
1263
1355
  await this.secondaries.detect(analysis.frame, analysis.scaler, objectDetections, results);
1264
1356
  this.perf.secondaryMs += Date.now() - secondaryStart;
@@ -1280,15 +1372,18 @@ let DetectionCoordinator = class DetectionCoordinator {
1280
1372
  }
1281
1373
  const facePlugin = this.plugins.get(SensorType.Face);
1282
1374
  if (facePlugin) {
1375
+ // the crop path reaches ingestDetectionResult directly, so the zone
1376
+ // filter that guards the plugin path has to be applied here too
1377
+ const detections = this.pipeline.runZoneFilterWithLabel(results.face.detections, 'face');
1283
1378
  // no mid-segment clear: a turned-away head yields a no-face frame and
1284
1379
  // would flap the list, departure is reflected when the segment ends
1285
- for (const detection of results.face.detections) {
1380
+ for (const detection of detections) {
1286
1381
  if (detection.identity)
1287
1382
  this.faceIdentities.add(detection.identity);
1288
1383
  }
1289
1384
  this.ingestDetectionResult(SensorType.Face, facePlugin.sensorId, {
1290
- detected: results.face.detected,
1291
- detections: results.face.detections,
1385
+ detected: detections.length > 0 && results.face.detected,
1386
+ detections,
1292
1387
  identities: [...this.faceIdentities].sort(),
1293
1388
  });
1294
1389
  }
@@ -1296,14 +1391,15 @@ let DetectionCoordinator = class DetectionCoordinator {
1296
1391
  if (results.licensePlate) {
1297
1392
  const lpdPlugin = this.plugins.get(SensorType.LicensePlate);
1298
1393
  if (lpdPlugin) {
1299
- for (const detection of results.licensePlate.detections) {
1394
+ const detections = this.pipeline.runZoneFilterWithLabel(results.licensePlate.detections, 'license_plate');
1395
+ for (const detection of detections) {
1300
1396
  const plate = detection.plateText ? normalizePlateText(detection.plateText) || detection.plateText : '';
1301
1397
  if (plate)
1302
1398
  this.platesSeen.add(plate);
1303
1399
  }
1304
1400
  this.ingestDetectionResult(SensorType.LicensePlate, lpdPlugin.sensorId, {
1305
- detected: results.licensePlate.detected,
1306
- detections: results.licensePlate.detections,
1401
+ detected: detections.length > 0 && results.licensePlate.detected,
1402
+ detections,
1307
1403
  plates: [...this.platesSeen].sort(),
1308
1404
  });
1309
1405
  }
@@ -1593,8 +1689,8 @@ let DetectionCoordinator = class DetectionCoordinator {
1593
1689
  try {
1594
1690
  const inferStart = Date.now();
1595
1691
  const result = await PromiseTimeout(assist.proxy.detectObjects(scaled.model), DETECT_TIMEOUT_MS, undefined, `Object assist timed out after ${DETECT_TIMEOUT_MS}ms`);
1596
- this.perf.inferMs += Date.now() - inferStart;
1597
- this.perf.inferCount++;
1692
+ this.perf.assistMs += Date.now() - inferStart;
1693
+ this.perf.assistCount++;
1598
1694
  const reportedLabels = new Set(reported.map((d) => d.label.toLowerCase()));
1599
1695
  const boxed = FrameScaler.undoLetterbox(ensureDetectionBoxes(result?.detections ?? []), scaled.geometry);
1600
1696
  const found = boxed.filter((d) => reportedLabels.size === 0 || reportedLabels.has(d.label.toLowerCase()));