@camera.ui/server 2.4.0 → 2.4.2

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 (1582) hide show
  1. package/dist/interface/assets/404-DjAi7TGJ.js +1 -0
  2. package/dist/interface/assets/404-DjAi7TGJ.js.br +0 -0
  3. package/dist/interface/assets/404-DjAi7TGJ.js.gz +0 -0
  4. package/dist/interface/assets/About-DgFgNzrr.js +1 -0
  5. package/dist/interface/assets/About-DgFgNzrr.js.br +0 -0
  6. package/dist/interface/assets/About-DgFgNzrr.js.gz +0 -0
  7. package/dist/interface/assets/Assistant-DFyCH8rW.js +1 -0
  8. package/dist/interface/assets/Assistant-DFyCH8rW.js.br +0 -0
  9. package/dist/interface/assets/Assistant-DFyCH8rW.js.gz +0 -0
  10. package/dist/interface/assets/Automation-Y7gnBVsz.js +3 -0
  11. package/dist/interface/assets/Automation-Y7gnBVsz.js.br +0 -0
  12. package/dist/interface/assets/Automation-Y7gnBVsz.js.gz +0 -0
  13. package/dist/interface/assets/AutomationImport-BAWCUNSF.js +2 -0
  14. package/dist/interface/assets/AutomationImport-BAWCUNSF.js.br +0 -0
  15. package/dist/interface/assets/AutomationImport-BAWCUNSF.js.gz +0 -0
  16. package/dist/interface/assets/AutomationStore-CLFRjxqB.js +2 -0
  17. package/dist/interface/assets/AutomationStore-CLFRjxqB.js.br +0 -0
  18. package/dist/interface/assets/AutomationStore-CLFRjxqB.js.gz +0 -0
  19. package/dist/interface/assets/AutomationStoreDetail-Da_OF8Zw.js +2 -0
  20. package/dist/interface/assets/AutomationStoreDetail-Da_OF8Zw.js.br +0 -0
  21. package/dist/interface/assets/AutomationStoreDetail-Da_OF8Zw.js.gz +0 -0
  22. package/dist/interface/assets/Automations-Dj4vxoes.js +2 -0
  23. package/dist/interface/assets/Automations-Dj4vxoes.js.br +0 -0
  24. package/dist/interface/assets/Automations-Dj4vxoes.js.gz +0 -0
  25. package/dist/interface/assets/BackupRestore-DbHPYoLQ.js +1 -0
  26. package/dist/interface/assets/BackupRestore-DbHPYoLQ.js.br +0 -0
  27. package/dist/interface/assets/BackupRestore-DbHPYoLQ.js.gz +0 -0
  28. package/dist/interface/assets/Camera-DD42OSkZ.js +2 -0
  29. package/dist/interface/assets/Camera-DD42OSkZ.js.br +0 -0
  30. package/dist/interface/assets/Camera-DD42OSkZ.js.gz +0 -0
  31. package/dist/interface/assets/CameraConsole-Cn8I2sC6.js +7 -0
  32. package/dist/interface/assets/CameraConsole-Cn8I2sC6.js.br +0 -0
  33. package/dist/interface/assets/CameraConsole-Cn8I2sC6.js.gz +0 -0
  34. package/dist/interface/assets/Cameras-BA4ZtLCN.js +651 -0
  35. package/dist/interface/assets/Cameras-BA4ZtLCN.js.br +0 -0
  36. package/dist/interface/assets/Cameras-BA4ZtLCN.js.gz +0 -0
  37. package/dist/interface/assets/Camview-CEEyXExJ.js +1 -0
  38. package/dist/interface/assets/Camview-CEEyXExJ.js.br +0 -0
  39. package/dist/interface/assets/Camview-CEEyXExJ.js.gz +0 -0
  40. package/dist/interface/assets/Config-7VDhtZly.js +2 -0
  41. package/dist/interface/assets/Config-7VDhtZly.js.br +0 -0
  42. package/dist/interface/assets/Config-7VDhtZly.js.gz +0 -0
  43. package/dist/interface/assets/ConfigActionAssistant-B_v8QtHd.js +1 -0
  44. package/dist/interface/assets/ConfigActionAssistant-B_v8QtHd.js.br +0 -0
  45. package/dist/interface/assets/ConfigActionAssistant-B_v8QtHd.js.gz +0 -0
  46. package/dist/interface/assets/ConfigActionCameraControl-Cp9htIfC.js +1 -0
  47. package/dist/interface/assets/ConfigActionCameraControl-Cp9htIfC.js.br +0 -0
  48. package/dist/interface/assets/ConfigActionCameraControl-Cp9htIfC.js.gz +0 -0
  49. package/dist/interface/assets/ConfigActionHttp-BrzVENpN.js +1 -0
  50. package/dist/interface/assets/ConfigActionHttp-BrzVENpN.js.br +0 -0
  51. package/dist/interface/assets/ConfigActionHttp-BrzVENpN.js.gz +0 -0
  52. package/dist/interface/assets/ConfigActionImageInput-CH0TW4SM.js +1 -0
  53. package/dist/interface/assets/ConfigActionImageInput-CH0TW4SM.js.br +0 -0
  54. package/dist/interface/assets/ConfigActionImageInput-CH0TW4SM.js.gz +0 -0
  55. package/dist/interface/assets/ConfigActionMqtt-CCMPJXSw.js +1 -0
  56. package/dist/interface/assets/ConfigActionMqtt-CCMPJXSw.js.br +0 -0
  57. package/dist/interface/assets/ConfigActionMqtt-CCMPJXSw.js.gz +0 -0
  58. package/dist/interface/assets/ConfigActionNotification-C2Tu80Lc.js +1 -0
  59. package/dist/interface/assets/ConfigActionNotification-C2Tu80Lc.js.br +0 -0
  60. package/dist/interface/assets/ConfigActionNotification-C2Tu80Lc.js.gz +0 -0
  61. package/dist/interface/assets/ConfigActionNotificationControl-CgED5yJ1.js +1 -0
  62. package/dist/interface/assets/ConfigActionNotificationControl-CgED5yJ1.js.br +0 -0
  63. package/dist/interface/assets/ConfigActionNotificationControl-CgED5yJ1.js.gz +0 -0
  64. package/dist/interface/assets/ConfigActionOutput-CCl-Kmbr.js +1 -0
  65. package/dist/interface/assets/ConfigActionOutput-CCl-Kmbr.js.br +0 -0
  66. package/dist/interface/assets/ConfigActionOutput-CCl-Kmbr.js.gz +0 -0
  67. package/dist/interface/assets/ConfigActionPlugin-D0mqVxPX.js +1 -0
  68. package/dist/interface/assets/ConfigActionPlugin-D0mqVxPX.js.br +0 -0
  69. package/dist/interface/assets/ConfigActionPlugin-D0mqVxPX.js.gz +0 -0
  70. package/dist/interface/assets/ConfigActionSensor-e7FD2s99.js +1 -0
  71. package/dist/interface/assets/ConfigActionSensor-e7FD2s99.js.br +0 -0
  72. package/dist/interface/assets/ConfigActionSensor-e7FD2s99.js.gz +0 -0
  73. package/dist/interface/assets/ConfigActionSnapshot-i5bUsx36.js +1 -0
  74. package/dist/interface/assets/ConfigActionSnapshot-i5bUsx36.js.br +0 -0
  75. package/dist/interface/assets/ConfigActionSnapshot-i5bUsx36.js.gz +0 -0
  76. package/dist/interface/assets/ConfigActionVariable-CVLSQ7C-.js +1 -0
  77. package/dist/interface/assets/ConfigActionVariable-CVLSQ7C-.js.br +0 -0
  78. package/dist/interface/assets/ConfigActionVariable-CVLSQ7C-.js.gz +0 -0
  79. package/dist/interface/assets/ConfigConditionIfElse-CiBQjdcM.js +1 -0
  80. package/dist/interface/assets/ConfigConditionIfElse-CiBQjdcM.js.br +0 -0
  81. package/dist/interface/assets/ConfigConditionIfElse-CiBQjdcM.js.gz +0 -0
  82. package/dist/interface/assets/ConfigConditionSensorState-BsoyEGCq.js +1 -0
  83. package/dist/interface/assets/ConfigConditionSensorState-BsoyEGCq.js.br +0 -0
  84. package/dist/interface/assets/ConfigConditionSensorState-BsoyEGCq.js.gz +0 -0
  85. package/dist/interface/assets/ConfigConditionSwitch-B_nuglnE.js +1 -0
  86. package/dist/interface/assets/ConfigConditionSwitch-B_nuglnE.js.br +0 -0
  87. package/dist/interface/assets/ConfigConditionSwitch-B_nuglnE.js.gz +0 -0
  88. package/dist/interface/assets/ConfigSensorValueInput-CUbuCYJc.js +1 -0
  89. package/dist/interface/assets/ConfigSensorValueInput-CUbuCYJc.js.br +0 -0
  90. package/dist/interface/assets/ConfigSensorValueInput-CUbuCYJc.js.gz +0 -0
  91. package/dist/interface/assets/ConfigTriggerDetection-Bl8KDz38.js +1 -0
  92. package/dist/interface/assets/ConfigTriggerDetection-Bl8KDz38.js.br +0 -0
  93. package/dist/interface/assets/ConfigTriggerDetection-Bl8KDz38.js.gz +0 -0
  94. package/dist/interface/assets/ConfigTriggerGeofence-Cq2fqDc1.js +1 -0
  95. package/dist/interface/assets/ConfigTriggerGeofence-Cq2fqDc1.js.br +0 -0
  96. package/dist/interface/assets/ConfigTriggerGeofence-Cq2fqDc1.js.gz +0 -0
  97. package/dist/interface/assets/ConfigTriggerMqtt-CxJ2kuT0.js +1 -0
  98. package/dist/interface/assets/ConfigTriggerMqtt-CxJ2kuT0.js.br +0 -0
  99. package/dist/interface/assets/ConfigTriggerMqtt-CxJ2kuT0.js.gz +0 -0
  100. package/dist/interface/assets/ConfigTriggerSensor-rxkiy0es.js +1 -0
  101. package/dist/interface/assets/ConfigTriggerSensor-rxkiy0es.js.br +0 -0
  102. package/dist/interface/assets/ConfigTriggerSensor-rxkiy0es.js.gz +0 -0
  103. package/dist/interface/assets/ConfigTriggerSystem-ZqkR-n7U.js +1 -0
  104. package/dist/interface/assets/ConfigTriggerSystem-ZqkR-n7U.js.br +0 -0
  105. package/dist/interface/assets/ConfigTriggerSystem-ZqkR-n7U.js.gz +0 -0
  106. package/dist/interface/assets/ConfigTriggerWebhook-Bwk3dOMV.js +1 -0
  107. package/dist/interface/assets/ConfigTriggerWebhook-Bwk3dOMV.js.br +0 -0
  108. package/dist/interface/assets/ConfigTriggerWebhook-Bwk3dOMV.js.gz +0 -0
  109. package/dist/interface/assets/Console-B1IGz3XF.js +1 -0
  110. package/dist/interface/assets/Console-B1IGz3XF.js.br +0 -0
  111. package/dist/interface/assets/Console-B1IGz3XF.js.gz +0 -0
  112. package/dist/interface/assets/CuiAssistantConversation-COHgYzWP.js +5 -0
  113. package/dist/interface/assets/CuiAssistantConversation-COHgYzWP.js.br +0 -0
  114. package/dist/interface/assets/CuiAssistantConversation-COHgYzWP.js.gz +0 -0
  115. package/dist/interface/assets/CuiAutomationRunsDialog-DHCxr6ag.js +1 -0
  116. package/dist/interface/assets/CuiAutomationRunsDialog-DHCxr6ag.js.br +0 -0
  117. package/dist/interface/assets/CuiAutomationRunsDialog-DHCxr6ag.js.gz +0 -0
  118. package/dist/interface/assets/CuiBottomSheet-BYifCbSl.js +1 -0
  119. package/dist/interface/assets/CuiBottomSheet-BYifCbSl.js.br +0 -0
  120. package/dist/interface/assets/CuiBottomSheet-BYifCbSl.js.gz +0 -0
  121. package/dist/interface/assets/CuiBottomSheet-Dlk-laff.js +1 -0
  122. package/dist/interface/assets/CuiBottomSheet-Dlk-laff.js.br +0 -0
  123. package/dist/interface/assets/CuiBottomSheet-Dlk-laff.js.gz +0 -0
  124. package/dist/interface/assets/CuiCameraPipCard-DiAAKOB3.js +1 -0
  125. package/dist/interface/assets/CuiCameraPipCard-DiAAKOB3.js.br +0 -0
  126. package/dist/interface/assets/CuiCameraPipCard-DiAAKOB3.js.gz +0 -0
  127. package/dist/interface/assets/CuiCameraRecordings-cr_gatvr.js +1 -0
  128. package/dist/interface/assets/CuiCameraRecordings-cr_gatvr.js.br +0 -0
  129. package/dist/interface/assets/CuiCameraRecordings-cr_gatvr.js.gz +0 -0
  130. package/dist/interface/assets/CuiCameraShares-Bs0rA-xA.js +1 -0
  131. package/dist/interface/assets/CuiCameraShares-Bs0rA-xA.js.br +0 -0
  132. package/dist/interface/assets/CuiCameraShares-Bs0rA-xA.js.gz +0 -0
  133. package/dist/interface/assets/CuiCameraTable-CMQYM_iK.js +2 -0
  134. package/dist/interface/assets/CuiCameraTable-CMQYM_iK.js.br +0 -0
  135. package/dist/interface/assets/CuiCameraTable-CMQYM_iK.js.gz +0 -0
  136. package/dist/interface/assets/CuiCameraTable-Zn0xVNzU.js +1 -0
  137. package/dist/interface/assets/CuiCameraTable-Zn0xVNzU.js.br +1 -0
  138. package/dist/interface/assets/CuiCameraTable-Zn0xVNzU.js.gz +0 -0
  139. package/dist/interface/assets/CuiChartTable-Dnr59YcD.js +1 -0
  140. package/dist/interface/assets/CuiChartTable-Dnr59YcD.js.br +0 -0
  141. package/dist/interface/assets/CuiChartTable-Dnr59YcD.js.gz +0 -0
  142. package/dist/interface/assets/CuiConsole-DvxwlxJS.js +5 -0
  143. package/dist/interface/assets/CuiConsole-DvxwlxJS.js.br +0 -0
  144. package/dist/interface/assets/CuiConsole-DvxwlxJS.js.gz +0 -0
  145. package/dist/interface/assets/CuiDataTable-UUvO2yqR.js +1 -0
  146. package/dist/interface/assets/CuiDataTable-UUvO2yqR.js.br +0 -0
  147. package/dist/interface/assets/CuiDataTable-UUvO2yqR.js.gz +0 -0
  148. package/dist/interface/assets/CuiLightControl-X27HMxwD.js +1 -0
  149. package/dist/interface/assets/CuiLightControl-X27HMxwD.js.br +0 -0
  150. package/dist/interface/assets/CuiLightControl-X27HMxwD.js.gz +0 -0
  151. package/dist/interface/assets/CuiMoveCodeForm-D8tUtI-h.js +1 -0
  152. package/dist/interface/assets/CuiMoveCodeForm-D8tUtI-h.js.br +0 -0
  153. package/dist/interface/assets/CuiMoveCodeForm-D8tUtI-h.js.gz +0 -0
  154. package/dist/interface/assets/CuiPluginCard-quiY2N5M.js +2 -0
  155. package/dist/interface/assets/CuiPluginCard-quiY2N5M.js.br +0 -0
  156. package/dist/interface/assets/CuiPluginCard-quiY2N5M.js.gz +0 -0
  157. package/dist/interface/assets/CuiPluginCategoryChip-TPE8kEfF.js +1 -0
  158. package/dist/interface/assets/CuiPluginCategoryChip-TPE8kEfF.js.br +0 -0
  159. package/dist/interface/assets/CuiPluginCategoryChip-TPE8kEfF.js.gz +0 -0
  160. package/dist/interface/assets/CuiPluginOAuthButton-OAaZwA8I.js +2 -0
  161. package/dist/interface/assets/CuiPluginOAuthButton-OAaZwA8I.js.br +0 -0
  162. package/dist/interface/assets/CuiPluginOAuthButton-OAaZwA8I.js.gz +0 -0
  163. package/dist/interface/assets/CuiPluginTrustBadge-DGE2Rk83.js +1 -0
  164. package/dist/interface/assets/CuiPluginTrustBadge-DGE2Rk83.js.br +0 -0
  165. package/dist/interface/assets/CuiPluginTrustBadge-DGE2Rk83.js.gz +0 -0
  166. package/dist/interface/assets/CuiQRCode-lR7MLV88.js +1 -0
  167. package/dist/interface/assets/CuiQRCode-lR7MLV88.js.br +0 -0
  168. package/dist/interface/assets/CuiQRCode-lR7MLV88.js.gz +0 -0
  169. package/dist/interface/assets/CuiSchema-XtfUw6ph.js +1 -0
  170. package/dist/interface/assets/CuiSchema-XtfUw6ph.js.br +0 -0
  171. package/dist/interface/assets/CuiSchema-XtfUw6ph.js.gz +0 -0
  172. package/dist/interface/assets/CuiTopNavbarItem-DTcQlETL.js +1 -0
  173. package/dist/interface/assets/CuiTopNavbarItem-DTcQlETL.js.br +0 -0
  174. package/dist/interface/assets/CuiTopNavbarItem-DTcQlETL.js.gz +0 -0
  175. package/dist/interface/assets/Faces-CTGfzxVA.js +1 -0
  176. package/dist/interface/assets/Faces-CTGfzxVA.js.br +0 -0
  177. package/dist/interface/assets/Faces-CTGfzxVA.js.gz +0 -0
  178. package/dist/interface/assets/FirstSteps-Co4eY2L1.js +1 -0
  179. package/dist/interface/assets/FirstSteps-Co4eY2L1.js.br +0 -0
  180. package/dist/interface/assets/FirstSteps-Co4eY2L1.js.gz +0 -0
  181. package/dist/interface/assets/Floorplan-DKNUfJ1z.js +1 -0
  182. package/dist/interface/assets/Floorplan-DKNUfJ1z.js.br +0 -0
  183. package/dist/interface/assets/Floorplan-DKNUfJ1z.js.gz +0 -0
  184. package/dist/interface/assets/Instances-s7y4bMLZ.js +1 -0
  185. package/dist/interface/assets/Instances-s7y4bMLZ.js.br +0 -0
  186. package/dist/interface/assets/Instances-s7y4bMLZ.js.gz +0 -0
  187. package/dist/interface/assets/Logs-LXnQHfYz.js +6 -0
  188. package/dist/interface/assets/Logs-LXnQHfYz.js.br +0 -0
  189. package/dist/interface/assets/Logs-LXnQHfYz.js.gz +0 -0
  190. package/dist/interface/assets/Menu-CUBh0as4.js +1 -0
  191. package/dist/interface/assets/Menu-CUBh0as4.js.br +0 -0
  192. package/dist/interface/assets/Menu-CUBh0as4.js.gz +0 -0
  193. package/dist/interface/assets/Metrics-BZeB3ViS.js +12 -0
  194. package/dist/interface/assets/Metrics-BZeB3ViS.js.br +0 -0
  195. package/dist/interface/assets/Metrics-BZeB3ViS.js.gz +0 -0
  196. package/dist/interface/assets/OAuthDeviceFlow-B4UyugZs.js +1 -0
  197. package/dist/interface/assets/OAuthDeviceFlow-B4UyugZs.js.br +0 -0
  198. package/dist/interface/assets/OAuthDeviceFlow-B4UyugZs.js.gz +0 -0
  199. package/dist/interface/assets/Plugin-tIUUIABi.js +1 -0
  200. package/dist/interface/assets/Plugin-tIUUIABi.js.br +0 -0
  201. package/dist/interface/assets/Plugin-tIUUIABi.js.gz +0 -0
  202. package/dist/interface/assets/PluginConsole-BNnLHoE0.js +7 -0
  203. package/dist/interface/assets/PluginConsole-BNnLHoE0.js.br +4 -0
  204. package/dist/interface/assets/PluginConsole-BNnLHoE0.js.gz +0 -0
  205. package/dist/interface/assets/PluginDetail-BNNHJ6VT.js +2 -0
  206. package/dist/interface/assets/PluginDetail-BNNHJ6VT.js.br +0 -0
  207. package/dist/interface/assets/PluginDetail-BNNHJ6VT.js.gz +0 -0
  208. package/dist/interface/assets/PluginSchema-BRhnXEuo.js +1 -0
  209. package/dist/interface/assets/PluginSchema-BRhnXEuo.js.br +0 -0
  210. package/dist/interface/assets/PluginSchema-BRhnXEuo.js.gz +0 -0
  211. package/dist/interface/assets/PluginSearch-C4enqdv2.js +2 -0
  212. package/dist/interface/assets/PluginSearch-C4enqdv2.js.br +0 -0
  213. package/dist/interface/assets/PluginSearch-C4enqdv2.js.gz +0 -0
  214. package/dist/interface/assets/PluginSearch-C7HjU4oV.js +1 -0
  215. package/dist/interface/assets/PluginSearch-C7HjU4oV.js.br +1 -0
  216. package/dist/interface/assets/PluginSearch-C7HjU4oV.js.gz +0 -0
  217. package/dist/interface/assets/PluginUpdateProgress-DpJR1951.js +1 -0
  218. package/dist/interface/assets/PluginUpdateProgress-DpJR1951.js.br +0 -0
  219. package/dist/interface/assets/PluginUpdateProgress-DpJR1951.js.gz +0 -0
  220. package/dist/interface/assets/Plugins-d4IPovAj.js +1 -0
  221. package/dist/interface/assets/Plugins-d4IPovAj.js.br +0 -0
  222. package/dist/interface/assets/Plugins-d4IPovAj.js.gz +0 -0
  223. package/dist/interface/assets/RecentEventsFilter-CjRJnw2W.js +1 -0
  224. package/dist/interface/assets/RecentEventsFilter-CjRJnw2W.js.br +0 -0
  225. package/dist/interface/assets/RecentEventsFilter-CjRJnw2W.js.gz +0 -0
  226. package/dist/interface/assets/Recordings-0XmIQL9c.js +1 -0
  227. package/dist/interface/assets/Recordings-0XmIQL9c.js.br +0 -0
  228. package/dist/interface/assets/Recordings-0XmIQL9c.js.gz +0 -0
  229. package/dist/interface/assets/SensorEdit-9nlxNGSS.js +1 -0
  230. package/dist/interface/assets/SensorEdit-9nlxNGSS.js.br +0 -0
  231. package/dist/interface/assets/SensorEdit-9nlxNGSS.js.gz +0 -0
  232. package/dist/interface/assets/SensorHistory-DHa4mmXp.js +1 -0
  233. package/dist/interface/assets/SensorHistory-DHa4mmXp.js.br +0 -0
  234. package/dist/interface/assets/SensorHistory-DHa4mmXp.js.gz +0 -0
  235. package/dist/interface/assets/Sensors-BkI7JNPH.js +2 -0
  236. package/dist/interface/assets/Sensors-BkI7JNPH.js.br +0 -0
  237. package/dist/interface/assets/Sensors-BkI7JNPH.js.gz +0 -0
  238. package/dist/interface/assets/Settings-B4-Rn_Fh.js +1 -0
  239. package/dist/interface/assets/Settings-B4-Rn_Fh.js.br +0 -0
  240. package/dist/interface/assets/Settings-B4-Rn_Fh.js.gz +0 -0
  241. package/dist/interface/assets/SettingsAccount-7Yd2_xCe.js +1 -0
  242. package/dist/interface/assets/SettingsAccount-7Yd2_xCe.js.br +0 -0
  243. package/dist/interface/assets/SettingsAccount-7Yd2_xCe.js.gz +0 -0
  244. package/dist/interface/assets/SettingsAccount-C5TbJjlz.css +1 -0
  245. package/dist/interface/assets/SettingsAccount-C5TbJjlz.css.br +0 -0
  246. package/dist/interface/assets/SettingsAccount-C5TbJjlz.css.gz +0 -0
  247. package/dist/interface/assets/SettingsAccount-Cp56tnsV.js +1 -0
  248. package/dist/interface/assets/SettingsAccount-Cp56tnsV.js.br +0 -0
  249. package/dist/interface/assets/SettingsAccount-Cp56tnsV.js.gz +0 -0
  250. package/dist/interface/assets/SettingsAppearance-BWnr6fl7.js +1 -0
  251. package/dist/interface/assets/SettingsAppearance-BWnr6fl7.js.br +0 -0
  252. package/dist/interface/assets/SettingsAppearance-BWnr6fl7.js.gz +0 -0
  253. package/dist/interface/assets/SettingsAppearance-Bna45w8P.js +1 -0
  254. package/dist/interface/assets/SettingsAppearance-Bna45w8P.js.br +0 -0
  255. package/dist/interface/assets/SettingsAppearance-Bna45w8P.js.gz +0 -0
  256. package/dist/interface/assets/SettingsAssistant-Dy_fTPCt.js +1 -0
  257. package/dist/interface/assets/SettingsAssistant-Dy_fTPCt.js.br +0 -0
  258. package/dist/interface/assets/SettingsAssistant-Dy_fTPCt.js.gz +0 -0
  259. package/dist/interface/assets/SettingsBackup-DCW1lM-Z.js +1 -0
  260. package/dist/interface/assets/SettingsBackup-DCW1lM-Z.js.br +0 -0
  261. package/dist/interface/assets/SettingsBackup-DCW1lM-Z.js.gz +0 -0
  262. package/dist/interface/assets/SettingsList-DcvafqMi.js +1 -0
  263. package/dist/interface/assets/SettingsList-DcvafqMi.js.br +0 -0
  264. package/dist/interface/assets/SettingsList-DcvafqMi.js.gz +0 -0
  265. package/dist/interface/assets/SettingsMqtt-BuPLGzAI.js +1 -0
  266. package/dist/interface/assets/SettingsMqtt-BuPLGzAI.js.br +0 -0
  267. package/dist/interface/assets/SettingsMqtt-BuPLGzAI.js.gz +0 -0
  268. package/dist/interface/assets/SettingsNotifications-CtXPMwEL.js +1 -0
  269. package/dist/interface/assets/SettingsNotifications-CtXPMwEL.js.br +0 -0
  270. package/dist/interface/assets/SettingsNotifications-CtXPMwEL.js.gz +0 -0
  271. package/dist/interface/assets/SettingsPermissions-CskWvMC-.js +1 -0
  272. package/dist/interface/assets/SettingsPermissions-CskWvMC-.js.br +0 -0
  273. package/dist/interface/assets/SettingsPermissions-CskWvMC-.js.gz +0 -0
  274. package/dist/interface/assets/SettingsRecordings-Cw3hykd4.js +1 -0
  275. package/dist/interface/assets/SettingsRecordings-Cw3hykd4.js.br +0 -0
  276. package/dist/interface/assets/SettingsRecordings-Cw3hykd4.js.gz +0 -0
  277. package/dist/interface/assets/SettingsRemote-C5pT1BpH.js +2 -0
  278. package/dist/interface/assets/SettingsRemote-C5pT1BpH.js.br +0 -0
  279. package/dist/interface/assets/SettingsRemote-C5pT1BpH.js.gz +0 -0
  280. package/dist/interface/assets/SettingsSystem-CPpKePdR.js +2 -0
  281. package/dist/interface/assets/SettingsSystem-CPpKePdR.js.br +0 -0
  282. package/dist/interface/assets/SettingsSystem-CPpKePdR.js.gz +0 -0
  283. package/dist/interface/assets/SettingsUsers-DHyf9Fh2.js +1 -0
  284. package/dist/interface/assets/SettingsUsers-DHyf9Fh2.js.br +0 -0
  285. package/dist/interface/assets/SettingsUsers-DHyf9Fh2.js.gz +0 -0
  286. package/dist/interface/assets/Terminal-PSzgxyUK.js +4 -0
  287. package/dist/interface/assets/Terminal-PSzgxyUK.js.br +0 -0
  288. package/dist/interface/assets/Terminal-PSzgxyUK.js.gz +0 -0
  289. package/dist/interface/assets/Training-B8y5oggc.js +1 -0
  290. package/dist/interface/assets/Training-B8y5oggc.js.br +0 -0
  291. package/dist/interface/assets/Training-B8y5oggc.js.gz +0 -0
  292. package/dist/interface/assets/Updates-DM0Q2isz.js +2 -0
  293. package/dist/interface/assets/Updates-DM0Q2isz.js.br +0 -0
  294. package/dist/interface/assets/Updates-DM0Q2isz.js.gz +0 -0
  295. package/dist/interface/assets/VariableInput-CEEKNFMa.js +1 -0
  296. package/dist/interface/assets/VariableInput-CEEKNFMa.js.br +0 -0
  297. package/dist/interface/assets/VariableInput-CEEKNFMa.js.gz +0 -0
  298. package/dist/interface/assets/VersionsHandler-BX_H4fKa.js +7 -0
  299. package/dist/interface/assets/VersionsHandler-BX_H4fKa.js.br +0 -0
  300. package/dist/interface/assets/VersionsHandler-BX_H4fKa.js.gz +0 -0
  301. package/dist/interface/assets/VirtualSensorCreate-V1Ge5Ua1.js +1 -0
  302. package/dist/interface/assets/VirtualSensorCreate-V1Ge5Ua1.js.br +0 -0
  303. package/dist/interface/assets/VirtualSensorCreate-V1Ge5Ua1.js.gz +0 -0
  304. package/dist/interface/assets/Workers-CuICoAdY.js +2 -0
  305. package/dist/interface/assets/Workers-CuICoAdY.js.br +0 -0
  306. package/dist/interface/assets/Workers-CuICoAdY.js.gz +0 -0
  307. package/dist/interface/assets/api-DrGMJXjR.js +1 -0
  308. package/dist/interface/assets/api-DrGMJXjR.js.br +0 -0
  309. package/dist/interface/assets/api-DrGMJXjR.js.gz +0 -0
  310. package/dist/interface/assets/api-TS3iFwhp.js +4 -0
  311. package/dist/interface/assets/api-TS3iFwhp.js.br +0 -0
  312. package/dist/interface/assets/api-TS3iFwhp.js.gz +0 -0
  313. package/dist/interface/assets/api-bOGDCWMd.css +2 -0
  314. package/dist/interface/assets/api-bOGDCWMd.css.br +0 -0
  315. package/dist/interface/assets/api-bOGDCWMd.css.gz +0 -0
  316. package/dist/interface/assets/assistant-BPhn6mY4.js +1 -0
  317. package/dist/interface/assets/assistant-BPhn6mY4.js.br +0 -0
  318. package/dist/interface/assets/assistant-BPhn6mY4.js.gz +0 -0
  319. package/dist/interface/assets/auth-DIf-rMc2.js +1 -0
  320. package/dist/interface/assets/auth-DIf-rMc2.js.br +0 -0
  321. package/dist/interface/assets/auth-DIf-rMc2.js.gz +0 -0
  322. package/dist/interface/assets/automations-LH5xFY2x.js +1 -0
  323. package/dist/interface/assets/automations-LH5xFY2x.js.br +0 -0
  324. package/dist/interface/assets/automations-LH5xFY2x.js.gz +0 -0
  325. package/dist/interface/assets/cameraSources-DlHjs6nH.js +1 -0
  326. package/dist/interface/assets/cameraSources-DlHjs6nH.js.br +0 -0
  327. package/dist/interface/assets/cameraSources-DlHjs6nH.js.gz +0 -0
  328. package/dist/interface/assets/cloudHandoff-CVVUJLRm.js +1 -0
  329. package/dist/interface/assets/cloudHandoff-CVVUJLRm.js.br +1 -0
  330. package/dist/interface/assets/cloudHandoff-CVVUJLRm.js.gz +0 -0
  331. package/dist/interface/assets/config-DN5_kiR_.js +1 -0
  332. package/dist/interface/assets/config-DN5_kiR_.js.br +0 -0
  333. package/dist/interface/assets/config-DN5_kiR_.js.gz +0 -0
  334. package/dist/interface/assets/flowSchema-Bmgr2vJ6.js +1 -0
  335. package/dist/interface/assets/flowSchema-Bmgr2vJ6.js.br +0 -0
  336. package/dist/interface/assets/flowSchema-Bmgr2vJ6.js.gz +0 -0
  337. package/dist/interface/assets/intercomService-Dmpnyb2n.js +1 -0
  338. package/dist/interface/assets/intercomService-Dmpnyb2n.js.br +0 -0
  339. package/dist/interface/assets/intercomService-Dmpnyb2n.js.gz +0 -0
  340. package/dist/interface/assets/main-BsuMuoqd.js +3 -0
  341. package/dist/interface/assets/main-BsuMuoqd.js.br +0 -0
  342. package/dist/interface/assets/main-BsuMuoqd.js.gz +0 -0
  343. package/dist/interface/assets/mqtt-BQ12PoIy.js +1 -0
  344. package/dist/interface/assets/mqtt-BQ12PoIy.js.br +3 -0
  345. package/dist/interface/assets/mqtt-BQ12PoIy.js.gz +0 -0
  346. package/dist/interface/assets/notifications-BboM5nUI.js +1 -0
  347. package/dist/interface/assets/notifications-BboM5nUI.js.br +0 -0
  348. package/dist/interface/assets/notifications-BboM5nUI.js.gz +0 -0
  349. package/dist/interface/assets/plugins-DYcMG0ja.js +1 -0
  350. package/dist/interface/assets/plugins-DYcMG0ja.js.br +0 -0
  351. package/dist/interface/assets/plugins-DYcMG0ja.js.gz +0 -0
  352. package/dist/interface/assets/rooms-D19GFS0T.js +1 -0
  353. package/dist/interface/assets/rooms-D19GFS0T.js.br +4 -0
  354. package/dist/interface/assets/rooms-D19GFS0T.js.gz +0 -0
  355. package/dist/interface/assets/sensorIcons-CG4oAxFn.js +1 -0
  356. package/dist/interface/assets/sensorIcons-CG4oAxFn.js.br +0 -0
  357. package/dist/interface/assets/sensorIcons-CG4oAxFn.js.gz +0 -0
  358. package/dist/interface/assets/sensorSlots-W8up8ZKb.js +1 -0
  359. package/dist/interface/assets/sensorSlots-W8up8ZKb.js.br +1 -0
  360. package/dist/interface/assets/sensorSlots-W8up8ZKb.js.gz +0 -0
  361. package/dist/interface/assets/sensors-BV0Trrvb.js +1 -0
  362. package/dist/interface/assets/sensors-BV0Trrvb.js.br +0 -0
  363. package/dist/interface/assets/sensors-BV0Trrvb.js.gz +0 -0
  364. package/dist/interface/assets/server-DK3nj5jh.js +1 -0
  365. package/dist/interface/assets/server-DK3nj5jh.js.br +0 -0
  366. package/dist/interface/assets/server-DK3nj5jh.js.gz +0 -0
  367. package/dist/interface/assets/theme-1Z9zEekC.js +1 -0
  368. package/dist/interface/assets/theme-1Z9zEekC.js.br +0 -0
  369. package/dist/interface/assets/theme-1Z9zEekC.js.gz +0 -0
  370. package/dist/interface/assets/types-B2RZ2n73.js +1 -0
  371. package/dist/interface/assets/types-B2RZ2n73.js.br +0 -0
  372. package/dist/interface/assets/types-B2RZ2n73.js.gz +0 -0
  373. package/dist/interface/assets/types-C4g382tE.js +1 -0
  374. package/dist/interface/assets/types-C4g382tE.js.br +0 -0
  375. package/dist/interface/assets/types-C4g382tE.js.gz +0 -0
  376. package/dist/interface/assets/types-CWY4rVYg.js +1 -0
  377. package/dist/interface/assets/types-CWY4rVYg.js.br +0 -0
  378. package/dist/interface/assets/types-CWY4rVYg.js.gz +0 -0
  379. package/dist/interface/assets/updates-C8KPyVl4.js +2 -0
  380. package/dist/interface/assets/updates-C8KPyVl4.js.br +0 -0
  381. package/dist/interface/assets/updates-C8KPyVl4.js.gz +0 -0
  382. package/dist/interface/assets/useAppVersion-B5cYcDW5.js +1 -0
  383. package/dist/interface/assets/useAppVersion-B5cYcDW5.js.br +0 -0
  384. package/dist/interface/assets/useAppVersion-B5cYcDW5.js.gz +0 -0
  385. package/dist/interface/assets/useCameraOptions-DnC7rx7V.js +1 -0
  386. package/dist/interface/assets/useCameraOptions-DnC7rx7V.js.br +0 -0
  387. package/dist/interface/assets/useCameraOptions-DnC7rx7V.js.gz +0 -0
  388. package/dist/interface/assets/useKeyboardInset-DzWAPvpD.js +1 -0
  389. package/dist/interface/assets/useKeyboardInset-DzWAPvpD.js.br +0 -0
  390. package/dist/interface/assets/useKeyboardInset-DzWAPvpD.js.gz +0 -0
  391. package/dist/interface/assets/useLogsSocket-DRCl-9rN.js +1 -0
  392. package/dist/interface/assets/useLogsSocket-DRCl-9rN.js.br +0 -0
  393. package/dist/interface/assets/useLogsSocket-DRCl-9rN.js.gz +0 -0
  394. package/dist/interface/assets/useNavLayout-DNoKsxiD.js +1 -0
  395. package/dist/interface/assets/useNavLayout-DNoKsxiD.js.br +0 -0
  396. package/dist/interface/assets/useNavLayout-DNoKsxiD.js.gz +0 -0
  397. package/dist/interface/assets/usePermissions-BhN_r4yu.js +1 -0
  398. package/dist/interface/assets/usePermissions-BhN_r4yu.js.br +0 -0
  399. package/dist/interface/assets/usePermissions-BhN_r4yu.js.gz +0 -0
  400. package/dist/interface/assets/usePlacementLabel-Dnd48LTa.js +1 -0
  401. package/dist/interface/assets/usePlacementLabel-Dnd48LTa.js.br +0 -0
  402. package/dist/interface/assets/usePlacementLabel-Dnd48LTa.js.gz +0 -0
  403. package/dist/interface/assets/usePlacementSocket-DyS_dG0O.js +1 -0
  404. package/dist/interface/assets/usePlacementSocket-DyS_dG0O.js.br +0 -0
  405. package/dist/interface/assets/usePlacementSocket-DyS_dG0O.js.gz +0 -0
  406. package/dist/interface/assets/usePluginUpdates-C9KRZVbI.js +1 -0
  407. package/dist/interface/assets/usePluginUpdates-C9KRZVbI.js.br +0 -0
  408. package/dist/interface/assets/usePluginUpdates-C9KRZVbI.js.gz +0 -0
  409. package/dist/interface/assets/usePluginsSocket-Dkrwgo_a.js +1 -0
  410. package/dist/interface/assets/usePluginsSocket-Dkrwgo_a.js.br +0 -0
  411. package/dist/interface/assets/usePluginsSocket-Dkrwgo_a.js.gz +0 -0
  412. package/dist/interface/assets/usePushRegistration-419l1m2J.js +2 -0
  413. package/dist/interface/assets/usePushRegistration-419l1m2J.js.br +0 -0
  414. package/dist/interface/assets/usePushRegistration-419l1m2J.js.gz +0 -0
  415. package/dist/interface/assets/useServerRestart-svjj-eQC.js +1 -0
  416. package/dist/interface/assets/useServerRestart-svjj-eQC.js.br +0 -0
  417. package/dist/interface/assets/useServerRestart-svjj-eQC.js.gz +0 -0
  418. package/dist/interface/assets/useTabSwipe-Bdr_wGrE.js +1 -0
  419. package/dist/interface/assets/useTabSwipe-Bdr_wGrE.js.br +0 -0
  420. package/dist/interface/assets/useTabSwipe-Bdr_wGrE.js.gz +0 -0
  421. package/dist/interface/assets/useUpdateChannel-DWQGivak.js +1 -0
  422. package/dist/interface/assets/useUpdateChannel-DWQGivak.js.br +0 -0
  423. package/dist/interface/assets/useUpdateChannel-DWQGivak.js.gz +0 -0
  424. package/dist/interface/assets/useUpdatesSocket-CjZRZpB7.js +1 -0
  425. package/dist/interface/assets/useUpdatesSocket-CjZRZpB7.js.br +0 -0
  426. package/dist/interface/assets/useUpdatesSocket-CjZRZpB7.js.gz +0 -0
  427. package/dist/interface/assets/useUserOptions-DrUS3Xze.js +1 -0
  428. package/dist/interface/assets/useUserOptions-DrUS3Xze.js.br +0 -0
  429. package/dist/interface/assets/useUserOptions-DrUS3Xze.js.gz +0 -0
  430. package/dist/interface/assets/vendor-cameraui-CA-wgTPI.css +1 -0
  431. package/dist/interface/assets/vendor-cameraui-CA-wgTPI.css.br +0 -0
  432. package/dist/interface/assets/vendor-cameraui-CA-wgTPI.css.gz +0 -0
  433. package/dist/interface/assets/vendor-cameraui-q2C0VTDU.js +8 -0
  434. package/dist/interface/assets/vendor-cameraui-q2C0VTDU.js.br +0 -0
  435. package/dist/interface/assets/vendor-cameraui-q2C0VTDU.js.gz +0 -0
  436. package/dist/interface/ha/404-Bl_FHw4S.js +60 -0
  437. package/dist/interface/ha/404-Bl_FHw4S.js.br +0 -0
  438. package/dist/interface/ha/404-Bl_FHw4S.js.gz +0 -0
  439. package/dist/interface/ha/About-C3LSLeW5.js +55 -0
  440. package/dist/interface/ha/About-C3LSLeW5.js.br +0 -0
  441. package/dist/interface/ha/About-C3LSLeW5.js.gz +0 -0
  442. package/dist/interface/ha/Assistant-BqTjwNPd.js +11071 -0
  443. package/dist/interface/ha/Assistant-BqTjwNPd.js.br +0 -0
  444. package/dist/interface/ha/Assistant-BqTjwNPd.js.gz +0 -0
  445. package/dist/interface/ha/Automation-Bt8_5TEo.js +2009 -0
  446. package/dist/interface/ha/Automation-Bt8_5TEo.js.br +0 -0
  447. package/dist/interface/ha/Automation-Bt8_5TEo.js.gz +0 -0
  448. package/dist/interface/ha/AutomationImport-CFunRLgJ.js +206 -0
  449. package/dist/interface/ha/AutomationImport-CFunRLgJ.js.br +0 -0
  450. package/dist/interface/ha/AutomationImport-CFunRLgJ.js.gz +0 -0
  451. package/dist/interface/ha/AutomationStore-C4DEABhG.js +336 -0
  452. package/dist/interface/ha/AutomationStore-C4DEABhG.js.br +0 -0
  453. package/dist/interface/ha/AutomationStore-C4DEABhG.js.gz +0 -0
  454. package/dist/interface/ha/AutomationStoreDetail-CfoXG1MC.js +121 -0
  455. package/dist/interface/ha/AutomationStoreDetail-CfoXG1MC.js.br +0 -0
  456. package/dist/interface/ha/AutomationStoreDetail-CfoXG1MC.js.gz +0 -0
  457. package/dist/interface/ha/Automations-BQ2bkyX_.js +423 -0
  458. package/dist/interface/ha/Automations-BQ2bkyX_.js.br +0 -0
  459. package/dist/interface/ha/Automations-BQ2bkyX_.js.gz +0 -0
  460. package/dist/interface/ha/Camera-B2B4G4od.js +740 -0
  461. package/dist/interface/ha/Camera-B2B4G4od.js.br +0 -0
  462. package/dist/interface/ha/Camera-B2B4G4od.js.gz +0 -0
  463. package/dist/interface/ha/CameraConsole-xFpvIWw_.js +52 -0
  464. package/dist/interface/ha/CameraConsole-xFpvIWw_.js.br +0 -0
  465. package/dist/interface/ha/CameraConsole-xFpvIWw_.js.gz +0 -0
  466. package/dist/interface/ha/Cameras-mQarYEDa.js +1934 -0
  467. package/dist/interface/ha/Cameras-mQarYEDa.js.br +0 -0
  468. package/dist/interface/ha/Cameras-mQarYEDa.js.gz +0 -0
  469. package/dist/interface/ha/Camview-Cxi3KsgB.js +1368 -0
  470. package/dist/interface/ha/Camview-Cxi3KsgB.js.br +0 -0
  471. package/dist/interface/ha/Camview-Cxi3KsgB.js.gz +0 -0
  472. package/dist/interface/ha/Config-CDmB9WLz.js +354 -0
  473. package/dist/interface/ha/Config-CDmB9WLz.js.br +0 -0
  474. package/dist/interface/ha/Config-CDmB9WLz.js.gz +0 -0
  475. package/dist/interface/ha/ConfigActionAssistant-4bgOEfWc.js +182 -0
  476. package/dist/interface/ha/ConfigActionAssistant-4bgOEfWc.js.br +0 -0
  477. package/dist/interface/ha/ConfigActionAssistant-4bgOEfWc.js.gz +0 -0
  478. package/dist/interface/ha/ConfigActionCameraControl-DHb4TaeM.js +152 -0
  479. package/dist/interface/ha/ConfigActionCameraControl-DHb4TaeM.js.br +0 -0
  480. package/dist/interface/ha/ConfigActionCameraControl-DHb4TaeM.js.gz +0 -0
  481. package/dist/interface/ha/ConfigActionHttp-Cf-KsMnx.js +134 -0
  482. package/dist/interface/ha/ConfigActionHttp-Cf-KsMnx.js.br +0 -0
  483. package/dist/interface/ha/ConfigActionHttp-Cf-KsMnx.js.gz +0 -0
  484. package/dist/interface/ha/ConfigActionImageInput-BojghoLq.js +170 -0
  485. package/dist/interface/ha/ConfigActionImageInput-BojghoLq.js.br +0 -0
  486. package/dist/interface/ha/ConfigActionImageInput-BojghoLq.js.gz +0 -0
  487. package/dist/interface/ha/ConfigActionMqtt-8I3xv7mW.js +78 -0
  488. package/dist/interface/ha/ConfigActionMqtt-8I3xv7mW.js.br +0 -0
  489. package/dist/interface/ha/ConfigActionMqtt-8I3xv7mW.js.gz +0 -0
  490. package/dist/interface/ha/ConfigActionNotification-CJ8AMA6m.js +198 -0
  491. package/dist/interface/ha/ConfigActionNotification-CJ8AMA6m.js.br +0 -0
  492. package/dist/interface/ha/ConfigActionNotification-CJ8AMA6m.js.gz +0 -0
  493. package/dist/interface/ha/ConfigActionNotificationControl-Jmal0N0N.js +111 -0
  494. package/dist/interface/ha/ConfigActionNotificationControl-Jmal0N0N.js.br +0 -0
  495. package/dist/interface/ha/ConfigActionNotificationControl-Jmal0N0N.js.gz +0 -0
  496. package/dist/interface/ha/ConfigActionOutput-Tjb8_M61.js +98 -0
  497. package/dist/interface/ha/ConfigActionOutput-Tjb8_M61.js.br +0 -0
  498. package/dist/interface/ha/ConfigActionOutput-Tjb8_M61.js.gz +0 -0
  499. package/dist/interface/ha/ConfigActionPlugin-BIZABnsj.js +171 -0
  500. package/dist/interface/ha/ConfigActionPlugin-BIZABnsj.js.br +0 -0
  501. package/dist/interface/ha/ConfigActionPlugin-BIZABnsj.js.gz +0 -0
  502. package/dist/interface/ha/ConfigActionSensor-CfdmXrhC.js +138 -0
  503. package/dist/interface/ha/ConfigActionSensor-CfdmXrhC.js.br +0 -0
  504. package/dist/interface/ha/ConfigActionSensor-CfdmXrhC.js.gz +0 -0
  505. package/dist/interface/ha/ConfigActionSnapshot-DZfadRAR.js +45 -0
  506. package/dist/interface/ha/ConfigActionSnapshot-DZfadRAR.js.br +0 -0
  507. package/dist/interface/ha/ConfigActionSnapshot-DZfadRAR.js.gz +0 -0
  508. package/dist/interface/ha/ConfigActionVariable-Bs6GAr-a.js +88 -0
  509. package/dist/interface/ha/ConfigActionVariable-Bs6GAr-a.js.br +0 -0
  510. package/dist/interface/ha/ConfigActionVariable-Bs6GAr-a.js.gz +0 -0
  511. package/dist/interface/ha/ConfigConditionIfElse-Djycw0JW.js +124 -0
  512. package/dist/interface/ha/ConfigConditionIfElse-Djycw0JW.js.br +0 -0
  513. package/dist/interface/ha/ConfigConditionIfElse-Djycw0JW.js.gz +0 -0
  514. package/dist/interface/ha/ConfigConditionSensorState-CMU8ZqA0.js +190 -0
  515. package/dist/interface/ha/ConfigConditionSensorState-CMU8ZqA0.js.br +0 -0
  516. package/dist/interface/ha/ConfigConditionSensorState-CMU8ZqA0.js.gz +0 -0
  517. package/dist/interface/ha/ConfigConditionSwitch-CgJOmOpa.js +128 -0
  518. package/dist/interface/ha/ConfigConditionSwitch-CgJOmOpa.js.br +0 -0
  519. package/dist/interface/ha/ConfigConditionSwitch-CgJOmOpa.js.gz +0 -0
  520. package/dist/interface/ha/ConfigSensorValueInput-Bf6Ok4pI.js +173 -0
  521. package/dist/interface/ha/ConfigSensorValueInput-Bf6Ok4pI.js.br +0 -0
  522. package/dist/interface/ha/ConfigSensorValueInput-Bf6Ok4pI.js.gz +0 -0
  523. package/dist/interface/ha/ConfigTriggerDetection-Dw1CThHp.js +241 -0
  524. package/dist/interface/ha/ConfigTriggerDetection-Dw1CThHp.js.br +0 -0
  525. package/dist/interface/ha/ConfigTriggerDetection-Dw1CThHp.js.gz +0 -0
  526. package/dist/interface/ha/ConfigTriggerGeofence-DdPHZNwK.js +4836 -0
  527. package/dist/interface/ha/ConfigTriggerGeofence-DdPHZNwK.js.br +0 -0
  528. package/dist/interface/ha/ConfigTriggerGeofence-DdPHZNwK.js.gz +0 -0
  529. package/dist/interface/ha/ConfigTriggerMqtt-Ca5Nyw3p.js +134 -0
  530. package/dist/interface/ha/ConfigTriggerMqtt-Ca5Nyw3p.js.br +0 -0
  531. package/dist/interface/ha/ConfigTriggerMqtt-Ca5Nyw3p.js.gz +0 -0
  532. package/dist/interface/ha/ConfigTriggerSensor-D9HhDzY2.js +73 -0
  533. package/dist/interface/ha/ConfigTriggerSensor-D9HhDzY2.js.br +0 -0
  534. package/dist/interface/ha/ConfigTriggerSensor-D9HhDzY2.js.gz +0 -0
  535. package/dist/interface/ha/ConfigTriggerSystem-DwjzJNEe.js +192 -0
  536. package/dist/interface/ha/ConfigTriggerSystem-DwjzJNEe.js.br +0 -0
  537. package/dist/interface/ha/ConfigTriggerSystem-DwjzJNEe.js.gz +0 -0
  538. package/dist/interface/ha/ConfigTriggerWebhook-BH4Tf_gl.js +94 -0
  539. package/dist/interface/ha/ConfigTriggerWebhook-BH4Tf_gl.js.br +0 -0
  540. package/dist/interface/ha/ConfigTriggerWebhook-BH4Tf_gl.js.gz +0 -0
  541. package/dist/interface/ha/Console--Fph-cv4.js +347 -0
  542. package/dist/interface/ha/Console--Fph-cv4.js.br +0 -0
  543. package/dist/interface/ha/Console--Fph-cv4.js.gz +0 -0
  544. package/dist/interface/ha/CuiAutomationRunsDialog-DTa_aU4B.js +129 -0
  545. package/dist/interface/ha/CuiAutomationRunsDialog-DTa_aU4B.js.br +0 -0
  546. package/dist/interface/ha/CuiAutomationRunsDialog-DTa_aU4B.js.gz +0 -0
  547. package/dist/interface/ha/CuiBottomSheet-C06koVFt.js +270 -0
  548. package/dist/interface/ha/CuiBottomSheet-C06koVFt.js.br +0 -0
  549. package/dist/interface/ha/CuiBottomSheet-C06koVFt.js.gz +0 -0
  550. package/dist/interface/ha/CuiCameraRecordings-DanVT3UG.js +575 -0
  551. package/dist/interface/ha/CuiCameraRecordings-DanVT3UG.js.br +0 -0
  552. package/dist/interface/ha/CuiCameraRecordings-DanVT3UG.js.gz +0 -0
  553. package/dist/interface/ha/CuiCameraShares-BdodP9gm.js +120 -0
  554. package/dist/interface/ha/CuiCameraShares-BdodP9gm.js.br +0 -0
  555. package/dist/interface/ha/CuiCameraShares-BdodP9gm.js.gz +0 -0
  556. package/dist/interface/ha/CuiCameraTable-DwKFkM_u.js +105 -0
  557. package/dist/interface/ha/CuiCameraTable-DwKFkM_u.js.br +0 -0
  558. package/dist/interface/ha/CuiCameraTable-DwKFkM_u.js.gz +0 -0
  559. package/dist/interface/ha/CuiChartTable-DRXQfdr1.js +6797 -0
  560. package/dist/interface/ha/CuiChartTable-DRXQfdr1.js.br +0 -0
  561. package/dist/interface/ha/CuiChartTable-DRXQfdr1.js.gz +0 -0
  562. package/dist/interface/ha/CuiConsole-Doi5SxCk.js +234 -0
  563. package/dist/interface/ha/CuiConsole-Doi5SxCk.js.br +0 -0
  564. package/dist/interface/ha/CuiConsole-Doi5SxCk.js.gz +0 -0
  565. package/dist/interface/ha/CuiDataTable-BFI82F5E.js +8860 -0
  566. package/dist/interface/ha/CuiDataTable-BFI82F5E.js.br +0 -0
  567. package/dist/interface/ha/CuiDataTable-BFI82F5E.js.gz +0 -0
  568. package/dist/interface/ha/CuiMoveCodeForm-CnghWGPw.js +89 -0
  569. package/dist/interface/ha/CuiMoveCodeForm-CnghWGPw.js.br +0 -0
  570. package/dist/interface/ha/CuiMoveCodeForm-CnghWGPw.js.gz +0 -0
  571. package/dist/interface/ha/CuiPluginCard-CGgQxSl7.js +778 -0
  572. package/dist/interface/ha/CuiPluginCard-CGgQxSl7.js.br +0 -0
  573. package/dist/interface/ha/CuiPluginCard-CGgQxSl7.js.gz +0 -0
  574. package/dist/interface/ha/CuiPluginOAuthButton-D8FJdgy9.js +74 -0
  575. package/dist/interface/ha/CuiPluginOAuthButton-D8FJdgy9.js.br +0 -0
  576. package/dist/interface/ha/CuiPluginOAuthButton-D8FJdgy9.js.gz +0 -0
  577. package/dist/interface/ha/CuiPluginTrustBadge-BnxyMthN.js +122 -0
  578. package/dist/interface/ha/CuiPluginTrustBadge-BnxyMthN.js.br +0 -0
  579. package/dist/interface/ha/CuiPluginTrustBadge-BnxyMthN.js.gz +0 -0
  580. package/dist/interface/ha/CuiQRCode-JSiQ_0PB.js +86 -0
  581. package/dist/interface/ha/CuiQRCode-JSiQ_0PB.js.br +0 -0
  582. package/dist/interface/ha/CuiQRCode-JSiQ_0PB.js.gz +0 -0
  583. package/dist/interface/ha/CuiSchema-DWR2Cu3w.js +952 -0
  584. package/dist/interface/ha/CuiSchema-DWR2Cu3w.js.br +0 -0
  585. package/dist/interface/ha/CuiSchema-DWR2Cu3w.js.gz +0 -0
  586. package/dist/interface/ha/CuiTopNavbarItem-DkijXvxk.js +141 -0
  587. package/dist/interface/ha/CuiTopNavbarItem-DkijXvxk.js.br +0 -0
  588. package/dist/interface/ha/CuiTopNavbarItem-DkijXvxk.js.gz +0 -0
  589. package/dist/interface/ha/Faces-foOYGnts.js +1659 -0
  590. package/dist/interface/ha/Faces-foOYGnts.js.br +0 -0
  591. package/dist/interface/ha/Faces-foOYGnts.js.gz +0 -0
  592. package/dist/interface/ha/FirstSteps-BC5oP04p.js +254 -0
  593. package/dist/interface/ha/FirstSteps-BC5oP04p.js.br +0 -0
  594. package/dist/interface/ha/FirstSteps-BC5oP04p.js.gz +0 -0
  595. package/dist/interface/ha/Floorplan-CBHoSOlq.js +3707 -0
  596. package/dist/interface/ha/Floorplan-CBHoSOlq.js.br +0 -0
  597. package/dist/interface/ha/Floorplan-CBHoSOlq.js.gz +0 -0
  598. package/dist/interface/ha/Instances-Bl_4pf1g.js +670 -0
  599. package/dist/interface/ha/Instances-Bl_4pf1g.js.br +0 -0
  600. package/dist/interface/ha/Instances-Bl_4pf1g.js.gz +0 -0
  601. package/dist/interface/ha/Logs-CqcG22n0.js +417 -0
  602. package/dist/interface/ha/Logs-CqcG22n0.js.br +0 -0
  603. package/dist/interface/ha/Logs-CqcG22n0.js.gz +0 -0
  604. package/dist/interface/ha/Menu-K235AI9d.js +159 -0
  605. package/dist/interface/ha/Menu-K235AI9d.js.br +0 -0
  606. package/dist/interface/ha/Menu-K235AI9d.js.gz +0 -0
  607. package/dist/interface/ha/Metrics-B9LmdPb8.js +1484 -0
  608. package/dist/interface/ha/Metrics-B9LmdPb8.js.br +0 -0
  609. package/dist/interface/ha/Metrics-B9LmdPb8.js.gz +0 -0
  610. package/dist/interface/ha/OAuthDeviceFlow-BRfNQrto.js +112 -0
  611. package/dist/interface/ha/OAuthDeviceFlow-BRfNQrto.js.br +0 -0
  612. package/dist/interface/ha/OAuthDeviceFlow-BRfNQrto.js.gz +0 -0
  613. package/dist/interface/ha/Plugin-DOfqAHfD.js +1746 -0
  614. package/dist/interface/ha/Plugin-DOfqAHfD.js.br +0 -0
  615. package/dist/interface/ha/Plugin-DOfqAHfD.js.gz +0 -0
  616. package/dist/interface/ha/PluginConsole-HCq1TtGP.js +56 -0
  617. package/dist/interface/ha/PluginConsole-HCq1TtGP.js.br +0 -0
  618. package/dist/interface/ha/PluginConsole-HCq1TtGP.js.gz +0 -0
  619. package/dist/interface/ha/PluginDetail-qmjMJZ6-.js +804 -0
  620. package/dist/interface/ha/PluginDetail-qmjMJZ6-.js.br +0 -0
  621. package/dist/interface/ha/PluginDetail-qmjMJZ6-.js.gz +0 -0
  622. package/dist/interface/ha/PluginSearch-CLuHh8A_.js +462 -0
  623. package/dist/interface/ha/PluginSearch-CLuHh8A_.js.br +0 -0
  624. package/dist/interface/ha/PluginSearch-CLuHh8A_.js.gz +0 -0
  625. package/dist/interface/ha/PluginUpdateProgress-ZUrKsC_B.js +34 -0
  626. package/dist/interface/ha/PluginUpdateProgress-ZUrKsC_B.js.br +0 -0
  627. package/dist/interface/ha/PluginUpdateProgress-ZUrKsC_B.js.gz +0 -0
  628. package/dist/interface/ha/Plugins-BzLdxtIt.js +697 -0
  629. package/dist/interface/ha/Plugins-BzLdxtIt.js.br +0 -0
  630. package/dist/interface/ha/Plugins-BzLdxtIt.js.gz +0 -0
  631. package/dist/interface/ha/Recordings-CIFmOqK1.js +1916 -0
  632. package/dist/interface/ha/Recordings-CIFmOqK1.js.br +0 -0
  633. package/dist/interface/ha/Recordings-CIFmOqK1.js.gz +0 -0
  634. package/dist/interface/ha/SensorEdit-NmH5BT2-.js +1466 -0
  635. package/dist/interface/ha/SensorEdit-NmH5BT2-.js.br +0 -0
  636. package/dist/interface/ha/SensorEdit-NmH5BT2-.js.gz +0 -0
  637. package/dist/interface/ha/SensorHistory-yJNAhOsS.js +54 -0
  638. package/dist/interface/ha/SensorHistory-yJNAhOsS.js.br +0 -0
  639. package/dist/interface/ha/SensorHistory-yJNAhOsS.js.gz +0 -0
  640. package/dist/interface/ha/Sensors-BWiodX1x.js +1400 -0
  641. package/dist/interface/ha/Sensors-BWiodX1x.js.br +0 -0
  642. package/dist/interface/ha/Sensors-BWiodX1x.js.gz +0 -0
  643. package/dist/interface/ha/Settings-BI81b55S.js +218 -0
  644. package/dist/interface/ha/Settings-BI81b55S.js.br +0 -0
  645. package/dist/interface/ha/Settings-BI81b55S.js.gz +0 -0
  646. package/dist/interface/ha/SettingsAccount-CdjWQCPu.js +968 -0
  647. package/dist/interface/ha/SettingsAccount-CdjWQCPu.js.br +0 -0
  648. package/dist/interface/ha/SettingsAccount-CdjWQCPu.js.gz +0 -0
  649. package/dist/interface/ha/SettingsAppearance-XIYoSRsN.js +201 -0
  650. package/dist/interface/ha/SettingsAppearance-XIYoSRsN.js.br +0 -0
  651. package/dist/interface/ha/SettingsAppearance-XIYoSRsN.js.gz +0 -0
  652. package/dist/interface/ha/SettingsAssistant-kyqD6iRN.js +2134 -0
  653. package/dist/interface/ha/SettingsAssistant-kyqD6iRN.js.br +0 -0
  654. package/dist/interface/ha/SettingsAssistant-kyqD6iRN.js.gz +0 -0
  655. package/dist/interface/ha/SettingsBackup-CwQZCEBK.js +1047 -0
  656. package/dist/interface/ha/SettingsBackup-CwQZCEBK.js.br +0 -0
  657. package/dist/interface/ha/SettingsBackup-CwQZCEBK.js.gz +0 -0
  658. package/dist/interface/ha/SettingsList-DGFZj9DR.js +40 -0
  659. package/dist/interface/ha/SettingsList-DGFZj9DR.js.br +0 -0
  660. package/dist/interface/ha/SettingsList-DGFZj9DR.js.gz +0 -0
  661. package/dist/interface/ha/SettingsMqtt-C3LiDHjt.js +512 -0
  662. package/dist/interface/ha/SettingsMqtt-C3LiDHjt.js.br +0 -0
  663. package/dist/interface/ha/SettingsMqtt-C3LiDHjt.js.gz +0 -0
  664. package/dist/interface/ha/SettingsNotifications-1nOlxIiU.js +514 -0
  665. package/dist/interface/ha/SettingsNotifications-1nOlxIiU.js.br +0 -0
  666. package/dist/interface/ha/SettingsNotifications-1nOlxIiU.js.gz +0 -0
  667. package/dist/interface/ha/SettingsRecordings-JNunCb_2.js +152 -0
  668. package/dist/interface/ha/SettingsRecordings-JNunCb_2.js.br +0 -0
  669. package/dist/interface/ha/SettingsRecordings-JNunCb_2.js.gz +0 -0
  670. package/dist/interface/ha/SettingsRemote-BMPH9q5l.js +1523 -0
  671. package/dist/interface/ha/SettingsRemote-BMPH9q5l.js.br +0 -0
  672. package/dist/interface/ha/SettingsRemote-BMPH9q5l.js.gz +0 -0
  673. package/dist/interface/ha/SettingsSystem-DDH4f0Vx.js +382 -0
  674. package/dist/interface/ha/SettingsSystem-DDH4f0Vx.js.br +0 -0
  675. package/dist/interface/ha/SettingsSystem-DDH4f0Vx.js.gz +0 -0
  676. package/dist/interface/ha/SettingsUsers-CFTsI0cg.js +370 -0
  677. package/dist/interface/ha/SettingsUsers-CFTsI0cg.js.br +0 -0
  678. package/dist/interface/ha/SettingsUsers-CFTsI0cg.js.gz +0 -0
  679. package/dist/interface/ha/Terminal-DSDOuCjQ.js +611 -0
  680. package/dist/interface/ha/Terminal-DSDOuCjQ.js.br +0 -0
  681. package/dist/interface/ha/Terminal-DSDOuCjQ.js.gz +0 -0
  682. package/dist/interface/ha/Training-Bz7zeaiv.js +1829 -0
  683. package/dist/interface/ha/Training-Bz7zeaiv.js.br +0 -0
  684. package/dist/interface/ha/Training-Bz7zeaiv.js.gz +0 -0
  685. package/dist/interface/ha/Updates-Dia2pdZq.js +357 -0
  686. package/dist/interface/ha/Updates-Dia2pdZq.js.br +0 -0
  687. package/dist/interface/ha/Updates-Dia2pdZq.js.gz +0 -0
  688. package/dist/interface/ha/VariableInput-BJNBozvt.js +52 -0
  689. package/dist/interface/ha/VariableInput-BJNBozvt.js.br +0 -0
  690. package/dist/interface/ha/VariableInput-BJNBozvt.js.gz +0 -0
  691. package/dist/interface/ha/VersionsHandler-nZZLPTIy.js +221 -0
  692. package/dist/interface/ha/VersionsHandler-nZZLPTIy.js.br +0 -0
  693. package/dist/interface/ha/VersionsHandler-nZZLPTIy.js.gz +0 -0
  694. package/dist/interface/ha/VirtualSensorCreate-BEY_5Moy.js +102 -0
  695. package/dist/interface/ha/VirtualSensorCreate-BEY_5Moy.js.br +0 -0
  696. package/dist/interface/ha/VirtualSensorCreate-BEY_5Moy.js.gz +0 -0
  697. package/dist/interface/ha/Workers-Cz7uqDB1.js +1270 -0
  698. package/dist/interface/ha/Workers-Cz7uqDB1.js.br +0 -0
  699. package/dist/interface/ha/Workers-Cz7uqDB1.js.gz +0 -0
  700. package/dist/interface/ha/api-CQn_HFOl.js +29 -0
  701. package/dist/interface/ha/api-CQn_HFOl.js.br +0 -0
  702. package/dist/interface/ha/api-CQn_HFOl.js.gz +0 -0
  703. package/dist/interface/ha/api-DUn3L5D7.js +69083 -0
  704. package/dist/interface/ha/api-DUn3L5D7.js.br +0 -0
  705. package/dist/interface/ha/api-DUn3L5D7.js.gz +0 -0
  706. package/dist/interface/ha/assistant-BN7Ii0r0.js +349 -0
  707. package/dist/interface/ha/assistant-BN7Ii0r0.js.br +0 -0
  708. package/dist/interface/ha/assistant-BN7Ii0r0.js.gz +0 -0
  709. package/dist/interface/ha/auth-BPmMlAAg.js +271 -0
  710. package/dist/interface/ha/auth-BPmMlAAg.js.br +0 -0
  711. package/dist/interface/ha/auth-BPmMlAAg.js.gz +0 -0
  712. package/dist/interface/ha/automationBlueprint-DLWpQex2.js +250 -0
  713. package/dist/interface/ha/automationBlueprint-DLWpQex2.js.br +0 -0
  714. package/dist/interface/ha/automationBlueprint-DLWpQex2.js.gz +0 -0
  715. package/dist/interface/ha/cameraui-cards.js +2 -2
  716. package/dist/interface/ha/cards-DESDf3mL.js +13315 -0
  717. package/dist/interface/ha/cards-DESDf3mL.js.br +0 -0
  718. package/dist/interface/ha/cards-DESDf3mL.js.gz +0 -0
  719. package/dist/interface/ha/config-D8j5zflt.js +116 -0
  720. package/dist/interface/ha/config-D8j5zflt.js.br +0 -0
  721. package/dist/interface/ha/config-D8j5zflt.js.gz +0 -0
  722. package/dist/interface/ha/dist-DTq7_9I6.js +4434 -0
  723. package/dist/interface/ha/dist-DTq7_9I6.js.br +0 -0
  724. package/dist/interface/ha/dist-DTq7_9I6.js.gz +0 -0
  725. package/dist/interface/ha/mqtt-e7J98M0X.js +56 -0
  726. package/dist/interface/ha/mqtt-e7J98M0X.js.br +0 -0
  727. package/dist/interface/ha/mqtt-e7J98M0X.js.gz +0 -0
  728. package/dist/interface/ha/notifications-CKfUQZcf.js +159 -0
  729. package/dist/interface/ha/notifications-CKfUQZcf.js.br +0 -0
  730. package/dist/interface/ha/notifications-CKfUQZcf.js.gz +0 -0
  731. package/dist/interface/ha/plugins-DHEWLE-_.js +592 -0
  732. package/dist/interface/ha/plugins-DHEWLE-_.js.br +0 -0
  733. package/dist/interface/ha/plugins-DHEWLE-_.js.gz +0 -0
  734. package/dist/interface/ha/rooms-B8un3Qdi.js +45 -0
  735. package/dist/interface/ha/rooms-B8un3Qdi.js.br +0 -0
  736. package/dist/interface/ha/rooms-B8un3Qdi.js.gz +0 -0
  737. package/dist/interface/ha/server-D2PcGC9A.js +244 -0
  738. package/dist/interface/ha/server-D2PcGC9A.js.br +0 -0
  739. package/dist/interface/ha/server-D2PcGC9A.js.gz +0 -0
  740. package/dist/interface/ha/speeddial-Cxa_1lY2.js +941 -0
  741. package/dist/interface/ha/speeddial-Cxa_1lY2.js.br +0 -0
  742. package/dist/interface/ha/speeddial-Cxa_1lY2.js.gz +0 -0
  743. package/dist/interface/ha/types-4TNJU75d.js +1073 -0
  744. package/dist/interface/ha/types-4TNJU75d.js.br +0 -0
  745. package/dist/interface/ha/types-4TNJU75d.js.gz +0 -0
  746. package/dist/interface/ha/updates-C7C7vE8R.js +414 -0
  747. package/dist/interface/ha/updates-C7C7vE8R.js.br +0 -0
  748. package/dist/interface/ha/updates-C7C7vE8R.js.gz +0 -0
  749. package/dist/interface/ha/useAppVersion-BJg6yGaM.js +22 -0
  750. package/dist/interface/ha/useAppVersion-BJg6yGaM.js.br +3 -0
  751. package/dist/interface/ha/useAppVersion-BJg6yGaM.js.gz +0 -0
  752. package/dist/interface/ha/useKeyboardInset-B4Ta0MYT.js +27 -0
  753. package/dist/interface/ha/useKeyboardInset-B4Ta0MYT.js.br +0 -0
  754. package/dist/interface/ha/useKeyboardInset-B4Ta0MYT.js.gz +0 -0
  755. package/dist/interface/ha/useLogsSocket-B_i59MM_.js +74 -0
  756. package/dist/interface/ha/useLogsSocket-B_i59MM_.js.br +0 -0
  757. package/dist/interface/ha/useLogsSocket-B_i59MM_.js.gz +0 -0
  758. package/dist/interface/ha/useNavLayout-DboStdqA.js +125 -0
  759. package/dist/interface/ha/useNavLayout-DboStdqA.js.br +0 -0
  760. package/dist/interface/ha/useNavLayout-DboStdqA.js.gz +0 -0
  761. package/dist/interface/ha/usePlacementLabel-DCu74x82.js +49 -0
  762. package/dist/interface/ha/usePlacementLabel-DCu74x82.js.br +0 -0
  763. package/dist/interface/ha/usePlacementLabel-DCu74x82.js.gz +0 -0
  764. package/dist/interface/ha/useTabSwipe-DjexjTaU.js +45 -0
  765. package/dist/interface/ha/useTabSwipe-DjexjTaU.js.br +0 -0
  766. package/dist/interface/ha/useTabSwipe-DjexjTaU.js.gz +0 -0
  767. package/dist/interface/ha/useUpdateChannel-BPWUr4Hf.js +22 -0
  768. package/dist/interface/ha/useUpdateChannel-BPWUr4Hf.js.br +0 -0
  769. package/dist/interface/ha/useUpdateChannel-BPWUr4Hf.js.gz +0 -0
  770. package/dist/interface/ha/useUpdatesSocket-M7K1vFu-.js +34 -0
  771. package/dist/interface/ha/useUpdatesSocket-M7K1vFu-.js.br +0 -0
  772. package/dist/interface/ha/useUpdatesSocket-M7K1vFu-.js.gz +0 -0
  773. package/dist/interface/ha/useUserOptions-BYC5YXhG.js +15 -0
  774. package/dist/interface/ha/useUserOptions-BYC5YXhG.js.br +0 -0
  775. package/dist/interface/ha/useUserOptions-BYC5YXhG.js.gz +0 -0
  776. package/dist/interface/index.html +29 -29
  777. package/dist/interface/sw.js +1 -1
  778. package/dist/server/assets/docs.json +1 -1
  779. package/dist/server/package.json +7 -7
  780. package/dist/server/requirements.txt +2 -2
  781. package/dist/server/src/_camera_ui_tools/camera_ui_sdk/camera/device.py +16 -1
  782. package/dist/server/src/api/controllers/cameras.controller.js +1 -2
  783. package/dist/server/src/api/controllers/cameras.controller.js.map +1 -1
  784. package/dist/server/src/assistant/tools/cameras.js +1 -2
  785. package/dist/server/src/assistant/tools/cameras.js.map +1 -1
  786. package/dist/server/src/automations/actions/snapshot.js +2 -4
  787. package/dist/server/src/automations/actions/snapshot.js.map +1 -1
  788. package/dist/server/src/camera/controller.d.ts +0 -1
  789. package/dist/server/src/camera/controller.js +4 -8
  790. package/dist/server/src/camera/controller.js.map +1 -1
  791. package/dist/server/src/camera/decoder/worker.js +2 -3
  792. package/dist/server/src/camera/decoder/worker.js.map +1 -1
  793. package/dist/server/src/camera/index.d.ts +4 -2
  794. package/dist/server/src/camera/index.js +8 -1
  795. package/dist/server/src/camera/index.js.map +1 -1
  796. package/dist/server/src/camera/snapshot-store.d.ts +1 -1
  797. package/dist/server/src/camera/snapshot-store.js +6 -2
  798. package/dist/server/src/camera/snapshot-store.js.map +1 -1
  799. package/dist/server/src/camera/streaming/fmp4-session.js +7 -1
  800. package/dist/server/src/camera/streaming/fmp4-session.js.map +1 -1
  801. package/dist/server/src/camera/streaming/rtp-session.d.ts +1 -0
  802. package/dist/server/src/camera/streaming/rtp-session.js +7 -0
  803. package/dist/server/src/camera/streaming/rtp-session.js.map +1 -1
  804. package/dist/server/src/mqtt/bridge.js +1 -1
  805. package/dist/server/src/mqtt/bridge.js.map +1 -1
  806. package/dist/server/src/plugins/runtime/python/proxy/camera_device.py +12 -4
  807. package/npm-shrinkwrap.json +117 -117
  808. package/package.json +7 -7
  809. package/dist/interface/assets/404-BMj79XjT.js +0 -1
  810. package/dist/interface/assets/404-BMj79XjT.js.br +0 -0
  811. package/dist/interface/assets/404-BMj79XjT.js.gz +0 -0
  812. package/dist/interface/assets/About-GuOorOsw.js +0 -1
  813. package/dist/interface/assets/About-GuOorOsw.js.br +0 -5
  814. package/dist/interface/assets/About-GuOorOsw.js.gz +0 -0
  815. package/dist/interface/assets/Assistant-BCmv7r12.js +0 -1
  816. package/dist/interface/assets/Assistant-BCmv7r12.js.br +0 -0
  817. package/dist/interface/assets/Assistant-BCmv7r12.js.gz +0 -0
  818. package/dist/interface/assets/Automation-DiCBllSc.js +0 -3
  819. package/dist/interface/assets/Automation-DiCBllSc.js.br +0 -0
  820. package/dist/interface/assets/Automation-DiCBllSc.js.gz +0 -0
  821. package/dist/interface/assets/AutomationImport-DxbW0MBD.js +0 -2
  822. package/dist/interface/assets/AutomationImport-DxbW0MBD.js.br +0 -0
  823. package/dist/interface/assets/AutomationImport-DxbW0MBD.js.gz +0 -0
  824. package/dist/interface/assets/AutomationStore-D2xJRXIr.js +0 -2
  825. package/dist/interface/assets/AutomationStore-D2xJRXIr.js.br +0 -0
  826. package/dist/interface/assets/AutomationStore-D2xJRXIr.js.gz +0 -0
  827. package/dist/interface/assets/AutomationStoreDetail-6UfqyhXv.js +0 -2
  828. package/dist/interface/assets/AutomationStoreDetail-6UfqyhXv.js.br +0 -0
  829. package/dist/interface/assets/AutomationStoreDetail-6UfqyhXv.js.gz +0 -0
  830. package/dist/interface/assets/Automations-B5D6uMR9.js +0 -2
  831. package/dist/interface/assets/Automations-B5D6uMR9.js.br +0 -0
  832. package/dist/interface/assets/Automations-B5D6uMR9.js.gz +0 -0
  833. package/dist/interface/assets/BackupRestore-CWYSA-p9.js +0 -1
  834. package/dist/interface/assets/BackupRestore-CWYSA-p9.js.br +0 -0
  835. package/dist/interface/assets/BackupRestore-CWYSA-p9.js.gz +0 -0
  836. package/dist/interface/assets/Camera-nAhvtDPM.js +0 -2
  837. package/dist/interface/assets/Camera-nAhvtDPM.js.br +0 -0
  838. package/dist/interface/assets/Camera-nAhvtDPM.js.gz +0 -0
  839. package/dist/interface/assets/CameraConsole-BubkOXIn.js +0 -7
  840. package/dist/interface/assets/CameraConsole-BubkOXIn.js.br +0 -0
  841. package/dist/interface/assets/CameraConsole-BubkOXIn.js.gz +0 -0
  842. package/dist/interface/assets/Cameras-_za6U1fG.js +0 -651
  843. package/dist/interface/assets/Cameras-_za6U1fG.js.br +0 -0
  844. package/dist/interface/assets/Cameras-_za6U1fG.js.gz +0 -0
  845. package/dist/interface/assets/Camview-Dz37ulQ3.js +0 -1
  846. package/dist/interface/assets/Camview-Dz37ulQ3.js.br +0 -0
  847. package/dist/interface/assets/Camview-Dz37ulQ3.js.gz +0 -0
  848. package/dist/interface/assets/Config-DJjqahmx.js +0 -2
  849. package/dist/interface/assets/Config-DJjqahmx.js.br +0 -0
  850. package/dist/interface/assets/Config-DJjqahmx.js.gz +0 -0
  851. package/dist/interface/assets/ConfigActionAssistant-DKrLo3aR.js +0 -1
  852. package/dist/interface/assets/ConfigActionAssistant-DKrLo3aR.js.br +0 -0
  853. package/dist/interface/assets/ConfigActionAssistant-DKrLo3aR.js.gz +0 -0
  854. package/dist/interface/assets/ConfigActionCameraControl-DQJouc3y.js +0 -1
  855. package/dist/interface/assets/ConfigActionCameraControl-DQJouc3y.js.br +0 -0
  856. package/dist/interface/assets/ConfigActionCameraControl-DQJouc3y.js.gz +0 -0
  857. package/dist/interface/assets/ConfigActionHttp-BnJFthTT.js +0 -1
  858. package/dist/interface/assets/ConfigActionHttp-BnJFthTT.js.br +0 -0
  859. package/dist/interface/assets/ConfigActionHttp-BnJFthTT.js.gz +0 -0
  860. package/dist/interface/assets/ConfigActionImageInput-ALM7KAw2.js +0 -1
  861. package/dist/interface/assets/ConfigActionImageInput-ALM7KAw2.js.br +0 -0
  862. package/dist/interface/assets/ConfigActionImageInput-ALM7KAw2.js.gz +0 -0
  863. package/dist/interface/assets/ConfigActionMqtt-nS3RS1Gk.js +0 -1
  864. package/dist/interface/assets/ConfigActionMqtt-nS3RS1Gk.js.br +0 -0
  865. package/dist/interface/assets/ConfigActionMqtt-nS3RS1Gk.js.gz +0 -0
  866. package/dist/interface/assets/ConfigActionNotification-CKHAbwQC.js +0 -1
  867. package/dist/interface/assets/ConfigActionNotification-CKHAbwQC.js.br +0 -0
  868. package/dist/interface/assets/ConfigActionNotification-CKHAbwQC.js.gz +0 -0
  869. package/dist/interface/assets/ConfigActionNotificationControl-BnSGHsU1.js +0 -1
  870. package/dist/interface/assets/ConfigActionNotificationControl-BnSGHsU1.js.br +0 -0
  871. package/dist/interface/assets/ConfigActionNotificationControl-BnSGHsU1.js.gz +0 -0
  872. package/dist/interface/assets/ConfigActionOutput-D4Wel3De.js +0 -1
  873. package/dist/interface/assets/ConfigActionOutput-D4Wel3De.js.br +0 -0
  874. package/dist/interface/assets/ConfigActionOutput-D4Wel3De.js.gz +0 -0
  875. package/dist/interface/assets/ConfigActionPlugin-bh6rI0um.js +0 -1
  876. package/dist/interface/assets/ConfigActionPlugin-bh6rI0um.js.br +0 -0
  877. package/dist/interface/assets/ConfigActionPlugin-bh6rI0um.js.gz +0 -0
  878. package/dist/interface/assets/ConfigActionSensor-O4MJd0M1.js +0 -1
  879. package/dist/interface/assets/ConfigActionSensor-O4MJd0M1.js.br +0 -0
  880. package/dist/interface/assets/ConfigActionSensor-O4MJd0M1.js.gz +0 -0
  881. package/dist/interface/assets/ConfigActionSnapshot-Bv1LvsN5.js +0 -1
  882. package/dist/interface/assets/ConfigActionSnapshot-Bv1LvsN5.js.br +0 -0
  883. package/dist/interface/assets/ConfigActionSnapshot-Bv1LvsN5.js.gz +0 -0
  884. package/dist/interface/assets/ConfigActionVariable-BhIOexZq.js +0 -1
  885. package/dist/interface/assets/ConfigActionVariable-BhIOexZq.js.br +0 -0
  886. package/dist/interface/assets/ConfigActionVariable-BhIOexZq.js.gz +0 -0
  887. package/dist/interface/assets/ConfigConditionIfElse-smevwPDS.js +0 -1
  888. package/dist/interface/assets/ConfigConditionIfElse-smevwPDS.js.br +0 -0
  889. package/dist/interface/assets/ConfigConditionIfElse-smevwPDS.js.gz +0 -0
  890. package/dist/interface/assets/ConfigConditionSensorState-BvqEGzvU.js +0 -1
  891. package/dist/interface/assets/ConfigConditionSensorState-BvqEGzvU.js.br +0 -0
  892. package/dist/interface/assets/ConfigConditionSensorState-BvqEGzvU.js.gz +0 -0
  893. package/dist/interface/assets/ConfigConditionSwitch-BarJmiQY.js +0 -1
  894. package/dist/interface/assets/ConfigConditionSwitch-BarJmiQY.js.br +0 -0
  895. package/dist/interface/assets/ConfigConditionSwitch-BarJmiQY.js.gz +0 -0
  896. package/dist/interface/assets/ConfigSensorValueInput-C4afzn70.js +0 -1
  897. package/dist/interface/assets/ConfigSensorValueInput-C4afzn70.js.br +0 -0
  898. package/dist/interface/assets/ConfigSensorValueInput-C4afzn70.js.gz +0 -0
  899. package/dist/interface/assets/ConfigTriggerDetection-CIiFBNYv.js +0 -1
  900. package/dist/interface/assets/ConfigTriggerDetection-CIiFBNYv.js.br +0 -0
  901. package/dist/interface/assets/ConfigTriggerDetection-CIiFBNYv.js.gz +0 -0
  902. package/dist/interface/assets/ConfigTriggerGeofence-DthsSDZs.js +0 -1
  903. package/dist/interface/assets/ConfigTriggerGeofence-DthsSDZs.js.br +0 -0
  904. package/dist/interface/assets/ConfigTriggerGeofence-DthsSDZs.js.gz +0 -0
  905. package/dist/interface/assets/ConfigTriggerMqtt-CmERgsUS.js +0 -1
  906. package/dist/interface/assets/ConfigTriggerMqtt-CmERgsUS.js.br +0 -0
  907. package/dist/interface/assets/ConfigTriggerMqtt-CmERgsUS.js.gz +0 -0
  908. package/dist/interface/assets/ConfigTriggerSensor-Cu6yNORc.js +0 -1
  909. package/dist/interface/assets/ConfigTriggerSensor-Cu6yNORc.js.br +0 -0
  910. package/dist/interface/assets/ConfigTriggerSensor-Cu6yNORc.js.gz +0 -0
  911. package/dist/interface/assets/ConfigTriggerSystem-GnAYuh6W.js +0 -1
  912. package/dist/interface/assets/ConfigTriggerSystem-GnAYuh6W.js.br +0 -0
  913. package/dist/interface/assets/ConfigTriggerSystem-GnAYuh6W.js.gz +0 -0
  914. package/dist/interface/assets/ConfigTriggerWebhook-BKLfeJO1.js +0 -1
  915. package/dist/interface/assets/ConfigTriggerWebhook-BKLfeJO1.js.br +0 -0
  916. package/dist/interface/assets/ConfigTriggerWebhook-BKLfeJO1.js.gz +0 -0
  917. package/dist/interface/assets/Console-B2LJUI0g.js +0 -1
  918. package/dist/interface/assets/Console-B2LJUI0g.js.br +0 -0
  919. package/dist/interface/assets/Console-B2LJUI0g.js.gz +0 -0
  920. package/dist/interface/assets/CuiAssistantConversation-CixQUjS9.js +0 -5
  921. package/dist/interface/assets/CuiAssistantConversation-CixQUjS9.js.br +0 -0
  922. package/dist/interface/assets/CuiAssistantConversation-CixQUjS9.js.gz +0 -0
  923. package/dist/interface/assets/CuiAutomationRunsDialog-6uaXrJS5.js +0 -1
  924. package/dist/interface/assets/CuiAutomationRunsDialog-6uaXrJS5.js.br +0 -0
  925. package/dist/interface/assets/CuiAutomationRunsDialog-6uaXrJS5.js.gz +0 -0
  926. package/dist/interface/assets/CuiBottomSheet-CAOO-nWU.js +0 -1
  927. package/dist/interface/assets/CuiBottomSheet-CAOO-nWU.js.br +0 -0
  928. package/dist/interface/assets/CuiBottomSheet-CAOO-nWU.js.gz +0 -0
  929. package/dist/interface/assets/CuiBottomSheet-Cs8U_ja7.js +0 -1
  930. package/dist/interface/assets/CuiBottomSheet-Cs8U_ja7.js.br +0 -1
  931. package/dist/interface/assets/CuiBottomSheet-Cs8U_ja7.js.gz +0 -0
  932. package/dist/interface/assets/CuiCameraPipCard-CIhq8eot.js +0 -1
  933. package/dist/interface/assets/CuiCameraPipCard-CIhq8eot.js.br +0 -0
  934. package/dist/interface/assets/CuiCameraPipCard-CIhq8eot.js.gz +0 -0
  935. package/dist/interface/assets/CuiCameraRecordings-DF_sMli8.js +0 -1
  936. package/dist/interface/assets/CuiCameraRecordings-DF_sMli8.js.br +0 -0
  937. package/dist/interface/assets/CuiCameraRecordings-DF_sMli8.js.gz +0 -0
  938. package/dist/interface/assets/CuiCameraShares-BFYlATKA.js +0 -1
  939. package/dist/interface/assets/CuiCameraShares-BFYlATKA.js.br +0 -0
  940. package/dist/interface/assets/CuiCameraShares-BFYlATKA.js.gz +0 -0
  941. package/dist/interface/assets/CuiCameraTable-Bnkb5yFU.js +0 -2
  942. package/dist/interface/assets/CuiCameraTable-Bnkb5yFU.js.br +0 -0
  943. package/dist/interface/assets/CuiCameraTable-Bnkb5yFU.js.gz +0 -0
  944. package/dist/interface/assets/CuiCameraTable-DFINjvlg.js +0 -1
  945. package/dist/interface/assets/CuiCameraTable-DFINjvlg.js.br +0 -1
  946. package/dist/interface/assets/CuiCameraTable-DFINjvlg.js.gz +0 -0
  947. package/dist/interface/assets/CuiChartTable-CGgKsEsq.js +0 -1
  948. package/dist/interface/assets/CuiChartTable-CGgKsEsq.js.br +0 -0
  949. package/dist/interface/assets/CuiChartTable-CGgKsEsq.js.gz +0 -0
  950. package/dist/interface/assets/CuiConsole-Bfg49Yzx.js +0 -5
  951. package/dist/interface/assets/CuiConsole-Bfg49Yzx.js.br +0 -0
  952. package/dist/interface/assets/CuiConsole-Bfg49Yzx.js.gz +0 -0
  953. package/dist/interface/assets/CuiDataTable-CfRZXOio.js +0 -1
  954. package/dist/interface/assets/CuiDataTable-CfRZXOio.js.br +0 -0
  955. package/dist/interface/assets/CuiDataTable-CfRZXOio.js.gz +0 -0
  956. package/dist/interface/assets/CuiLightControl-DJh1UfRy.js +0 -1
  957. package/dist/interface/assets/CuiLightControl-DJh1UfRy.js.br +0 -0
  958. package/dist/interface/assets/CuiLightControl-DJh1UfRy.js.gz +0 -0
  959. package/dist/interface/assets/CuiMoveCodeForm-Cbcc3Rds.js +0 -1
  960. package/dist/interface/assets/CuiMoveCodeForm-Cbcc3Rds.js.br +0 -0
  961. package/dist/interface/assets/CuiMoveCodeForm-Cbcc3Rds.js.gz +0 -0
  962. package/dist/interface/assets/CuiPluginCard-D5o7bz82.js +0 -2
  963. package/dist/interface/assets/CuiPluginCard-D5o7bz82.js.br +0 -0
  964. package/dist/interface/assets/CuiPluginCard-D5o7bz82.js.gz +0 -0
  965. package/dist/interface/assets/CuiPluginCategoryChip-DMxs_ZwE.js +0 -1
  966. package/dist/interface/assets/CuiPluginCategoryChip-DMxs_ZwE.js.br +0 -0
  967. package/dist/interface/assets/CuiPluginCategoryChip-DMxs_ZwE.js.gz +0 -0
  968. package/dist/interface/assets/CuiPluginOAuthButton-BZ5GiClf.js +0 -2
  969. package/dist/interface/assets/CuiPluginOAuthButton-BZ5GiClf.js.br +0 -0
  970. package/dist/interface/assets/CuiPluginOAuthButton-BZ5GiClf.js.gz +0 -0
  971. package/dist/interface/assets/CuiPluginTrustBadge-CXPARdCH.js +0 -1
  972. package/dist/interface/assets/CuiPluginTrustBadge-CXPARdCH.js.br +0 -0
  973. package/dist/interface/assets/CuiPluginTrustBadge-CXPARdCH.js.gz +0 -0
  974. package/dist/interface/assets/CuiQRCode-UBWUs5tI.js +0 -1
  975. package/dist/interface/assets/CuiQRCode-UBWUs5tI.js.br +0 -0
  976. package/dist/interface/assets/CuiQRCode-UBWUs5tI.js.gz +0 -0
  977. package/dist/interface/assets/CuiSchema-CWj38bGw.js +0 -1
  978. package/dist/interface/assets/CuiSchema-CWj38bGw.js.br +0 -0
  979. package/dist/interface/assets/CuiSchema-CWj38bGw.js.gz +0 -0
  980. package/dist/interface/assets/CuiTopNavbarItem-DVEK1s5d.js +0 -1
  981. package/dist/interface/assets/CuiTopNavbarItem-DVEK1s5d.js.br +0 -0
  982. package/dist/interface/assets/CuiTopNavbarItem-DVEK1s5d.js.gz +0 -0
  983. package/dist/interface/assets/Faces-Bo3AYgKe.js +0 -1
  984. package/dist/interface/assets/Faces-Bo3AYgKe.js.br +0 -0
  985. package/dist/interface/assets/Faces-Bo3AYgKe.js.gz +0 -0
  986. package/dist/interface/assets/FirstSteps-BGR7npQx.js +0 -1
  987. package/dist/interface/assets/FirstSteps-BGR7npQx.js.br +0 -0
  988. package/dist/interface/assets/FirstSteps-BGR7npQx.js.gz +0 -0
  989. package/dist/interface/assets/Floorplan-DEAXLv_l.js +0 -1
  990. package/dist/interface/assets/Floorplan-DEAXLv_l.js.br +0 -0
  991. package/dist/interface/assets/Floorplan-DEAXLv_l.js.gz +0 -0
  992. package/dist/interface/assets/Instances-BWRmmXbE.js +0 -1
  993. package/dist/interface/assets/Instances-BWRmmXbE.js.br +0 -0
  994. package/dist/interface/assets/Instances-BWRmmXbE.js.gz +0 -0
  995. package/dist/interface/assets/Logs-Dk47dJCa.js +0 -6
  996. package/dist/interface/assets/Logs-Dk47dJCa.js.br +0 -0
  997. package/dist/interface/assets/Logs-Dk47dJCa.js.gz +0 -0
  998. package/dist/interface/assets/Menu-ByNNjqJn.js +0 -1
  999. package/dist/interface/assets/Menu-ByNNjqJn.js.br +0 -0
  1000. package/dist/interface/assets/Menu-ByNNjqJn.js.gz +0 -0
  1001. package/dist/interface/assets/Metrics-dwRXd0D8.js +0 -12
  1002. package/dist/interface/assets/Metrics-dwRXd0D8.js.br +0 -0
  1003. package/dist/interface/assets/Metrics-dwRXd0D8.js.gz +0 -0
  1004. package/dist/interface/assets/OAuthDeviceFlow-DN3VwFHC.js +0 -1
  1005. package/dist/interface/assets/OAuthDeviceFlow-DN3VwFHC.js.br +0 -0
  1006. package/dist/interface/assets/OAuthDeviceFlow-DN3VwFHC.js.gz +0 -0
  1007. package/dist/interface/assets/Plugin-CIH0TF3a.js +0 -1
  1008. package/dist/interface/assets/Plugin-CIH0TF3a.js.br +0 -0
  1009. package/dist/interface/assets/Plugin-CIH0TF3a.js.gz +0 -0
  1010. package/dist/interface/assets/PluginConsole-OUbDe_Qi.js +0 -7
  1011. package/dist/interface/assets/PluginConsole-OUbDe_Qi.js.br +0 -0
  1012. package/dist/interface/assets/PluginConsole-OUbDe_Qi.js.gz +0 -0
  1013. package/dist/interface/assets/PluginDetail-DzDdyA_n.js +0 -2
  1014. package/dist/interface/assets/PluginDetail-DzDdyA_n.js.br +0 -0
  1015. package/dist/interface/assets/PluginDetail-DzDdyA_n.js.gz +0 -0
  1016. package/dist/interface/assets/PluginSchema-CDiNUijO.js +0 -1
  1017. package/dist/interface/assets/PluginSchema-CDiNUijO.js.br +0 -0
  1018. package/dist/interface/assets/PluginSchema-CDiNUijO.js.gz +0 -0
  1019. package/dist/interface/assets/PluginSearch-6nfv0rUZ.js +0 -1
  1020. package/dist/interface/assets/PluginSearch-6nfv0rUZ.js.br +0 -1
  1021. package/dist/interface/assets/PluginSearch-6nfv0rUZ.js.gz +0 -0
  1022. package/dist/interface/assets/PluginSearch-CpjvGZ1K.js +0 -2
  1023. package/dist/interface/assets/PluginSearch-CpjvGZ1K.js.br +0 -0
  1024. package/dist/interface/assets/PluginSearch-CpjvGZ1K.js.gz +0 -0
  1025. package/dist/interface/assets/PluginUpdateProgress-BpxK2MxE.js +0 -1
  1026. package/dist/interface/assets/PluginUpdateProgress-BpxK2MxE.js.br +0 -0
  1027. package/dist/interface/assets/PluginUpdateProgress-BpxK2MxE.js.gz +0 -0
  1028. package/dist/interface/assets/Plugins-DRbDoh8f.js +0 -1
  1029. package/dist/interface/assets/Plugins-DRbDoh8f.js.br +0 -0
  1030. package/dist/interface/assets/Plugins-DRbDoh8f.js.gz +0 -0
  1031. package/dist/interface/assets/RecentEventsFilter-BFlbTCVN.js +0 -1
  1032. package/dist/interface/assets/RecentEventsFilter-BFlbTCVN.js.br +0 -0
  1033. package/dist/interface/assets/RecentEventsFilter-BFlbTCVN.js.gz +0 -0
  1034. package/dist/interface/assets/Recordings-JmKjg1rv.js +0 -1
  1035. package/dist/interface/assets/Recordings-JmKjg1rv.js.br +0 -0
  1036. package/dist/interface/assets/Recordings-JmKjg1rv.js.gz +0 -0
  1037. package/dist/interface/assets/SensorEdit-5iXuq1hX.js +0 -1
  1038. package/dist/interface/assets/SensorEdit-5iXuq1hX.js.br +0 -0
  1039. package/dist/interface/assets/SensorEdit-5iXuq1hX.js.gz +0 -0
  1040. package/dist/interface/assets/SensorHistory-DvW4ylhc.js +0 -1
  1041. package/dist/interface/assets/SensorHistory-DvW4ylhc.js.br +0 -0
  1042. package/dist/interface/assets/SensorHistory-DvW4ylhc.js.gz +0 -0
  1043. package/dist/interface/assets/Sensors-CJRgHg24.js +0 -2
  1044. package/dist/interface/assets/Sensors-CJRgHg24.js.br +0 -0
  1045. package/dist/interface/assets/Sensors-CJRgHg24.js.gz +0 -0
  1046. package/dist/interface/assets/Settings-gP6KvlhW.js +0 -1
  1047. package/dist/interface/assets/Settings-gP6KvlhW.js.br +0 -0
  1048. package/dist/interface/assets/Settings-gP6KvlhW.js.gz +0 -0
  1049. package/dist/interface/assets/SettingsAccount-DxBaSiP3.js +0 -1
  1050. package/dist/interface/assets/SettingsAccount-DxBaSiP3.js.br +0 -0
  1051. package/dist/interface/assets/SettingsAccount-DxBaSiP3.js.gz +0 -0
  1052. package/dist/interface/assets/SettingsAccount-UaKygscf.css +0 -1
  1053. package/dist/interface/assets/SettingsAccount-UaKygscf.css.br +0 -0
  1054. package/dist/interface/assets/SettingsAccount-UaKygscf.css.gz +0 -0
  1055. package/dist/interface/assets/SettingsAccount-UyW8Rdbu.js +0 -1
  1056. package/dist/interface/assets/SettingsAccount-UyW8Rdbu.js.br +0 -0
  1057. package/dist/interface/assets/SettingsAccount-UyW8Rdbu.js.gz +0 -0
  1058. package/dist/interface/assets/SettingsAppearance-CklnBo48.js +0 -1
  1059. package/dist/interface/assets/SettingsAppearance-CklnBo48.js.br +0 -0
  1060. package/dist/interface/assets/SettingsAppearance-CklnBo48.js.gz +0 -0
  1061. package/dist/interface/assets/SettingsAppearance-CyRil5A6.js +0 -1
  1062. package/dist/interface/assets/SettingsAppearance-CyRil5A6.js.br +0 -0
  1063. package/dist/interface/assets/SettingsAppearance-CyRil5A6.js.gz +0 -0
  1064. package/dist/interface/assets/SettingsAssistant-BpoC9nuP.js +0 -1
  1065. package/dist/interface/assets/SettingsAssistant-BpoC9nuP.js.br +0 -0
  1066. package/dist/interface/assets/SettingsAssistant-BpoC9nuP.js.gz +0 -0
  1067. package/dist/interface/assets/SettingsBackup-qF_Tg4EV.js +0 -1
  1068. package/dist/interface/assets/SettingsBackup-qF_Tg4EV.js.br +0 -0
  1069. package/dist/interface/assets/SettingsBackup-qF_Tg4EV.js.gz +0 -0
  1070. package/dist/interface/assets/SettingsList-DVIZe748.js +0 -1
  1071. package/dist/interface/assets/SettingsList-DVIZe748.js.br +0 -0
  1072. package/dist/interface/assets/SettingsList-DVIZe748.js.gz +0 -0
  1073. package/dist/interface/assets/SettingsMqtt-B-vy3STG.js +0 -1
  1074. package/dist/interface/assets/SettingsMqtt-B-vy3STG.js.br +0 -0
  1075. package/dist/interface/assets/SettingsMqtt-B-vy3STG.js.gz +0 -0
  1076. package/dist/interface/assets/SettingsNotifications-DxkB07uF.js +0 -1
  1077. package/dist/interface/assets/SettingsNotifications-DxkB07uF.js.br +0 -0
  1078. package/dist/interface/assets/SettingsNotifications-DxkB07uF.js.gz +0 -0
  1079. package/dist/interface/assets/SettingsPermissions-GDUHm4Dy.js +0 -1
  1080. package/dist/interface/assets/SettingsPermissions-GDUHm4Dy.js.br +0 -0
  1081. package/dist/interface/assets/SettingsPermissions-GDUHm4Dy.js.gz +0 -0
  1082. package/dist/interface/assets/SettingsRecordings-yoLYy5Ye.js +0 -1
  1083. package/dist/interface/assets/SettingsRecordings-yoLYy5Ye.js.br +0 -0
  1084. package/dist/interface/assets/SettingsRecordings-yoLYy5Ye.js.gz +0 -0
  1085. package/dist/interface/assets/SettingsRemote-Bl6CSoS0.js +0 -2
  1086. package/dist/interface/assets/SettingsRemote-Bl6CSoS0.js.br +0 -0
  1087. package/dist/interface/assets/SettingsRemote-Bl6CSoS0.js.gz +0 -0
  1088. package/dist/interface/assets/SettingsSystem-CSXyXTlu.js +0 -2
  1089. package/dist/interface/assets/SettingsSystem-CSXyXTlu.js.br +0 -0
  1090. package/dist/interface/assets/SettingsSystem-CSXyXTlu.js.gz +0 -0
  1091. package/dist/interface/assets/SettingsUsers-BWGACbPP.js +0 -1
  1092. package/dist/interface/assets/SettingsUsers-BWGACbPP.js.br +0 -0
  1093. package/dist/interface/assets/SettingsUsers-BWGACbPP.js.gz +0 -0
  1094. package/dist/interface/assets/Terminal-B6u7U9CX.js +0 -4
  1095. package/dist/interface/assets/Terminal-B6u7U9CX.js.br +0 -0
  1096. package/dist/interface/assets/Terminal-B6u7U9CX.js.gz +0 -0
  1097. package/dist/interface/assets/Training-Cn7kcb1P.js +0 -1
  1098. package/dist/interface/assets/Training-Cn7kcb1P.js.br +0 -0
  1099. package/dist/interface/assets/Training-Cn7kcb1P.js.gz +0 -0
  1100. package/dist/interface/assets/Updates-BtcOsKwD.js +0 -2
  1101. package/dist/interface/assets/Updates-BtcOsKwD.js.br +0 -0
  1102. package/dist/interface/assets/Updates-BtcOsKwD.js.gz +0 -0
  1103. package/dist/interface/assets/VariableInput-BmTqWbr7.js +0 -1
  1104. package/dist/interface/assets/VariableInput-BmTqWbr7.js.br +0 -4
  1105. package/dist/interface/assets/VariableInput-BmTqWbr7.js.gz +0 -0
  1106. package/dist/interface/assets/VersionsHandler-B11ImKTw.js +0 -7
  1107. package/dist/interface/assets/VersionsHandler-B11ImKTw.js.br +0 -0
  1108. package/dist/interface/assets/VersionsHandler-B11ImKTw.js.gz +0 -0
  1109. package/dist/interface/assets/VirtualSensorCreate-Bk6dDT3N.js +0 -1
  1110. package/dist/interface/assets/VirtualSensorCreate-Bk6dDT3N.js.br +0 -0
  1111. package/dist/interface/assets/VirtualSensorCreate-Bk6dDT3N.js.gz +0 -0
  1112. package/dist/interface/assets/Workers-BVY0gIZ6.js +0 -2
  1113. package/dist/interface/assets/Workers-BVY0gIZ6.js.br +0 -0
  1114. package/dist/interface/assets/Workers-BVY0gIZ6.js.gz +0 -0
  1115. package/dist/interface/assets/api-CQ3LkXuk.js +0 -4
  1116. package/dist/interface/assets/api-CQ3LkXuk.js.br +0 -0
  1117. package/dist/interface/assets/api-CQ3LkXuk.js.gz +0 -0
  1118. package/dist/interface/assets/api-D5pj1z4u.js +0 -1
  1119. package/dist/interface/assets/api-D5pj1z4u.js.br +0 -2
  1120. package/dist/interface/assets/api-D5pj1z4u.js.gz +0 -0
  1121. package/dist/interface/assets/api-Dh8jUlae.css +0 -2
  1122. package/dist/interface/assets/api-Dh8jUlae.css.br +0 -0
  1123. package/dist/interface/assets/api-Dh8jUlae.css.gz +0 -0
  1124. package/dist/interface/assets/assistant-Ak1qQkQD.js +0 -1
  1125. package/dist/interface/assets/assistant-Ak1qQkQD.js.br +0 -0
  1126. package/dist/interface/assets/assistant-Ak1qQkQD.js.gz +0 -0
  1127. package/dist/interface/assets/auth-C5r5IkMf.js +0 -1
  1128. package/dist/interface/assets/auth-C5r5IkMf.js.br +0 -0
  1129. package/dist/interface/assets/auth-C5r5IkMf.js.gz +0 -0
  1130. package/dist/interface/assets/automations-DCanZA4l.js +0 -1
  1131. package/dist/interface/assets/automations-DCanZA4l.js.br +0 -0
  1132. package/dist/interface/assets/automations-DCanZA4l.js.gz +0 -0
  1133. package/dist/interface/assets/cameraSources-C-oeWnEa.js +0 -1
  1134. package/dist/interface/assets/cameraSources-C-oeWnEa.js.br +0 -0
  1135. package/dist/interface/assets/cameraSources-C-oeWnEa.js.gz +0 -0
  1136. package/dist/interface/assets/cloudHandoff-gh1JjFoB.js +0 -1
  1137. package/dist/interface/assets/cloudHandoff-gh1JjFoB.js.br +0 -1
  1138. package/dist/interface/assets/cloudHandoff-gh1JjFoB.js.gz +0 -0
  1139. package/dist/interface/assets/config-CaeB2xoR.js +0 -1
  1140. package/dist/interface/assets/config-CaeB2xoR.js.br +0 -0
  1141. package/dist/interface/assets/config-CaeB2xoR.js.gz +0 -0
  1142. package/dist/interface/assets/flowSchema-DDR5Bkwp.js +0 -1
  1143. package/dist/interface/assets/flowSchema-DDR5Bkwp.js.br +0 -0
  1144. package/dist/interface/assets/flowSchema-DDR5Bkwp.js.gz +0 -0
  1145. package/dist/interface/assets/intercomService-CrNJqjSQ.js +0 -1
  1146. package/dist/interface/assets/intercomService-CrNJqjSQ.js.br +0 -0
  1147. package/dist/interface/assets/intercomService-CrNJqjSQ.js.gz +0 -0
  1148. package/dist/interface/assets/main-81DFrOv-.js +0 -3
  1149. package/dist/interface/assets/main-81DFrOv-.js.br +0 -0
  1150. package/dist/interface/assets/main-81DFrOv-.js.gz +0 -0
  1151. package/dist/interface/assets/mqtt-dxw2twZb.js +0 -1
  1152. package/dist/interface/assets/mqtt-dxw2twZb.js.br +0 -0
  1153. package/dist/interface/assets/mqtt-dxw2twZb.js.gz +0 -0
  1154. package/dist/interface/assets/notifications-wddGMkjM.js +0 -1
  1155. package/dist/interface/assets/notifications-wddGMkjM.js.br +0 -0
  1156. package/dist/interface/assets/notifications-wddGMkjM.js.gz +0 -0
  1157. package/dist/interface/assets/plugins-BtRZhMzq.js +0 -1
  1158. package/dist/interface/assets/plugins-BtRZhMzq.js.br +0 -0
  1159. package/dist/interface/assets/plugins-BtRZhMzq.js.gz +0 -0
  1160. package/dist/interface/assets/rooms-BdUbpVQC.js +0 -1
  1161. package/dist/interface/assets/rooms-BdUbpVQC.js.br +0 -0
  1162. package/dist/interface/assets/rooms-BdUbpVQC.js.gz +0 -0
  1163. package/dist/interface/assets/sensorIcons-BqRrUC21.js +0 -1
  1164. package/dist/interface/assets/sensorIcons-BqRrUC21.js.br +0 -0
  1165. package/dist/interface/assets/sensorIcons-BqRrUC21.js.gz +0 -0
  1166. package/dist/interface/assets/sensorSlots-CZ1jzvW0.js +0 -1
  1167. package/dist/interface/assets/sensorSlots-CZ1jzvW0.js.br +0 -0
  1168. package/dist/interface/assets/sensorSlots-CZ1jzvW0.js.gz +0 -0
  1169. package/dist/interface/assets/sensors-vvTTj4i4.js +0 -1
  1170. package/dist/interface/assets/sensors-vvTTj4i4.js.br +0 -0
  1171. package/dist/interface/assets/sensors-vvTTj4i4.js.gz +0 -0
  1172. package/dist/interface/assets/server-tY100hue.js +0 -1
  1173. package/dist/interface/assets/server-tY100hue.js.br +0 -0
  1174. package/dist/interface/assets/server-tY100hue.js.gz +0 -0
  1175. package/dist/interface/assets/theme-BstpTBPu.js +0 -1
  1176. package/dist/interface/assets/theme-BstpTBPu.js.br +0 -0
  1177. package/dist/interface/assets/theme-BstpTBPu.js.gz +0 -0
  1178. package/dist/interface/assets/types-Bo-NWCAC.js +0 -1
  1179. package/dist/interface/assets/types-Bo-NWCAC.js.br +0 -0
  1180. package/dist/interface/assets/types-Bo-NWCAC.js.gz +0 -0
  1181. package/dist/interface/assets/types-DY8IfyYP.js +0 -1
  1182. package/dist/interface/assets/types-DY8IfyYP.js.br +0 -0
  1183. package/dist/interface/assets/types-DY8IfyYP.js.gz +0 -0
  1184. package/dist/interface/assets/types-OT17v6kT.js +0 -1
  1185. package/dist/interface/assets/types-OT17v6kT.js.br +0 -0
  1186. package/dist/interface/assets/types-OT17v6kT.js.gz +0 -0
  1187. package/dist/interface/assets/updates-Cy0UvDur.js +0 -2
  1188. package/dist/interface/assets/updates-Cy0UvDur.js.br +0 -0
  1189. package/dist/interface/assets/updates-Cy0UvDur.js.gz +0 -0
  1190. package/dist/interface/assets/useAppVersion-BDm52JIU.js +0 -1
  1191. package/dist/interface/assets/useAppVersion-BDm52JIU.js.br +0 -0
  1192. package/dist/interface/assets/useAppVersion-BDm52JIU.js.gz +0 -0
  1193. package/dist/interface/assets/useCameraOptions-qvQUXrOM.js +0 -1
  1194. package/dist/interface/assets/useCameraOptions-qvQUXrOM.js.br +0 -0
  1195. package/dist/interface/assets/useCameraOptions-qvQUXrOM.js.gz +0 -0
  1196. package/dist/interface/assets/useKeyboardInset-CQKU4-0b.js +0 -1
  1197. package/dist/interface/assets/useKeyboardInset-CQKU4-0b.js.br +0 -0
  1198. package/dist/interface/assets/useKeyboardInset-CQKU4-0b.js.gz +0 -0
  1199. package/dist/interface/assets/useLogsSocket-DW6j2kMx.js +0 -1
  1200. package/dist/interface/assets/useLogsSocket-DW6j2kMx.js.br +0 -0
  1201. package/dist/interface/assets/useLogsSocket-DW6j2kMx.js.gz +0 -0
  1202. package/dist/interface/assets/useNavLayout-Dst4SfJr.js +0 -1
  1203. package/dist/interface/assets/useNavLayout-Dst4SfJr.js.br +0 -0
  1204. package/dist/interface/assets/useNavLayout-Dst4SfJr.js.gz +0 -0
  1205. package/dist/interface/assets/usePermissions-Bh_myrIK.js +0 -1
  1206. package/dist/interface/assets/usePermissions-Bh_myrIK.js.br +0 -0
  1207. package/dist/interface/assets/usePermissions-Bh_myrIK.js.gz +0 -0
  1208. package/dist/interface/assets/usePlacementLabel-C2Prw5hU.js +0 -1
  1209. package/dist/interface/assets/usePlacementLabel-C2Prw5hU.js.br +0 -0
  1210. package/dist/interface/assets/usePlacementLabel-C2Prw5hU.js.gz +0 -0
  1211. package/dist/interface/assets/usePlacementSocket-xLhwFZgQ.js +0 -1
  1212. package/dist/interface/assets/usePlacementSocket-xLhwFZgQ.js.br +0 -0
  1213. package/dist/interface/assets/usePlacementSocket-xLhwFZgQ.js.gz +0 -0
  1214. package/dist/interface/assets/usePluginUpdates-9mTaq1uR.js +0 -1
  1215. package/dist/interface/assets/usePluginUpdates-9mTaq1uR.js.br +0 -0
  1216. package/dist/interface/assets/usePluginUpdates-9mTaq1uR.js.gz +0 -0
  1217. package/dist/interface/assets/usePluginsSocket-TdmWm307.js +0 -1
  1218. package/dist/interface/assets/usePluginsSocket-TdmWm307.js.br +0 -0
  1219. package/dist/interface/assets/usePluginsSocket-TdmWm307.js.gz +0 -0
  1220. package/dist/interface/assets/usePushRegistration-Bbf8WqlP.js +0 -2
  1221. package/dist/interface/assets/usePushRegistration-Bbf8WqlP.js.br +0 -0
  1222. package/dist/interface/assets/usePushRegistration-Bbf8WqlP.js.gz +0 -0
  1223. package/dist/interface/assets/useServerRestart-BqYQ-1Fg.js +0 -1
  1224. package/dist/interface/assets/useServerRestart-BqYQ-1Fg.js.br +0 -0
  1225. package/dist/interface/assets/useServerRestart-BqYQ-1Fg.js.gz +0 -0
  1226. package/dist/interface/assets/useTabSwipe-kd6jHGXB.js +0 -1
  1227. package/dist/interface/assets/useTabSwipe-kd6jHGXB.js.br +0 -0
  1228. package/dist/interface/assets/useTabSwipe-kd6jHGXB.js.gz +0 -0
  1229. package/dist/interface/assets/useUpdateChannel-Dfk91JvX.js +0 -1
  1230. package/dist/interface/assets/useUpdateChannel-Dfk91JvX.js.br +0 -0
  1231. package/dist/interface/assets/useUpdateChannel-Dfk91JvX.js.gz +0 -0
  1232. package/dist/interface/assets/useUpdatesSocket-fqaXJCdb.js +0 -1
  1233. package/dist/interface/assets/useUpdatesSocket-fqaXJCdb.js.br +0 -0
  1234. package/dist/interface/assets/useUpdatesSocket-fqaXJCdb.js.gz +0 -0
  1235. package/dist/interface/assets/useUserOptions-DyE_X9iJ.js +0 -1
  1236. package/dist/interface/assets/useUserOptions-DyE_X9iJ.js.br +0 -0
  1237. package/dist/interface/assets/useUserOptions-DyE_X9iJ.js.gz +0 -0
  1238. package/dist/interface/assets/vendor-cameraui-CFuqYfdj.js +0 -8
  1239. package/dist/interface/assets/vendor-cameraui-CFuqYfdj.js.br +0 -0
  1240. package/dist/interface/assets/vendor-cameraui-CFuqYfdj.js.gz +0 -0
  1241. package/dist/interface/assets/vendor-cameraui-zEcMwDN6.css +0 -1
  1242. package/dist/interface/assets/vendor-cameraui-zEcMwDN6.css.br +0 -0
  1243. package/dist/interface/assets/vendor-cameraui-zEcMwDN6.css.gz +0 -0
  1244. package/dist/interface/ha/404-GEJ7Ktz6.js +0 -60
  1245. package/dist/interface/ha/404-GEJ7Ktz6.js.br +0 -0
  1246. package/dist/interface/ha/404-GEJ7Ktz6.js.gz +0 -0
  1247. package/dist/interface/ha/About-DCpvPgLH.js +0 -55
  1248. package/dist/interface/ha/About-DCpvPgLH.js.br +0 -0
  1249. package/dist/interface/ha/About-DCpvPgLH.js.gz +0 -0
  1250. package/dist/interface/ha/Assistant-DW7Ji5Rq.js +0 -11071
  1251. package/dist/interface/ha/Assistant-DW7Ji5Rq.js.br +0 -0
  1252. package/dist/interface/ha/Assistant-DW7Ji5Rq.js.gz +0 -0
  1253. package/dist/interface/ha/Automation-igwD7Gn1.js +0 -2009
  1254. package/dist/interface/ha/Automation-igwD7Gn1.js.br +0 -0
  1255. package/dist/interface/ha/Automation-igwD7Gn1.js.gz +0 -0
  1256. package/dist/interface/ha/AutomationImport-ClhpDYCk.js +0 -206
  1257. package/dist/interface/ha/AutomationImport-ClhpDYCk.js.br +0 -0
  1258. package/dist/interface/ha/AutomationImport-ClhpDYCk.js.gz +0 -0
  1259. package/dist/interface/ha/AutomationStore-Cow2m1WS.js +0 -336
  1260. package/dist/interface/ha/AutomationStore-Cow2m1WS.js.br +0 -0
  1261. package/dist/interface/ha/AutomationStore-Cow2m1WS.js.gz +0 -0
  1262. package/dist/interface/ha/AutomationStoreDetail-DbgmNVZA.js +0 -121
  1263. package/dist/interface/ha/AutomationStoreDetail-DbgmNVZA.js.br +0 -0
  1264. package/dist/interface/ha/AutomationStoreDetail-DbgmNVZA.js.gz +0 -0
  1265. package/dist/interface/ha/Automations-DMoaF0m5.js +0 -423
  1266. package/dist/interface/ha/Automations-DMoaF0m5.js.br +0 -0
  1267. package/dist/interface/ha/Automations-DMoaF0m5.js.gz +0 -0
  1268. package/dist/interface/ha/Camera-CMJ5iJAs.js +0 -740
  1269. package/dist/interface/ha/Camera-CMJ5iJAs.js.br +0 -0
  1270. package/dist/interface/ha/Camera-CMJ5iJAs.js.gz +0 -0
  1271. package/dist/interface/ha/CameraConsole-DOG0ivnD.js +0 -52
  1272. package/dist/interface/ha/CameraConsole-DOG0ivnD.js.br +0 -0
  1273. package/dist/interface/ha/CameraConsole-DOG0ivnD.js.gz +0 -0
  1274. package/dist/interface/ha/Cameras-CTvWFekL.js +0 -1934
  1275. package/dist/interface/ha/Cameras-CTvWFekL.js.br +0 -0
  1276. package/dist/interface/ha/Cameras-CTvWFekL.js.gz +0 -0
  1277. package/dist/interface/ha/Camview-DhjdhCR6.js +0 -1368
  1278. package/dist/interface/ha/Camview-DhjdhCR6.js.br +0 -0
  1279. package/dist/interface/ha/Camview-DhjdhCR6.js.gz +0 -0
  1280. package/dist/interface/ha/Config-DvpZ-GkT.js +0 -354
  1281. package/dist/interface/ha/Config-DvpZ-GkT.js.br +0 -0
  1282. package/dist/interface/ha/Config-DvpZ-GkT.js.gz +0 -0
  1283. package/dist/interface/ha/ConfigActionAssistant-CYdWIg9z.js +0 -182
  1284. package/dist/interface/ha/ConfigActionAssistant-CYdWIg9z.js.br +0 -0
  1285. package/dist/interface/ha/ConfigActionAssistant-CYdWIg9z.js.gz +0 -0
  1286. package/dist/interface/ha/ConfigActionCameraControl-CxBJelQl.js +0 -152
  1287. package/dist/interface/ha/ConfigActionCameraControl-CxBJelQl.js.br +0 -0
  1288. package/dist/interface/ha/ConfigActionCameraControl-CxBJelQl.js.gz +0 -0
  1289. package/dist/interface/ha/ConfigActionHttp-B6gQwkvP.js +0 -134
  1290. package/dist/interface/ha/ConfigActionHttp-B6gQwkvP.js.br +0 -0
  1291. package/dist/interface/ha/ConfigActionHttp-B6gQwkvP.js.gz +0 -0
  1292. package/dist/interface/ha/ConfigActionImageInput-DJVauVUw.js +0 -170
  1293. package/dist/interface/ha/ConfigActionImageInput-DJVauVUw.js.br +0 -0
  1294. package/dist/interface/ha/ConfigActionImageInput-DJVauVUw.js.gz +0 -0
  1295. package/dist/interface/ha/ConfigActionMqtt-BUa-HrP3.js +0 -78
  1296. package/dist/interface/ha/ConfigActionMqtt-BUa-HrP3.js.br +0 -0
  1297. package/dist/interface/ha/ConfigActionMqtt-BUa-HrP3.js.gz +0 -0
  1298. package/dist/interface/ha/ConfigActionNotification-CHJDWRRJ.js +0 -198
  1299. package/dist/interface/ha/ConfigActionNotification-CHJDWRRJ.js.br +0 -0
  1300. package/dist/interface/ha/ConfigActionNotification-CHJDWRRJ.js.gz +0 -0
  1301. package/dist/interface/ha/ConfigActionNotificationControl-BKMaUQZT.js +0 -111
  1302. package/dist/interface/ha/ConfigActionNotificationControl-BKMaUQZT.js.br +0 -0
  1303. package/dist/interface/ha/ConfigActionNotificationControl-BKMaUQZT.js.gz +0 -0
  1304. package/dist/interface/ha/ConfigActionOutput-cEels_Xm.js +0 -98
  1305. package/dist/interface/ha/ConfigActionOutput-cEels_Xm.js.br +0 -0
  1306. package/dist/interface/ha/ConfigActionOutput-cEels_Xm.js.gz +0 -0
  1307. package/dist/interface/ha/ConfigActionPlugin-B-x3ZGbV.js +0 -171
  1308. package/dist/interface/ha/ConfigActionPlugin-B-x3ZGbV.js.br +0 -0
  1309. package/dist/interface/ha/ConfigActionPlugin-B-x3ZGbV.js.gz +0 -0
  1310. package/dist/interface/ha/ConfigActionSensor-2N2FUkVu.js +0 -138
  1311. package/dist/interface/ha/ConfigActionSensor-2N2FUkVu.js.br +0 -0
  1312. package/dist/interface/ha/ConfigActionSensor-2N2FUkVu.js.gz +0 -0
  1313. package/dist/interface/ha/ConfigActionSnapshot-DfNDWDx9.js +0 -45
  1314. package/dist/interface/ha/ConfigActionSnapshot-DfNDWDx9.js.br +0 -0
  1315. package/dist/interface/ha/ConfigActionSnapshot-DfNDWDx9.js.gz +0 -0
  1316. package/dist/interface/ha/ConfigActionVariable-v0e-sIMS.js +0 -88
  1317. package/dist/interface/ha/ConfigActionVariable-v0e-sIMS.js.br +0 -0
  1318. package/dist/interface/ha/ConfigActionVariable-v0e-sIMS.js.gz +0 -0
  1319. package/dist/interface/ha/ConfigConditionIfElse-C0WandPI.js +0 -124
  1320. package/dist/interface/ha/ConfigConditionIfElse-C0WandPI.js.br +0 -0
  1321. package/dist/interface/ha/ConfigConditionIfElse-C0WandPI.js.gz +0 -0
  1322. package/dist/interface/ha/ConfigConditionSensorState-BeLtrLOX.js +0 -190
  1323. package/dist/interface/ha/ConfigConditionSensorState-BeLtrLOX.js.br +0 -0
  1324. package/dist/interface/ha/ConfigConditionSensorState-BeLtrLOX.js.gz +0 -0
  1325. package/dist/interface/ha/ConfigConditionSwitch-B-M1Vqkg.js +0 -128
  1326. package/dist/interface/ha/ConfigConditionSwitch-B-M1Vqkg.js.br +0 -0
  1327. package/dist/interface/ha/ConfigConditionSwitch-B-M1Vqkg.js.gz +0 -0
  1328. package/dist/interface/ha/ConfigSensorValueInput-DtZnPmIW.js +0 -173
  1329. package/dist/interface/ha/ConfigSensorValueInput-DtZnPmIW.js.br +0 -0
  1330. package/dist/interface/ha/ConfigSensorValueInput-DtZnPmIW.js.gz +0 -0
  1331. package/dist/interface/ha/ConfigTriggerDetection-CgOWAkNy.js +0 -241
  1332. package/dist/interface/ha/ConfigTriggerDetection-CgOWAkNy.js.br +0 -0
  1333. package/dist/interface/ha/ConfigTriggerDetection-CgOWAkNy.js.gz +0 -0
  1334. package/dist/interface/ha/ConfigTriggerGeofence-BPRYWDZy.js +0 -4836
  1335. package/dist/interface/ha/ConfigTriggerGeofence-BPRYWDZy.js.br +0 -0
  1336. package/dist/interface/ha/ConfigTriggerGeofence-BPRYWDZy.js.gz +0 -0
  1337. package/dist/interface/ha/ConfigTriggerMqtt-DPUaal2K.js +0 -134
  1338. package/dist/interface/ha/ConfigTriggerMqtt-DPUaal2K.js.br +0 -0
  1339. package/dist/interface/ha/ConfigTriggerMqtt-DPUaal2K.js.gz +0 -0
  1340. package/dist/interface/ha/ConfigTriggerSensor-DJRM2X3t.js +0 -73
  1341. package/dist/interface/ha/ConfigTriggerSensor-DJRM2X3t.js.br +0 -0
  1342. package/dist/interface/ha/ConfigTriggerSensor-DJRM2X3t.js.gz +0 -0
  1343. package/dist/interface/ha/ConfigTriggerSystem-Df7sI0VY.js +0 -192
  1344. package/dist/interface/ha/ConfigTriggerSystem-Df7sI0VY.js.br +0 -0
  1345. package/dist/interface/ha/ConfigTriggerSystem-Df7sI0VY.js.gz +0 -0
  1346. package/dist/interface/ha/ConfigTriggerWebhook-W9NUmjTn.js +0 -94
  1347. package/dist/interface/ha/ConfigTriggerWebhook-W9NUmjTn.js.br +0 -0
  1348. package/dist/interface/ha/ConfigTriggerWebhook-W9NUmjTn.js.gz +0 -0
  1349. package/dist/interface/ha/Console-BwA46zsU.js +0 -347
  1350. package/dist/interface/ha/Console-BwA46zsU.js.br +0 -0
  1351. package/dist/interface/ha/Console-BwA46zsU.js.gz +0 -0
  1352. package/dist/interface/ha/CuiAutomationRunsDialog-B07r5tMe.js +0 -129
  1353. package/dist/interface/ha/CuiAutomationRunsDialog-B07r5tMe.js.br +0 -0
  1354. package/dist/interface/ha/CuiAutomationRunsDialog-B07r5tMe.js.gz +0 -0
  1355. package/dist/interface/ha/CuiBottomSheet-B5uQgEqB.js +0 -270
  1356. package/dist/interface/ha/CuiBottomSheet-B5uQgEqB.js.br +0 -0
  1357. package/dist/interface/ha/CuiBottomSheet-B5uQgEqB.js.gz +0 -0
  1358. package/dist/interface/ha/CuiCameraRecordings-xoThi8Kj.js +0 -575
  1359. package/dist/interface/ha/CuiCameraRecordings-xoThi8Kj.js.br +0 -0
  1360. package/dist/interface/ha/CuiCameraRecordings-xoThi8Kj.js.gz +0 -0
  1361. package/dist/interface/ha/CuiCameraShares-BzvAMAbb.js +0 -120
  1362. package/dist/interface/ha/CuiCameraShares-BzvAMAbb.js.br +0 -0
  1363. package/dist/interface/ha/CuiCameraShares-BzvAMAbb.js.gz +0 -0
  1364. package/dist/interface/ha/CuiCameraTable-BIgtm8mF.js +0 -105
  1365. package/dist/interface/ha/CuiCameraTable-BIgtm8mF.js.br +0 -0
  1366. package/dist/interface/ha/CuiCameraTable-BIgtm8mF.js.gz +0 -0
  1367. package/dist/interface/ha/CuiChartTable-DNcdhXqp.js +0 -6797
  1368. package/dist/interface/ha/CuiChartTable-DNcdhXqp.js.br +0 -0
  1369. package/dist/interface/ha/CuiChartTable-DNcdhXqp.js.gz +0 -0
  1370. package/dist/interface/ha/CuiConsole-CpD0nhAD.js +0 -234
  1371. package/dist/interface/ha/CuiConsole-CpD0nhAD.js.br +0 -0
  1372. package/dist/interface/ha/CuiConsole-CpD0nhAD.js.gz +0 -0
  1373. package/dist/interface/ha/CuiDataTable-CTRZF9Ia.js +0 -8860
  1374. package/dist/interface/ha/CuiDataTable-CTRZF9Ia.js.br +0 -0
  1375. package/dist/interface/ha/CuiDataTable-CTRZF9Ia.js.gz +0 -0
  1376. package/dist/interface/ha/CuiMoveCodeForm-B0MKxC53.js +0 -89
  1377. package/dist/interface/ha/CuiMoveCodeForm-B0MKxC53.js.br +0 -0
  1378. package/dist/interface/ha/CuiMoveCodeForm-B0MKxC53.js.gz +0 -0
  1379. package/dist/interface/ha/CuiPluginCard-CHZ4DaRv.js +0 -778
  1380. package/dist/interface/ha/CuiPluginCard-CHZ4DaRv.js.br +0 -0
  1381. package/dist/interface/ha/CuiPluginCard-CHZ4DaRv.js.gz +0 -0
  1382. package/dist/interface/ha/CuiPluginOAuthButton-uZT2viFR.js +0 -74
  1383. package/dist/interface/ha/CuiPluginOAuthButton-uZT2viFR.js.br +0 -0
  1384. package/dist/interface/ha/CuiPluginOAuthButton-uZT2viFR.js.gz +0 -0
  1385. package/dist/interface/ha/CuiPluginTrustBadge-1MhUw5es.js +0 -122
  1386. package/dist/interface/ha/CuiPluginTrustBadge-1MhUw5es.js.br +0 -0
  1387. package/dist/interface/ha/CuiPluginTrustBadge-1MhUw5es.js.gz +0 -0
  1388. package/dist/interface/ha/CuiQRCode-Grr7tjGw.js +0 -86
  1389. package/dist/interface/ha/CuiQRCode-Grr7tjGw.js.br +0 -0
  1390. package/dist/interface/ha/CuiQRCode-Grr7tjGw.js.gz +0 -0
  1391. package/dist/interface/ha/CuiSchema-BHm9vNRI.js +0 -952
  1392. package/dist/interface/ha/CuiSchema-BHm9vNRI.js.br +0 -0
  1393. package/dist/interface/ha/CuiSchema-BHm9vNRI.js.gz +0 -0
  1394. package/dist/interface/ha/CuiTopNavbarItem-KI3ljdF5.js +0 -141
  1395. package/dist/interface/ha/CuiTopNavbarItem-KI3ljdF5.js.br +0 -0
  1396. package/dist/interface/ha/CuiTopNavbarItem-KI3ljdF5.js.gz +0 -0
  1397. package/dist/interface/ha/Faces-Qjkjd__-.js +0 -1659
  1398. package/dist/interface/ha/Faces-Qjkjd__-.js.br +0 -0
  1399. package/dist/interface/ha/Faces-Qjkjd__-.js.gz +0 -0
  1400. package/dist/interface/ha/FirstSteps-CMPZcVGm.js +0 -254
  1401. package/dist/interface/ha/FirstSteps-CMPZcVGm.js.br +0 -0
  1402. package/dist/interface/ha/FirstSteps-CMPZcVGm.js.gz +0 -0
  1403. package/dist/interface/ha/Floorplan-CrozNTqV.js +0 -3707
  1404. package/dist/interface/ha/Floorplan-CrozNTqV.js.br +0 -0
  1405. package/dist/interface/ha/Floorplan-CrozNTqV.js.gz +0 -0
  1406. package/dist/interface/ha/Instances-DBwRzClE.js +0 -670
  1407. package/dist/interface/ha/Instances-DBwRzClE.js.br +0 -0
  1408. package/dist/interface/ha/Instances-DBwRzClE.js.gz +0 -0
  1409. package/dist/interface/ha/Logs-Ua7xzSrk.js +0 -417
  1410. package/dist/interface/ha/Logs-Ua7xzSrk.js.br +0 -0
  1411. package/dist/interface/ha/Logs-Ua7xzSrk.js.gz +0 -0
  1412. package/dist/interface/ha/Menu-BUIjRr5o.js +0 -159
  1413. package/dist/interface/ha/Menu-BUIjRr5o.js.br +0 -0
  1414. package/dist/interface/ha/Menu-BUIjRr5o.js.gz +0 -0
  1415. package/dist/interface/ha/Metrics-BHEKh6IC.js +0 -1484
  1416. package/dist/interface/ha/Metrics-BHEKh6IC.js.br +0 -0
  1417. package/dist/interface/ha/Metrics-BHEKh6IC.js.gz +0 -0
  1418. package/dist/interface/ha/OAuthDeviceFlow-C_bEkfff.js +0 -112
  1419. package/dist/interface/ha/OAuthDeviceFlow-C_bEkfff.js.br +0 -9
  1420. package/dist/interface/ha/OAuthDeviceFlow-C_bEkfff.js.gz +0 -0
  1421. package/dist/interface/ha/Plugin-CVxXmLva.js +0 -1746
  1422. package/dist/interface/ha/Plugin-CVxXmLva.js.br +0 -0
  1423. package/dist/interface/ha/Plugin-CVxXmLva.js.gz +0 -0
  1424. package/dist/interface/ha/PluginConsole-ChOzllUF.js +0 -56
  1425. package/dist/interface/ha/PluginConsole-ChOzllUF.js.br +0 -0
  1426. package/dist/interface/ha/PluginConsole-ChOzllUF.js.gz +0 -0
  1427. package/dist/interface/ha/PluginDetail-DkM9ow6C.js +0 -804
  1428. package/dist/interface/ha/PluginDetail-DkM9ow6C.js.br +0 -0
  1429. package/dist/interface/ha/PluginDetail-DkM9ow6C.js.gz +0 -0
  1430. package/dist/interface/ha/PluginSearch-CSCTK5iK.js +0 -462
  1431. package/dist/interface/ha/PluginSearch-CSCTK5iK.js.br +0 -0
  1432. package/dist/interface/ha/PluginSearch-CSCTK5iK.js.gz +0 -0
  1433. package/dist/interface/ha/PluginUpdateProgress-DaRg6gt1.js +0 -34
  1434. package/dist/interface/ha/PluginUpdateProgress-DaRg6gt1.js.br +0 -0
  1435. package/dist/interface/ha/PluginUpdateProgress-DaRg6gt1.js.gz +0 -0
  1436. package/dist/interface/ha/Plugins-BmIKxTQu.js +0 -697
  1437. package/dist/interface/ha/Plugins-BmIKxTQu.js.br +0 -0
  1438. package/dist/interface/ha/Plugins-BmIKxTQu.js.gz +0 -0
  1439. package/dist/interface/ha/Recordings-CU6D4Een.js +0 -1916
  1440. package/dist/interface/ha/Recordings-CU6D4Een.js.br +0 -0
  1441. package/dist/interface/ha/Recordings-CU6D4Een.js.gz +0 -0
  1442. package/dist/interface/ha/SensorEdit-BOQCEFrt.js +0 -1466
  1443. package/dist/interface/ha/SensorEdit-BOQCEFrt.js.br +0 -0
  1444. package/dist/interface/ha/SensorEdit-BOQCEFrt.js.gz +0 -0
  1445. package/dist/interface/ha/SensorHistory-Bslkq7Kj.js +0 -54
  1446. package/dist/interface/ha/SensorHistory-Bslkq7Kj.js.br +0 -0
  1447. package/dist/interface/ha/SensorHistory-Bslkq7Kj.js.gz +0 -0
  1448. package/dist/interface/ha/Sensors-uoK_jFo8.js +0 -1400
  1449. package/dist/interface/ha/Sensors-uoK_jFo8.js.br +0 -0
  1450. package/dist/interface/ha/Sensors-uoK_jFo8.js.gz +0 -0
  1451. package/dist/interface/ha/Settings-3vjjQ3o3.js +0 -218
  1452. package/dist/interface/ha/Settings-3vjjQ3o3.js.br +0 -0
  1453. package/dist/interface/ha/Settings-3vjjQ3o3.js.gz +0 -0
  1454. package/dist/interface/ha/SettingsAccount-DYU_xa5v.js +0 -968
  1455. package/dist/interface/ha/SettingsAccount-DYU_xa5v.js.br +0 -0
  1456. package/dist/interface/ha/SettingsAccount-DYU_xa5v.js.gz +0 -0
  1457. package/dist/interface/ha/SettingsAppearance-CpgnJYLf.js +0 -201
  1458. package/dist/interface/ha/SettingsAppearance-CpgnJYLf.js.br +0 -0
  1459. package/dist/interface/ha/SettingsAppearance-CpgnJYLf.js.gz +0 -0
  1460. package/dist/interface/ha/SettingsAssistant-CXrllafp.js +0 -2134
  1461. package/dist/interface/ha/SettingsAssistant-CXrllafp.js.br +0 -0
  1462. package/dist/interface/ha/SettingsAssistant-CXrllafp.js.gz +0 -0
  1463. package/dist/interface/ha/SettingsBackup-BbEfaQ1e.js +0 -1047
  1464. package/dist/interface/ha/SettingsBackup-BbEfaQ1e.js.br +0 -0
  1465. package/dist/interface/ha/SettingsBackup-BbEfaQ1e.js.gz +0 -0
  1466. package/dist/interface/ha/SettingsList-Dieayk1W.js +0 -40
  1467. package/dist/interface/ha/SettingsList-Dieayk1W.js.br +0 -0
  1468. package/dist/interface/ha/SettingsList-Dieayk1W.js.gz +0 -0
  1469. package/dist/interface/ha/SettingsMqtt-DagskW76.js +0 -512
  1470. package/dist/interface/ha/SettingsMqtt-DagskW76.js.br +0 -0
  1471. package/dist/interface/ha/SettingsMqtt-DagskW76.js.gz +0 -0
  1472. package/dist/interface/ha/SettingsNotifications-B3bcsE4m.js +0 -514
  1473. package/dist/interface/ha/SettingsNotifications-B3bcsE4m.js.br +0 -0
  1474. package/dist/interface/ha/SettingsNotifications-B3bcsE4m.js.gz +0 -0
  1475. package/dist/interface/ha/SettingsRecordings-UqTgc63f.js +0 -152
  1476. package/dist/interface/ha/SettingsRecordings-UqTgc63f.js.br +0 -0
  1477. package/dist/interface/ha/SettingsRecordings-UqTgc63f.js.gz +0 -0
  1478. package/dist/interface/ha/SettingsRemote-Biy7URGf.js +0 -1523
  1479. package/dist/interface/ha/SettingsRemote-Biy7URGf.js.br +0 -0
  1480. package/dist/interface/ha/SettingsRemote-Biy7URGf.js.gz +0 -0
  1481. package/dist/interface/ha/SettingsSystem-6geFXXRq.js +0 -382
  1482. package/dist/interface/ha/SettingsSystem-6geFXXRq.js.br +0 -0
  1483. package/dist/interface/ha/SettingsSystem-6geFXXRq.js.gz +0 -0
  1484. package/dist/interface/ha/SettingsUsers-BXUZX3Dv.js +0 -370
  1485. package/dist/interface/ha/SettingsUsers-BXUZX3Dv.js.br +0 -0
  1486. package/dist/interface/ha/SettingsUsers-BXUZX3Dv.js.gz +0 -0
  1487. package/dist/interface/ha/Terminal-CAsT_c5K.js +0 -611
  1488. package/dist/interface/ha/Terminal-CAsT_c5K.js.br +0 -0
  1489. package/dist/interface/ha/Terminal-CAsT_c5K.js.gz +0 -0
  1490. package/dist/interface/ha/Training-Cs-X3tTu.js +0 -1829
  1491. package/dist/interface/ha/Training-Cs-X3tTu.js.br +0 -0
  1492. package/dist/interface/ha/Training-Cs-X3tTu.js.gz +0 -0
  1493. package/dist/interface/ha/Updates-BRwFnbJd.js +0 -357
  1494. package/dist/interface/ha/Updates-BRwFnbJd.js.br +0 -0
  1495. package/dist/interface/ha/Updates-BRwFnbJd.js.gz +0 -0
  1496. package/dist/interface/ha/VariableInput-q03fAY9F.js +0 -52
  1497. package/dist/interface/ha/VariableInput-q03fAY9F.js.br +0 -0
  1498. package/dist/interface/ha/VariableInput-q03fAY9F.js.gz +0 -0
  1499. package/dist/interface/ha/VersionsHandler-DDk482uv.js +0 -221
  1500. package/dist/interface/ha/VersionsHandler-DDk482uv.js.br +0 -0
  1501. package/dist/interface/ha/VersionsHandler-DDk482uv.js.gz +0 -0
  1502. package/dist/interface/ha/VirtualSensorCreate-CdxwTlbd.js +0 -102
  1503. package/dist/interface/ha/VirtualSensorCreate-CdxwTlbd.js.br +0 -0
  1504. package/dist/interface/ha/VirtualSensorCreate-CdxwTlbd.js.gz +0 -0
  1505. package/dist/interface/ha/Workers-D4MZqg97.js +0 -1270
  1506. package/dist/interface/ha/Workers-D4MZqg97.js.br +0 -0
  1507. package/dist/interface/ha/Workers-D4MZqg97.js.gz +0 -0
  1508. package/dist/interface/ha/api-CNQ6bAly.js +0 -29
  1509. package/dist/interface/ha/api-CNQ6bAly.js.br +0 -0
  1510. package/dist/interface/ha/api-CNQ6bAly.js.gz +0 -0
  1511. package/dist/interface/ha/api-xWofDMK3.js +0 -69075
  1512. package/dist/interface/ha/api-xWofDMK3.js.br +0 -0
  1513. package/dist/interface/ha/api-xWofDMK3.js.gz +0 -0
  1514. package/dist/interface/ha/assistant-Dwelc7yC.js +0 -349
  1515. package/dist/interface/ha/assistant-Dwelc7yC.js.br +0 -0
  1516. package/dist/interface/ha/assistant-Dwelc7yC.js.gz +0 -0
  1517. package/dist/interface/ha/auth-CUnSYAeS.js +0 -271
  1518. package/dist/interface/ha/auth-CUnSYAeS.js.br +0 -0
  1519. package/dist/interface/ha/auth-CUnSYAeS.js.gz +0 -0
  1520. package/dist/interface/ha/automationBlueprint-ZizU2N5n.js +0 -250
  1521. package/dist/interface/ha/automationBlueprint-ZizU2N5n.js.br +0 -0
  1522. package/dist/interface/ha/automationBlueprint-ZizU2N5n.js.gz +0 -0
  1523. package/dist/interface/ha/cards-DZEFY6FW.js +0 -13296
  1524. package/dist/interface/ha/cards-DZEFY6FW.js.br +0 -0
  1525. package/dist/interface/ha/cards-DZEFY6FW.js.gz +0 -0
  1526. package/dist/interface/ha/config-nCVNp-au.js +0 -116
  1527. package/dist/interface/ha/config-nCVNp-au.js.br +0 -0
  1528. package/dist/interface/ha/config-nCVNp-au.js.gz +0 -0
  1529. package/dist/interface/ha/dist-rrvfmgLd.js +0 -4425
  1530. package/dist/interface/ha/dist-rrvfmgLd.js.br +0 -0
  1531. package/dist/interface/ha/dist-rrvfmgLd.js.gz +0 -0
  1532. package/dist/interface/ha/mqtt-D_nDqDyk.js +0 -56
  1533. package/dist/interface/ha/mqtt-D_nDqDyk.js.br +0 -3
  1534. package/dist/interface/ha/mqtt-D_nDqDyk.js.gz +0 -0
  1535. package/dist/interface/ha/notifications-BTJTCdXR.js +0 -159
  1536. package/dist/interface/ha/notifications-BTJTCdXR.js.br +0 -0
  1537. package/dist/interface/ha/notifications-BTJTCdXR.js.gz +0 -0
  1538. package/dist/interface/ha/plugins-C7XSgstR.js +0 -592
  1539. package/dist/interface/ha/plugins-C7XSgstR.js.br +0 -0
  1540. package/dist/interface/ha/plugins-C7XSgstR.js.gz +0 -0
  1541. package/dist/interface/ha/rooms-La0MdVF7.js +0 -45
  1542. package/dist/interface/ha/rooms-La0MdVF7.js.br +0 -0
  1543. package/dist/interface/ha/rooms-La0MdVF7.js.gz +0 -0
  1544. package/dist/interface/ha/server-CElgG4N0.js +0 -244
  1545. package/dist/interface/ha/server-CElgG4N0.js.br +0 -0
  1546. package/dist/interface/ha/server-CElgG4N0.js.gz +0 -0
  1547. package/dist/interface/ha/speeddial-C70QgR0r.js +0 -941
  1548. package/dist/interface/ha/speeddial-C70QgR0r.js.br +0 -0
  1549. package/dist/interface/ha/speeddial-C70QgR0r.js.gz +0 -0
  1550. package/dist/interface/ha/types-zKLgkZPc.js +0 -1073
  1551. package/dist/interface/ha/types-zKLgkZPc.js.br +0 -0
  1552. package/dist/interface/ha/types-zKLgkZPc.js.gz +0 -0
  1553. package/dist/interface/ha/updates-b6EERiem.js +0 -414
  1554. package/dist/interface/ha/updates-b6EERiem.js.br +0 -0
  1555. package/dist/interface/ha/updates-b6EERiem.js.gz +0 -0
  1556. package/dist/interface/ha/useAppVersion-DwqaMEI5.js +0 -22
  1557. package/dist/interface/ha/useAppVersion-DwqaMEI5.js.br +0 -0
  1558. package/dist/interface/ha/useAppVersion-DwqaMEI5.js.gz +0 -0
  1559. package/dist/interface/ha/useKeyboardInset-Crl1GPan.js +0 -27
  1560. package/dist/interface/ha/useKeyboardInset-Crl1GPan.js.br +0 -0
  1561. package/dist/interface/ha/useKeyboardInset-Crl1GPan.js.gz +0 -0
  1562. package/dist/interface/ha/useLogsSocket-BfmhLRN4.js +0 -74
  1563. package/dist/interface/ha/useLogsSocket-BfmhLRN4.js.br +0 -0
  1564. package/dist/interface/ha/useLogsSocket-BfmhLRN4.js.gz +0 -0
  1565. package/dist/interface/ha/useNavLayout-ue6sHO61.js +0 -125
  1566. package/dist/interface/ha/useNavLayout-ue6sHO61.js.br +0 -0
  1567. package/dist/interface/ha/useNavLayout-ue6sHO61.js.gz +0 -0
  1568. package/dist/interface/ha/usePlacementLabel-DZMzFDB5.js +0 -49
  1569. package/dist/interface/ha/usePlacementLabel-DZMzFDB5.js.br +0 -0
  1570. package/dist/interface/ha/usePlacementLabel-DZMzFDB5.js.gz +0 -0
  1571. package/dist/interface/ha/useTabSwipe-CZHGpGVQ.js +0 -45
  1572. package/dist/interface/ha/useTabSwipe-CZHGpGVQ.js.br +0 -0
  1573. package/dist/interface/ha/useTabSwipe-CZHGpGVQ.js.gz +0 -0
  1574. package/dist/interface/ha/useUpdateChannel-Rn843zLL.js +0 -22
  1575. package/dist/interface/ha/useUpdateChannel-Rn843zLL.js.br +0 -0
  1576. package/dist/interface/ha/useUpdateChannel-Rn843zLL.js.gz +0 -0
  1577. package/dist/interface/ha/useUpdatesSocket-CGOVegyc.js +0 -34
  1578. package/dist/interface/ha/useUpdatesSocket-CGOVegyc.js.br +0 -0
  1579. package/dist/interface/ha/useUpdatesSocket-CGOVegyc.js.gz +0 -0
  1580. package/dist/interface/ha/useUserOptions-ydSmcJA8.js +0 -15
  1581. package/dist/interface/ha/useUserOptions-ydSmcJA8.js.br +0 -0
  1582. package/dist/interface/ha/useUserOptions-ydSmcJA8.js.gz +0 -0
@@ -1 +1 @@
1
- [{"page":"admin/assistant-setup","title":"Assistant setup","heading":"Assistant setup","anchor":"","text":"Everything the assistant needs sits under Settings → Assistant, admins only: the models it may use, which plugins may use them too, what bounds a run, and the endpoints other AI tools talk to."},{"page":"admin/assistant-setup","title":"Assistant setup","heading":"Choosing a model","anchor":"choosing-a-model","text":"The assistant uses models you bring; nothing is sent to camera.ui. Add model takes as many as you like, local and cloud side by side.\n\n- Ollama. Runs on your own hardware, nothing leaves your network. Install Ollama, pull a model that supports tool calling, and enter the Server URL if Ollama does not run on the camera.ui machine.\n- OpenAI-compatible server. Any server speaking the OpenAI chat completions API: LM Studio, vLLM, LiteLLM, llama.cpp. Enter its Server URL and, if it requires one, an API key.\n- OpenAI, Anthropic, Google Gemini, OpenRouter. Cloud providers. Paste the API key of your account, or reuse the key of a model you already added for the same provider. The key is stored encrypted on the server and never reaches the browser or a plugin. The provider bills you per token.\n- A plugin. Plugin models show up in the provider list, Apple LLM (plugin) for example, and answer on the machine the plugin runs on. The Apple LLM plugin needs a Mac with Apple Silicon and Apple Intelligence (macOS 26, pictures from macOS 27). It holds only a short conversation: fine for questions about cameras and the docs, weak on long multi-step tasks.\n\nPick the Model from the list the provider offers (the arrow loads it) or type its name. Saving tests the model with two short requests: whether it calls a tool, and whether it accepts a picture. The result shows as badges in the list (Tools, No tools, Pictures, No pictures, Test failed), and saving again repeats the test.\n\nThe default model answers wherever nothing else is picked: conversations without a profile, scheduled prompts, the recordings search and what the assistant remembers. The star next to a model makes it the default; with none picked, the first one in the list answers. Deleting a model moves the profiles and plugins that used it to the default.\n\nA model can be kept for admins: switch off Available to users in its dialog and the list marks it Admins only. What users run then falls back to the default model, or to the first one open to them. Without such a model the assistant counts as not set up for them.\n\nSend images to the model is a switch per model. With it on, a snapshot the assistant takes or an event picture from the recordings goes to the model so it can describe what it sees, which with a cloud provider means the picture leaves your machine. The pictures are always shown in the chat, whether the model receives them or not."},{"page":"admin/assistant-setup","title":"Assistant setup","heading":"Small models and the context window","anchor":"small-models-and-the-context-window","text":"Context window in the model dialog says how much this model holds. A plugin model brings the number itself, for everything else it is empty and the context budget from the general settings applies. Set it for a local or small model, 8192 for an 8k model for example, and camera.ui plans the request around it: the procedures move behind a load_skill call, the feature list gives way to a hint at the documentation, and tools the question did not start from are listed by name and fetched when the model wants them. The same happens on a large model in a big installation, where many plugins and connected servers push the tool list past what the window can carry.\n\nPick the tools first in the same dialog helps models that get lost in a long tool list: one extra call per question asks the model which tools it needs, and only those are offered, with a short set of rules. A plugin model switches it on by itself when it needs it; leave it off for large models."},{"page":"admin/assistant-setup","title":"Assistant setup","heading":"Models without tools or pictures","anchor":"models-without-tools-or-pictures","text":"Nothing is switched off for the user: every model can be picked, the assistant adapts and says what is missing.\n\n- No tools. The model can only chat; questions about cameras, recordings or settings stay unanswered.\n- No pictures. Snapshots and event pictures still appear in the chat, the model gets the text of the tool results instead.\n\nSome models pass both tests and still call tools unreliably, with invented ids for example. When answers come back empty or wrong, try a larger model."},{"page":"admin/assistant-setup","title":"Assistant setup","heading":"Plugin access","anchor":"plugin-access","text":"Plugins can use your models too, the NVR plugin for event descriptions and to double-check text alerts. Under Plugin access pick a model per plugin or No access. A text alert can check its pictures with another model, the NVR plugin still needs access for that. The plugin sends its request to camera.ui, which runs it on that model, so the key stays on the server and a plugin on a worker needs no internet access of its own. Its tokens show up under Usage with the plugin's name. The list is a decision, not a protection: plugins run with the rights of camera.ui."},{"page":"admin/assistant-setup","title":"Assistant setup","heading":"Thinking, cost and limits","anchor":"thinking-cost-and-limits","text":"Thinking sets how long the model reasons before it answers. *Model default* sends nothing and leaves it to the provider, *Off* asks for a direct answer, *Brief* and *Thorough* request short or long reasoning. The setting is mapped to what each provider offers, and a model without reasoning ignores it. When the model shares its reasoning, the answer shows a collapsible Thinking block.\n\nUnder every answer a small line shows the tokens sent and received, the time it took, the output speed and, for a model with a known list price, an estimated cost. Usage adds it up per user or plugin, model and month, chats and scheduled prompts together. The estimate uses public list prices and the tokens the provider reported; the invoice of your provider is what counts.\n\nThe Behavior card bounds a run:\n\n- Model turns per question. How often the model may call tools and think again before it has to answer. Default 8.\n- Tool calls per question. A hard stop against a model stuck in a loop. Default 25.\n- Context budget. How much of a long conversation the model sees. Ollama models get a context window of this size plus headroom for the instructions and the answer, so lower it on small hardware. Without it Ollama would use its 4k default and cut off the instructions, and the assistant would then claim it cannot look at your cameras.\n- Conversations kept per user and Pictures kept per conversation. What the history takes on disk. Older ones go first. Defaults 50 and 24.\n- Additional instructions. Appended to the built-in instructions, for house rules and camera hints, for example which camera points at the street.\n- Remember facts about users. Lets the assistant keep facts like names across conversations. On by default, and every user sees and clears their own list in the chat. A model with a window under 16k tokens only remembers what you ask it to remember, nothing it picks up on its own.\n\nReset to defaults puts these values back, the additional instructions stay."},{"page":"admin/assistant-setup","title":"Assistant setup","heading":"Shell commands","anchor":"shell-commands","text":"Off by default. Shell commands lets admins ask the assistant for host diagnostics the other tools cannot answer: disk usage, whether a camera answers on the network, processes, container logs. The assistant runs one command at a time on the server that hosts camera.ui, and every command is shown with the exact command line to allow, edit or reject before it runs. Output is capped, a command is killed after 30 seconds by default, and the tool never appears in the MCP server, in scheduled prompts or in automations. What you allow runs with the rights of the camera.ui process. While the switch is off and a question would need it, the answer carries the switch, so an admin can turn it on in the chat."},{"page":"admin/assistant-setup","title":"Assistant setup","heading":"MCP: use the instance from other AI tools","anchor":"mcp-use-the-instance-from-other-ai-tools","text":"The assistant's tools are also available over the Model Context Protocol, so Claude Code, Claude Desktop, Cursor and similar tools can ask your instance directly, with their own model. Turn on Enable MCP endpoint, create an API token under Account, and paste the setup snippet the page shows for your client. The endpoint is /api/assistant/mcp, authenticated with the token as bearer.\n\nBy default only reading tools are offered: cameras, snapshots, system status, recordings, the read side of the API. Allow changes adds the tools that change something. camera.ui cannot ask for confirmation over MCP, so the client is responsible for that; Claude Code and Claude Desktop ask before every tool call. The tools follow the role of the token owner, a user account never sees admin tools. The endpoint is independent of the chat model, so it works even when no model is configured."},{"page":"admin/assistant-setup","title":"Assistant setup","heading":"External MCP servers","anchor":"external-mcp-servers","text":"Other systems that offer an MCP server can lend the assistant their tools. Add one under External MCP servers with a name, its URL and an access token; the token is stored encrypted. The state next to the name shows whether the server answered and how many tools it offers. Accept a self-signed certificate is there for servers in your own network. Before a tool of such a server runs, the user confirms it, unless the server marks the tool as read-only. Many servers mark nothing, Home Assistant for example. Under Tools, the tab of the server has the field Run without asking: the tools picked there run without confirmation, in scheduled prompts and automations too, so pick only tools that read. In the chat these tools form their own group that can be switched off per conversation.\n\nFor Home Assistant enable the Model Context Protocol Server integration, create a long-lived access token in your Home Assistant profile and enter http://:8123/api/mcp as the URL. The assistant can then read states and switch lights, scenes and climate through Home Assistant's own tools, and it can wire them into automations."},{"page":"admin/assistant-setup","title":"Assistant setup","heading":"Next steps","anchor":"next-steps","text":"Assistant — what it can do and how it is used day to day."},{"page":"admin/backup","title":"Backup & restore","heading":"Backup & restore","anchor":"","text":"Backups live in Settings → Backup (admins only) and restore on any platform."},{"page":"admin/backup","title":"Backup & restore","heading":"Creating a backup","anchor":"creating-a-backup","text":"Choose Download backup archive to save a single encrypted archive (.cuibackup) of your camera.ui storage directory: configuration, database, user accounts, and plugin data. Recordings are not included.[^ignore] The server packs the archive first and needs about as much free space as the data in it.[^packing] If that does not fit, the backup stops and names the space it needs. The card shows when the last backup was made, and so does Settings → System."},{"page":"admin/backup","title":"Backup & restore","heading":"Backup key","anchor":"backup-key","text":"Every backup is encrypted with the backup key of its server, shown in the Backup key card. The same server opens its archives by itself, another server asks for the key. Keep it outside the server, in a password manager or as the file from Save key file: without it nobody can open the backups. Until you confirm with I saved the key, camera.ui reminds you after each download.\n\nA restore brings the backup key of the backup along, so the restored server opens the archives of the old one and encrypts new ones with the same key. Unencrypted .tar.gz archives restore without a key."},{"page":"admin/backup","title":"Backup & restore","heading":"Restoring","anchor":"restoring","text":"Pick an archive and choose Restore backup archive, or use the restore button of an archive under Available backups. A dropped connection does not start the upload over: it continues where it stopped, also when you pick the same file again in the same browser tab.\n\nAn archive from another server asks for its backup key: enter it or open the key file. camera.ui then checks the archive and shows what it holds: when it was created, the camera.ui version and platform, the number of cameras and plugins, and the accounts to sign in with afterwards. A restore outside Home Assistant leaves out the homeassistant account of the Home Assistant app, which signs in only there. If no other master account remains, an account named admin from the backup becomes the master and keeps its password. Without such an account, sign in as admin with the password admin afterwards. Ports and addresses that belong to the old machine, such as port 3543 of the desktop app or an IP this server does not have, are reset to this server's defaults, and the summary lists each one. Nothing changes until you confirm with Restore. A backup that waits for confirmation also shows under Last restore and is discarded after 24 hours.\n\nEach plugin installs in the version from the backup if that version runs on this camera.ui, otherwise in the newest version that does. The summary names a different version, a plugin that cannot be installed, one that does not run on this system (its cameras keep their settings), and plugins that ran on a worker. It also names a plugin that is installed on this server but missing from the backup: it stays installed and starts without its settings.\n\nAfter the confirmation the server restarts, applies the backup and installs its plugins, with the progress per plugin in the window. At the end camera.ui logs you out; sign in with an account from the backup. A plugin that fails to install is retried at every start, and its cameras keep their settings until then. Under Last restore you can retry it right away or discard it.\n\nFolders a backup leaves out, such as recordings, stay as they are on this server. The summary lists the ones the old server had, with their size, so you can copy them over yourself. For a backup of this same server the list is left out, the folders are already here.\n\n- Undo. The setup from before the restore is kept for 7 days.[^previous] Undo restore under Last restore puts it back and restarts the server. Changes made since the restore are lost.\n- Auto"},{"page":"admin/backup","title":"Backup & restore","heading":"Restoring","anchor":"restoring","text":"matic rollback. If camera.ui fails to start three times with the restored setup, it puts the previous one back by itself. The login page and Last restore say so.\n- Interrupted restore. If the server stops while it applies the backup, it finishes the restore at the next start.\n- Rejected archives. An archive with a damaged database is rejected before anything changes, and so is a backup from a newer camera.ui than the one on this server. Update camera.ui first in that case.\n\nBefore you confirm, choose how this server takes over the backup. camera.ui suggests Move for a backup of this server and for a server without cameras, otherwise Copy.\n\n- Move. This server replaces the old one and keeps its identity: certificate authority, remote access, cloud registration, sessions and paired workers. Apps, the Apple TV and workers keep trusting it. Turn the old server off. If it still answers on this network, the summary names its address. If camera.ui could not look for it, the summary says so, on a Mac usually because camera.ui has no access under System Settings → Privacy & Security → Local Network. If the address changed, set the new one as master address on the server. Workers in the same network find the new server by themselves, others need the new address too. A backup without a certificate authority keeps this server's own, so devices that trusted the old server need the certificate again.\n- Copy. Both servers keep running and the copy gets its own identity. Sessions, API tokens and workers of the old server are left behind, cameras and plugins run on this server, remote access is off and the copy is not registered in the cloud.\n\nAfter every restore, an undo included, sign the server in to cameraui.com again under Settings → Remote Access and connect a licensed NVR again in its settings. Until then, after a move or an undo, the NVR runs on its saved license for at most 14 days from the backup, and its notification names the date. After a copy the license is inactive until you connect it."},{"page":"admin/backup","title":"Backup & restore","heading":"Moving to another server","anchor":"moving-to-another-server","text":"The new server can fetch everything from the old one directly, without downloading an archive. Both servers have to reach each other on the network.\n\n1. On the old server, choose Create move code under Move this server. The code is valid for 30 minutes and is shown once. Treat it like a password, it gives access to the whole backup.\n2. On the new server, enter the code on the welcome screen under From Another Server or in the Restore backup card, then choose Fetch backup. The code only works on another server, the old one cannot fetch a backup while it offers a code. If the code does not find the old server, for example in another network, enter its address as host:port under Address of the old server.\n3. The old server restarts, stops its cameras, plugins and workers, packs its backup and sends it. The summary then shows as for an archive, with Move suggested. Confirm as described under Restoring.\n\nUntil the new server has fetched the backup, Cancel move under Move this server stops the move and the old server runs as before. After the transfer the old server stays stopped, and its login page says it moved. Run here again in the same place starts its cameras and plugins again. It does so by itself if you cancel on the new server, discard the backup there before confirming, or if the new server does not fetch the packed backup within 30 minutes. Restoring a backup on the old server lets it run again as well."},{"page":"admin/backup","title":"Backup & restore","heading":"Scheduled backups","anchor":"scheduled-backups","text":"The Backup scheduler card creates backups automatically and keeps only the newest archives.\n\nTurn it on and set:\n\n- Frequency. Daily, Weekly, or Monthly.\n- Time. The time of day the backup runs, as HH:mm (default 03:00).\n- Weekday. Shown only when Frequency is Weekly.\n- Day of month. Shown only when Frequency is Monthly, 1-28.\n- Keep backups. How many of the newest archives to retain, 1-60 (default 7). Older archives are deleted automatically after each run.\n- Destination folder. An absolute path on the server, for example a NAS mount. Leave it empty to use the default folder inside your storage directory. A relative path is rejected.\n\nRun now starts a backup immediately. Last run shows the time and result of the most recent scheduled run. A run missed while the server was off follows two minutes after the next start. A failed run sends the system notification Scheduled backup failed to admins.\n\nScheduled archives are encrypted like a download and written to the destination folder on the server. In a folder of your own, each server writes into a subfolder named after its instance ID, so several servers can share one NAS folder. Saving the destination places a .backupignore file in it. A run that finds the folder missing, or empty without that file (a share that is not mounted), writes nothing and fails. The destination folder is never part of a backup and stays in place when one is restored. Available backups lists the archives for download, restore or removal and marks those encrypted with the key of another server."},{"page":"admin/backup","title":"Backup & restore","heading":"Command line","anchor":"command-line","text":"On the machine the server runs on, the cameraui command creates and restores backups without the browser. In Docker run it inside the container (docker exec -it cameraui cameraui backup) and write to a mounted folder.\n\ncameraui backup -o /mnt/nas/camera.ui\ncameraui restore camera.ui-backup-1760000000000.cuibackup --key-file key.txt --move\n\n- backup. Saves an encrypted archive to the current folder, or to the file or folder given with -o. It signs in with an admin API token from --token or CAMERA_UI_API_TOKEN, otherwise with an admin account.\n- restore. Uploads the archive, prints the summary and asks before it changes anything. --move or --copy sets the mode, otherwise it asks with the suggested one as default. --key-file gives the backup key of another server, -y restores without asking. A restore needs an admin account, API tokens are not accepted.\n- Sign-in. --username names the account, the password comes from CAMERA_UI_PASSWORD or a prompt, --code gives the two-factor code.\n\nThe exit code is 0 on success and 1 otherwise.\n\n[^previous]: In the restore/previous folder of the camera.ui home directory.\n\n[^packing]: In the restore/packing folder of the camera.ui home directory.\n\n[^ignore]: To exclude a folder from the backup (for example a large media folder), place an empty file named .backupignore in it. A restore leaves such a folder untouched."},{"page":"admin/instances","title":"Instances","heading":"Instances","anchor":"","text":"An instance is another camera.ui server saved here, so you can switch to it without logging in again. Manage them in Instances (in the menu). Instances are separate, full servers; to make a second machine share the load of *this* server instead, see Workers.\n\n- Add an instance with a name, its URL, and a username and password for it.\n- Two-factor accounts. An account with 2FA needs the 6-digit code from your authenticator app when you add the instance or switch to it, see Security.[^backupcodes] Until then the card shows a 2FA tag and Enter code in its menu.\n- Each instance card shows its status: version, uptime, cameras, whether it's online and recording, and resource use.\n\n[^backupcodes]: The prompt takes the 6-digit code from the app only, not a backup code.\n\nWhen you have more than one, an instance switcher appears in the top bar.\n\nSwitching to an instance over plain HTTP passes your session in the address, so camera.ui warns you first. Use HTTPS where you can."},{"page":"admin/logs","title":"Logs & console","heading":"Logs & console","anchor":"","text":"The Logs view (in the menu, admins only) shows the server log."},{"page":"admin/logs","title":"Logs & console","heading":"Reading the logs","anchor":"reading-the-logs","text":"Logs stream live. Filter by source (All, a system component such as Server, go2rtc, nats, tunnel, a camera or a plugin), search the visible lines, or download, copy all or delete the log."},{"page":"admin/logs","title":"Logs & console","heading":"Console","anchor":"console","text":"The Console (in the menu, available to every user) shows the log of the app itself, not the server: the place to look when a view stays empty, a stream won't play or a button does nothing.\n\nIt only collects entries while Record logs is on, in Settings → Appearance. The same section has Verbose logging, which adds debug-level entries.\n\nThe Levels menu filters by Debug, Log, Info, Warning and Error. Export / share saves the output as a text file.\n\nFor problems with the app, the Console export is the useful thing to attach. For problems with the server, the Logs download."},{"page":"admin/logs","title":"Logs & console","heading":"Terminal","anchor":"terminal","text":"There's also a Terminal in the menu, an interactive shell on the server for advanced troubleshooting. It's available on server installations, not in the desktop app."},{"page":"admin/mqtt","title":"MQTT","heading":"MQTT","anchor":"","text":"The Settings → MQTT page (admins only) connects camera.ui to an MQTT broker. Once connected, camera.ui publishes server events, camera states, detections, snapshots and sensor values as MQTT topics, and accepts commands for your sensors. It's off by default."},{"page":"admin/mqtt","title":"MQTT","heading":"Turning it on","anchor":"turning-it-on","text":"Switch Enabled on and fill in the connection. Every save restarts the connection, so a wrong value shows up right away in the status card at the top."},{"page":"admin/mqtt","title":"MQTT","heading":"Mode","anchor":"mode","text":"Mode decides where the broker lives.\n\n- External broker. camera.ui connects to a broker you already run (Mosquitto, the Home Assistant Mosquitto add-on, and so on).\n- Built-in broker. camera.ui runs its own broker and connects to it locally."},{"page":"admin/mqtt","title":"MQTT","heading":"Built-in broker","anchor":"built-in-broker","text":"camera.ui starts the broker on Port (default 1883) and shows the Broker address as a read-only field with a copy button, in the form mqtt://:. Devices log in with:\n\n- Username. Defaults to cameraui.\n- Password. Generated randomly on first start, replaceable with your own.[^anon]"},{"page":"admin/mqtt","title":"MQTT","heading":"External broker","anchor":"external-broker","text":"- Host. The broker's hostname or IP, for example 192.168.1.10.\n- Port. Defaults to 1883.\n- Protocol. mqtt:// (TCP) or mqtts:// (TLS). Default is mqtt://.\n- Username and Password. Leave both empty for an anonymous broker. An empty password field after saving keeps the stored password."},{"page":"admin/mqtt","title":"MQTT","heading":"Client ID and topic prefix","anchor":"client-id-and-topic-prefix","text":"Both apply to either mode.\n\n- Client ID. The name camera.ui registers with at the broker. Defaults to cameraui. Change it if something else on your network already uses that ID.\n- Topic prefix. Everything camera.ui publishes lives below this prefix. Defaults to cameraui. Letters, numbers, _, - and / separators only."},{"page":"admin/mqtt","title":"MQTT","heading":"TLS","anchor":"tls","text":"The TLS fields appear for an external broker on mqtts://.\n\n- Verify server certificate. On by default. Turn it off only for a broker with a self-signed certificate.\n- CA certificate (PEM). The certificate authority that signed the broker's certificate.\n- Client certificate (PEM) and Client key (PEM). For brokers that require client certificates."},{"page":"admin/mqtt","title":"MQTT","heading":"Testing and status","anchor":"testing-and-status","text":"Test connection opens a short-lived, separate connection with the values currently in the form, without saving. For the built-in broker, save first: the test connects to the running broker.[^test]\n\nThe status card at the top shows the live state of the connection:\n\n| State | Meaning |\n|---|---|\n| Disabled | MQTT is off, or an external broker has no host set |\n| Connecting… | The connection is being established |\n| Connected | Connected to the broker |\n| Reconnecting… | The connection dropped, camera.ui is retrying |\n| Error | The last attempt failed · the card shows the broker's message |\n\nWhen the built-in broker is running, the card says so along with its port."},{"page":"admin/mqtt","title":"MQTT","heading":"Topic reference","anchor":"topic-reference","text":"below is your topic prefix, the camera's ID, a sensor's ID. In topic segments, #, +, / and whitespace are replaced with -.\n\nState topics are retained, so a subscriber that connects later still gets the current value. Events are not retained.\n\nSensors publish under their own tree, not under a camera: a sensor is its own entity and can belong to several cameras or none. Only exposed sensors are published; turning the expose toggle off clears the sensor's retained topics."},{"page":"admin/mqtt","title":"MQTT","heading":"What camera.ui publishes","anchor":"what-cameraui-publishes","text":"| Topic | Payload | Retained |\n|---|---|---|\n| /status | online · offline | yes |\n| /server/state | JSON with the server version and a timestamp | yes |\n| /server/event | JSON, started or shutdown with a timestamp | no |\n| /server/notification | JSON, system notifications | no |\n| /plugin//status | started · stopped · error · crashed | yes |\n| /camera//status | online · offline | yes |\n| /camera//frameworker | online · offline | yes |\n| /camera//meta | JSON: id, name, room, type, disabled, camera info | yes |\n| /camera//event | JSON detection event, start to end | no |\n| /camera//motion | ON · OFF | yes |\n| /camera//detection/ | ON · OFF, one topic per detected label | yes |\n| /camera//snapshot | JPEG of the last event thumbnail | yes |\n| /sensor//meta | JSON: ID, type, name, display name, plugin, assigned cameras | yes |\n| /sensor// | the property's value as JSON | yes |\n\n/status is also the last will: if camera.ui goes away without saying goodbye, the broker publishes offline for it.\n\nThe motion topic goes ON when an event includes motion, a label topic when that label is first detected. Both go back to OFF when the event ends. The event payload carries the detection details without any image data. The image goes to the snapshot topic."},{"page":"admin/mqtt","title":"MQTT","heading":"What camera.ui subscribes to","anchor":"what-cameraui-subscribes-to","text":"| Topic | Purpose |\n|---|---|\n| /sensor///set | Set a sensor property |\n\nPublish to a /set topic to control a sensor: ON and OFF become true and false, anything else is read as JSON and falls back to plain text. Detection sensors ignore commands, their values come from the camera.\n\nAutomations with an MQTT trigger subscribe to their own topics on top of this."},{"page":"admin/mqtt","title":"MQTT","heading":"Home Assistant discovery","anchor":"home-assistant-discovery","text":"Home Assistant discovery makes Home Assistant create the devices and entities for you, no YAML. It's off by default.\n\nPick one path, not both\nMQTT discovery is one of two ways to get camera.ui entities into Home Assistant. The other is the Home Assistant integration, which needs no broker. The two do not merge: run both and every device shows up twice. Use MQTT discovery if you already run a broker, otherwise use the integration.\nit on and check the Discovery prefix. It has to match the discovery prefix configured in Home Assistant, which is homeassistant unless you changed it.\n\nHome Assistant then builds one device per camera, using the camera's name, plus its model and firmware version when camera.ui knows them. Each device gets:\n\n- Status. A connectivity sensor, listed under diagnostics.\n- Motion. A motion sensor.\n- Snapshot. A camera entity fed by the snapshot topic.\n- Person, Vehicle, Animal, Package. Detection sensors, only for cameras that have object detection.\n\nEvery exposed sensor becomes a device of its own when its type maps to a Home Assistant one: contact, occupancy, smoke and leak sensors, doorbells, temperature, humidity and battery readings, switches, lights (with brightness where the light supports it), sirens, locks, garage doors and security systems. A sensor assigned to exactly one camera is linked below that camera's device. Switches, lights, sirens, locks, garage doors and security systems are controllable from Home Assistant.[^detsensors]\n\nEntities stay available only while both camera.ui and the camera are online. Status is the exception: it follows camera.ui alone, so it can still report the camera as offline. Turning discovery back off, turning MQTT off, or changing the discovery prefix or topic prefix removes the entities camera.ui created."},{"page":"admin/mqtt","title":"MQTT","heading":"Next steps","anchor":"next-steps","text":"- Automations — react to an incoming MQTT message, or publish one as an action.\n- Sensors — the sensors that show up on the sensor topics and in Home Assistant, and the expose toggle that controls it.\n\n[^detsensors]: Detection sensors (motion, object, audio, face, license plate and the like) get no entity of their own. They feed the Motion and detection entities above.\n[^anon]: Clearing the username or the password lets the built-in broker accept any client without credentials.\n[^test]: Testing the built-in broker before it's ever been saved reports that the broker isn't running."},{"page":"admin/security","title":"Security & 2FA","heading":"Security & 2FA","anchor":"","text":"Each account has its own security settings, in Settings → Account. They apply to your own login, so every user can set them up."},{"page":"admin/security","title":"Security & 2FA","heading":"Two-factor authentication","anchor":"two-factor-authentication","text":"Two-factor authentication (2FA) adds a code from an authenticator app, such as Google Authenticator or Authy, on top of your password.\n\nSet up 2FA shows a QR code for your app (or a code to enter by hand); confirm with the 6-digit code. The backup codes shown afterwards are the way back in if you lose your phone.\n\nOnce enabled, you can regenerate backup codes or disable 2FA, both of which ask for a current code."},{"page":"admin/security","title":"Security & 2FA","heading":"Remote instances","anchor":"remote-instances","text":"If the account on a remote instance has 2FA enabled, camera.ui asks for the 6-digit code when you add that instance, change its credentials, or switch to it. Only the code from your authenticator app works here, backup codes aren't accepted.\n\nIf you cancel while adding or editing, the instance is still saved, but its card shows Two-factor confirmation pending. Pick Enter code in the card's menu to finish. Cancelling during a switch only stops the switch."},{"page":"admin/security","title":"Security & 2FA","heading":"Biometric login (mobile app)","anchor":"biometric-login-mobile-app","text":"On a phone or tablet with Face ID or Touch ID, the app offers to save your credentials in the device's secure storage the first time you sign in successfully. If you pick Not now, the app won't ask again for that server.\n\nOnce enabled, the login screen offers Sign in with Face ID. The Use Face ID / Touch ID switch under Settings → Account only turns biometric login off: it removes the saved credentials, and the app offers to save them again the next time you sign in."},{"page":"admin/security","title":"Security & 2FA","heading":"Active sessions","anchor":"active-sessions","text":"The Active sessions list shows every device signed in to your account, with the current one marked. You can sign out a single device, or sign out all the others at once. A device you sign out also loses its live view right away."},{"page":"admin/security","title":"Security & 2FA","heading":"API tokens","anchor":"api-tokens","text":"Integrations that talk to camera.ui on their own, the Home Assistant integration and the Apple TV app for example, sign in with a token instead of your password. Create one under Settings → Account → API tokens and copy it right away, it is shown only once.\n\nA token has full access to your account, so treat it like a password. The list shows when each was last used, and Revoke kills one immediately."},{"page":"admin/security","title":"Security & 2FA","heading":"Password","anchor":"password","text":"Change your password under Account. Changing your password or username signs you out everywhere at once. Your API tokens keep working."},{"page":"admin/settings","title":"Settings overview","heading":"Settings overview","anchor":"","text":"Most configuration lives under Settings in the menu. Some settings are personal to your account; others are server-wide and need an admin."},{"page":"admin/settings","title":"Settings overview","heading":"Personal settings","anchor":"personal-settings","text":"Available to everyone signed in:\n\n- Account. Your profile, password, two-factor authentication, and active sessions. See Security.\n- Appearance. Theme, language, and interface options, including whether the settings pages form their own sidebar group and which groups can collapse.\n- Notifications. How and when this device alerts you. See Notifications."},{"page":"admin/settings","title":"Settings overview","heading":"Administrator settings","anchor":"administrator-settings","text":"Available to admins:\n\n- Users. Accounts, roles, and active sessions. See Users.\n- Recordings. Recording and storage options. See Recording.\n- Remote access. Reaching the server from outside, plus the addresses apps use to reach it on your own network. See Remote access.\n- MQTT. Connect camera.ui to an MQTT broker.\n- Backup. Back up and restore your setup, and schedule recurring backups. See Backup.\n- System. Version, updates, and server actions. See System."},{"page":"admin/settings","title":"Settings overview","heading":"Admin tools","anchor":"admin-tools","text":"The menu has more admin tools:\n\n- Metrics for how the server and each camera are doing, in three tabs: Overview with the system and process tables, Cameras with what detection costs per camera, and Storage with the disk breakdown. See What detection costs.\n- Updates for the server, the plugins and the workers in one list.\n- Automations for connecting events to actions.\n- Logs and a terminal for troubleshooting.\n- Instances for switching between several servers, and Workers for sharing the load of this one.\n- A raw configuration editor for advanced changes."},{"page":"admin/system","title":"System & network","heading":"System & network","anchor":"","text":"The Settings → System page (admins only) handles the server's version and a few power actions."},{"page":"admin/system","title":"System & network","heading":"Version and updates","anchor":"version-and-updates","text":"It shows the server version, and the app version in the desktop and mobile apps. Installing updates happens on the Updates page; what stays here is Manage, which lists every published version to install a specific one, and Beta updates, the toggle at the top of the page that decides whether pre-releases are offered at all. When an installed version needs a restart to take effect, the version line shows both and offers Restart. See Updating for how updates work across platforms."},{"page":"admin/system","title":"System & network","heading":"Server actions","anchor":"server-actions","text":"- Restart server. Restarts camera.ui.\n- Reset server. Wipes the configuration back to defaults, removing all settings and data. It can't be undone and is limited to the master account."},{"page":"admin/system","title":"System & network","heading":"Certificates","anchor":"certificates","text":"Certificate downloads the server's own certificate, for adding it to a device's trust store. Own certificate takes a certificate you obtained for your domain and serves it for the names it covers. See Certificate.\n\nLooking for the server name or network addresses? Those are under Remote access. Theme and language are under Appearance."},{"page":"admin/users","title":"Users & roles","heading":"Users & roles","anchor":"","text":"Every account has a role that decides what it can do. Accounts are managed in Settings → Users (admins only)."},{"page":"admin/users","title":"Users & roles","heading":"Roles","anchor":"roles","text":"- User. Day-to-day access: the camera list, live view, recordings, Camview, and their own account settings. The Cameras page opens as a plain list to tap through to any camera; discovery, adding cameras, and camera settings stay with admins. A user account sees a camera's source addresses only as protocol, host and port, without logins, paths or tokens.\n- Admin. Everything a user can do, plus managing cameras, plugins, automations, users, and all server settings.\n- Master. The main account. The same as an admin, plus it can reset the server. It can't be deleted, and its role can't be changed, not even by itself. Only the master can edit its own username, password and avatar; other admins get an error if they try.\n\nAccess is decided by role, not per camera; there are no per-camera permissions."},{"page":"admin/users","title":"Users & roles","heading":"Managing users","anchor":"managing-users","text":"The Registered users list creates, edits and removes accounts. A new account gets the role user or admin. Accounts named ha-… come from the Home Assistant app, one for each Home Assistant user without admin rights who opened its panel. Deleting an account or taking its admin rights away applies at once, also on devices where it is signed in."},{"page":"admin/users","title":"Users & roles","heading":"Logged in users","anchor":"logged-in-users","text":"Below the registered users, the Logged in users list shows every active session across all accounts, with the device, its address, and type. Sign out on a row ends that session."},{"page":"admin/users","title":"Users & roles","heading":"Connection limit","anchor":"connection-limit","text":"Each sign-in, meaning a browser or app you logged in with or an API token, can have up to 64 live streams and about 32 camera.ui tabs or windows open at the same time. Past that, new connections wait and retry until others close. Connections of devices that stopped answering are closed to make room."},{"page":"admin/users","title":"Users & roles","heading":"The first account","anchor":"the-first-account","text":"camera.ui starts with a single master account (username admin, password admin). You're prompted to change the password on first login."},{"page":"admin/worker-assignments","title":"Worker assignments","heading":"Worker assignments","anchor":"","text":"Cameras and plugins run on the machine you assign them to under Workers. The status dot at the start of each row shows where the camera or plugin runs right now, for example while it installs on a worker, waits for one that restarts, or runs on the main server during a failover. Its tooltip names the state and the reason. Changes in both tables take effect when you click Save below the table. The camera's settings show the same under Frame Worker, a plugin's status dot on its card."},{"page":"admin/worker-assignments","title":"Worker assignments","heading":"Cameras","anchor":"cameras","text":"Under Camera Assignments, choose which worker decodes each camera; cameras left as Local stay on the main server.\n\nDecoding runs where the camera is assigned, detection runs where the detection plugin runs. When a camera and a detection plugin that analyses its frames run on different machines, every analysed frame crosses the network: per active camera about 2.3 Mbit/s for motion and 12 to 74 Mbit/s for object detection at a 320 px model input, four times that at 640 px. A warning icon next to the camera's name shows when its frames take this path. Keep a camera and its detection plugins on the same machine."},{"page":"admin/worker-assignments","title":"Worker assignments","heading":"Plugins","anchor":"plugins","text":"Under Plugin Assignments, an entire plugin can run on a worker instead of the main server. Only workers whose capabilities allow the job are listed, and a worker whose platform does not fit the plugin can't be chosen. Moving a plugin asks first: files it keeps on its machine stay there, such as downloaded models, and it starts without them on the new one. The worker installs and runs the plugin; it's still configured normally in the UI. A plugin version built for a plugin API the worker doesn't support is refused before the install, and the message says whether to update the worker or pick another plugin version.\n\nA plugin update installs the new version on the worker next to the running one and then switches with one restart. If that install fails, the worker keeps the previous version. Uninstalling the plugin removes it from the worker too, its data included when you choose to remove it.\n\nA plugin on a worker uses the worker's own network addresses, not the Server addresses of the main server. Choose them with Edit Worker in the workers list; left empty, the plugin listens on all of them. Changing them restarts the plugins on that worker.\n\nSome plugins serve the camera video on their own machine, Reolink for example. On a worker, the worker sends that video to the master's HTTPS port, nothing has to be opened for it.\n\nThe NVR plugin stores its recordings on the machine it runs on, on a worker that is the worker's disk. Recordings from a time it ran elsewhere, during a failover for example, stay on that machine; the timeline marks the time with a band that names it."},{"page":"admin/worker-assignments","title":"Worker assignments","heading":"Failover","anchor":"failover","text":"Workers check in with the master every 5 seconds. A worker the master has not heard from for 15 seconds shows as not reachable, but its cameras and plugins stay where they are. After 25 seconds without an answer they fall back to the main server. A plugin the main server's platform can't run, one for macOS only for example, waits for the worker instead, and its status says so. A worker that loses the master stops its cameras and plugins after 20 seconds, so nothing runs on two machines at once. A shorter drop changes nothing: cameras and plugins keep running and exchange the sensor states that changed in between once the link is back. Once the worker has answered for a minute, its cameras and plugins move back to it one after the other; a worker that keeps dropping out waits longer, up to 10 minutes.\n\nTurn off Fallback next to an assignment to keep a camera or plugin on its worker. It then waits for the worker as well, also after a failure there, and starts on it as soon as the worker is back. That suits a plugin that keeps files on the worker, or a main server that could not carry the load. While a camera waits, camera.ui does not analyse its video; live view and recording keep running.\n\nRestarting or updating the main server leaves the workers running: their cameras and plugins carry on without a restart once the main server is back. Older versions of Go plugins like Reolink restart once instead. A worker that restarts or updates tells the master first, and its cameras and plugins wait for it (up to 30 seconds for a restart, 2 minutes for an update) before they fall back."},{"page":"admin/worker-setup","title":"Worker setup","heading":"Worker setup","anchor":"","text":"How the snippet from Pair a worker gets onto the worker depends on its installation."},{"page":"admin/worker-setup","title":"Worker setup","heading":"Docker","anchor":"docker","text":"A worker runs from the same image as the server. Save this as docker-compose.worker.yml on the second machine, with your master's address and the pairing code:\n\nname: cameraui-worker\n\nservices:\n cameraui-worker:\n image: ghcr.io/cameraui/camera.ui:latest\n container_name: cameraui-worker\n restart: unless-stopped\n stop_grace_period: 30s\n network_mode: host\n environment:\n - TZ=Europe/Berlin\n - CAMERA_UI_WORKER=true\n - CAMERA_UI_WORKER_MASTER=192.168.1.10\n - CAMERA_UI_WORKER_PAIRING_CODE=XXXX-XXXX\n - CAMERA_UI_WORKER_NAME=worker-1\n volumes:\n - cameraui-worker-data:/data\n\nvolumes:\n cameraui-worker-data:\n\nThen start it:\n\ndocker compose -f docker-compose.worker.yml up -d\n\nThe master address takes a hostname or IP, without a scheme. CAMERA_UI_WORKER_API_PORT is only needed if the master's HTTPS port is not 3443, a desktop app as master listens on 3543. To give the worker a GPU, see Hardware acceleration below."},{"page":"admin/worker-setup","title":"Worker setup","heading":"Desktop app","anchor":"desktop-app","text":"A machine with the desktop app joins as a worker when you choose Worker in the mode picker on first start (or later via Switch mode… in the tray menu) and enter the master's address and the pairing code, or without the picker from the command line. The app then shows only a status window. If the address is wrong or the code has expired, the picker reopens with the error.\n\nThree tray options: Open At Login starts the app with the system, minimized to the tray, Close to tray keeps the worker running in the background when the window is closed,[^trayopts] and Keep Computer Awake (on by default) keeps the computer from sleeping while the worker runs cameras or plugins; the display can still turn off. If the computer sleeps anyway, the worker stops its work first and the master takes over until it is back. The address takes a hostname or IP, with :port only when the master's HTTPS port differs from the default.\n\n[^trayopts]: Open At Login is offered on Windows and macOS. Close to tray is offered on Windows and Linux; on macOS, closing the window leaves the app running anyway."},{"page":"admin/worker-setup","title":"Worker setup","heading":"Linux (bare-metal)","anchor":"linux-bare-metal","text":"Paste the snippet into camera.ui.yaml in the worker machine's storage directory, then start it with cameraui --worker run. To keep the machine a worker across reboots, install it as a service instead: cameraui --worker install (the flag is stored with the service, see Linux (bare-metal))."},{"page":"admin/worker-setup","title":"Worker setup","heading":"Hardware acceleration","anchor":"hardware-acceleration","text":"In Docker, the override files from the main deployment do not fit: they patch the service cameraui, not cameraui-worker, so layering one on top starts a second, unrelated container. Put the image flavor that matches the hardware and the device into the worker file itself:\n\nservices:\n cameraui-worker:\n image: ghcr.io/cameraui/camera.ui:intel # or :nvidia, :amd\n devices:\n - /dev/dri:/dev/dri\n\nFor NVIDIA, take the environment and deploy blocks from the NVIDIA override as well, and install the NVIDIA Container Toolkit on the worker machine. On bare-metal and in the desktop app there is nothing to pass through, the host driver is all it takes.\n\nWhich decoder a camera actually uses on a worker is set per camera, under Frame Worker in its settings. The second selection there applies while the camera runs on a worker, so master and worker can sit on different hardware. Hardware acceleration covers host drivers and how to check what the container really got."},{"page":"admin/worker-setup","title":"Worker setup","heading":"Environment variables","anchor":"environment-variables","text":"The snippet values can also be set as environment variables. CAMERA_UI_WORKER=true starts worker mode without the --worker option, and CAMERA_UI_WORKER_MASTER, CAMERA_UI_WORKER_API_PORT, CAMERA_UI_WORKER_PAIRING_CODE, CAMERA_UI_WORKER_NAME and CAMERA_UI_WORKER_CAPABILITIES (comma-separated) cover the rest of the snippet. No camera.ui.yaml is needed then, and if you set both, the environment variable wins.\n\nWithout capabilities the worker offers everything; the master still only assigns what you give it. Set them to restrict a worker to one job, for example only hosting plugins."},{"page":"admin/workers","title":"Workers","heading":"Workers","anchor":"","text":"A worker is a second machine that takes on part of a server's processing. The main server (the master) keeps the UI and the settings; workers contribute compute. Manage them in Workers (in the menu).\n\nA worker decodes the cameras assigned to it, or runs an entire plugin, for example a detection backend that needs a GPU the master doesn't have, or a plugin the master's platform can't load."},{"page":"admin/workers","title":"Workers","heading":"How workers work","anchor":"how-workers-work","text":"A worker is a regular camera.ui installation started in worker mode. It has no UI of its own and holds no cameras; it connects to the master and waits for work.\n\n- The worker connects outward. Pairing goes to the master's HTTPS port, the running connection to the master's worker port (default 7422). Only the master has to be reachable; nothing needs to be opened on the worker machine.\n- Paired once, then on its own. A one-time pairing code is exchanged for the worker's own credentials. Nothing is shared between workers, each one can be revoked independently, and after the first start the worker reconnects by itself.\n- Assignments follow the worker. Cameras and plugins you assign run on the worker, fall back to the master while it's offline, and move back when it returns (see Failover).\n\nMaster and worker can mix installation types: a Docker server with a desktop app worker, a desktop-app server with a Docker worker, and so on. The camera.ui version should match across machines; the workers list warns when it doesn't."},{"page":"admin/workers","title":"Workers","heading":"Enable workers","anchor":"enable-workers","text":"Turn on Enable Workers on the master and set a Master Address, the LAN IP or hostname other machines use to reach this server. Workers can't be enabled without an address. A Worker Connection Port (default 7422) is also required; changing it later means already-paired workers must be re-paired.\n\nA new master address needs no new pairing. Set it as the master address on each worker and restart the worker: it checks that the master behind the address is the one it was paired with and connects there. This works for the master's private IPs and the Master Address set here. In the same local network a worker finds the master by itself, for example after a move: when the master has not answered for a minute, the worker looks for it on the network and restarts to connect there.[^discovery]"},{"page":"admin/workers","title":"Workers","heading":"Pair a worker","anchor":"pair-a-worker","text":"Generate Pairing Code creates a one-time code (valid for 15 minutes) and a configuration snippet:\n\nworker:\n master: \n apiPort: \n pairingCode: \n name: my-worker\n capabilities:\n - frameDecoding\n - pluginHost\n\nname is how the worker shows up on the master. Without it, the worker uses its host name.\n\nBelow the code the master shows its fingerprint. The worker logs the same fingerprint when it pairs; a different one means it paired with another machine.\n\nOnly the first start needs the code, and it may stay in the configuration afterwards: a used code is never sent again. A worker that starts before the master is reachable keeps trying, at least once a minute. A worker without a code, or with one the master refused, keeps running and waits for a new one. It reads camera.ui.yaml every 30 seconds, a new code in an environment variable takes effect when the container restarts.\n\nHow the snippet reaches the worker depends on the installation, see Worker setup."},{"page":"admin/workers","title":"Workers","heading":"The workers list","anchor":"the-workers-list","text":"Once paired, a worker appears in the Workers list with its status, the number of its cameras and plugins that run, platform (OS/architecture), process ID, version, and live CPU and memory use. The status is Connecting until a new or restarted worker checks in, then Online, Not reachable while its answers stay away, Restarting while it restarts or updates, Offline, Outdated for a release this master cannot work with, and Disabled once workers are turned off. Hover it for the reason and the time from which its cameras and plugins run elsewhere, and the count for what each one is doing. An orange dot means the worker's version differs from the master's. When a worker is behind, a dot appears on the Updates entry in the navigation.\n\nA worker that fails to link up at startup, or loses the link later, logs the reason in its log instead of looking online. Cameras and plugins running on a worker show up in Metrics with the worker's name and its load.\n\nThe Updates page lists every lagging online worker next to the server and the plugins, and a lagging worker shows an Update button right in the list too: the worker installs the version the master is heading for and restarts itself. The update counts as done once the worker runs that version; one that failed, or that the worker rolled back, shows why. A worker can't be restarted while it updates. Normally that is the version the master runs; when a server update is waiting, it is that one, so the worker does not have to be updated twice. An update that needs a newer Node.js on the master does not count until Node.js is updated. Desktop-app workers update the same way. See Updating.\n\nA worker on a release this master cannot work with shows Outdated. It runs nothing, its cameras and plugins run elsewhere until it is updated, and its Update button and the Updates page install the new version on it. If the worker refuses, the message says why: a desktop app that cannot install updates remotely, and a worker on a release too old to take updates from the master, are updated on their own machine, see Updating."},{"page":"admin/workers","title":"Workers","heading":"Remove or replace a worker","anchor":"remove-or-replace-a-worker","text":"Removing a worker revokes its credentials and drops the connection; its cameras and plugins stop there and fall back to the main server. A worker that was online keeps running and waits for a new pairing code; the old code in its configuration does not pair it again. One that was offline logs the rejection when it returns and keeps trying until it gets a new code. A new code in camera.ui.yaml pairs it within 30 seconds, one in an environment variable once the container restarts.\n\nTwo machines that run with one worker's identity, from a copied worker folder or a restored backup, get no cameras or plugins: the master stops the work on both and logs it. Remove the worker and pair one of them again. A second machine needs its own, empty storage directory to pair as another worker: while one identity is online, the master refuses to pair it a second time. To hand the identity to new hardware while the old machine still runs, pick the worker under Replaces before you generate the code; the machine that pairs with it takes over and the old one loses its access.\n\n[^discovery]: Through mDNS. A worker in a Docker container needs host networking for it."},{"page":"assistant/","title":"Assistant","heading":"Assistant","anchor":"","text":"The Assistant answers questions about your instance in plain language: what happened at home today, whether every camera is online, which sensors are active, what a camera sees right now. The answers come from a language model you bring, and the model reaches your instance only through a fixed set of tools. Anything that changes something is shown to you first and runs after you confirm it.\n\nOnce an admin has set it up, every user sees the Assistant in the navigation, and the sparkle button in the top bar opens it on any page: as a side panel, or as a small window in the corner that leaves the page usable while you chat."},{"page":"assistant/","title":"Assistant","heading":"Asking","anchor":"asking","text":"The assistant answers in your interface language unless Answer language is set to a fixed one. Every tool it used appears as a card under the answer with its arguments and result.\n\nIt also works the app for you: \"show me the garden camera\", \"open the recording of the gate from 7:12\" or \"go to the automations\" opens the matching page, and on a camera page it moves and zooms the timeline. It does that only when you ask to see or do something, a plain question is answered in the chat. The recordings page has its own box for the assistant, see Browsing recordings.\n\nWhen a request is ambiguous the assistant asks back with a small form: pick a camera, choose a time range, or type an answer. Skip the question and it continues with an assumption it names. A recap of a day or a system check comes back as a card with one row per camera or component, and the rows open the moment they describe.\n\nNext to the text box sits a microphone: the browser turns speech into text, camera.ui never receives audio. It shows up in browsers that offer speech recognition, on a secure connection, and in the iOS and Android apps, which ask for microphone and speech recognition access the first time.\n\nThe paper clip attaches your own files, pasting a picture works too: pictures up to 12 MB, audio up to 25 MB, video up to 60 MB, eight files per message. The assistant hands them to the detection plugins. Audio and video never go to the language model, only to the plugins. Attached pictures stay with the conversation, audio and video are analyzed once and not kept.\n\nA recording card and a camera's menu both offer Ask the assistant, which opens the chat with that event or camera already named. During playback the camera's menu asks about the moment on screen instead of what the camera sees now."},{"page":"assistant/","title":"Assistant","heading":"Working with a conversation","anchor":"working-with-a-conversation","text":"Hover a message for its actions. A question can be edited and sent again, which discards everything after it. An answer can be regenerated, copied, or deleted together with its question. Branch from here copies the conversation up to that point into a new one. A stopped answer offers Continue. You can type the next message while an answer is still running; it waits below the text box and goes out when the answer is done.\n\nThe sliders button in the text box opens the settings of this conversation. Instructions apply to every answer in it, \"answer in one sentence\" for example, and keep working in long conversations where early messages drop out of what the model sees. Tools lists the tool groups (camera.ui, the documentation, one per plugin); a removed group is hidden from the model in this conversation. With more than one model the panel also picks the Model, and the conversation keeps it instead of the default. A combination of model, tool groups and instructions can be saved as a profile; profiles belong to your account.\n\nConversations are stored per user on the server, together with the pictures and the open-this buttons of the last tool calls. An answer that is still streaming survives a bad connection, a reload or a second tab: the browser picks the stream up where it broke off instead of asking the model again, and a confirmation that was waiting is shown again after a restart. Stop ends the run on the server too, closing the tab alone does not."},{"page":"assistant/","title":"Assistant","heading":"What the assistant remembers","anchor":"what-the-assistant-remembers","text":"Facts you mention or ask it to remember (\"remember that our dog is called Bruno\", \"the Gate camera watches the street\") are kept across conversations and given to the model every time. After each exchange a short model call picks such facts out on its own; events and anything temporary are never stored. A model with a context window below 16,000 tokens skips that step and keeps only what you ask it to remember. The brain button in the text box shows the list under What the assistant remembers about you, and each entry can be forgotten. Admins can turn the feature off."},{"page":"assistant/","title":"Assistant","heading":"What the assistant can do","anchor":"what-the-assistant-can-do","text":"The tools decide what the assistant can see and do. The Tools card at the bottom of Settings → Assistant lists them, one tab per provider: camera.ui itself and every plugin that contributes tools.\n\nThe camera.ui set is generic: snapshots, the sensors with their current state and switching one, push notifications to your own devices, the live load of the instance, the house layout, a health overview, and three generic API tools. Those three give the assistant the whole camera.ui REST API: it searches the endpoint that fits a question, reads it with your permissions, and changes something only after you confirm. That covers plugins, workers, sessions, automations, users and zones. Two instance tools cover what the API does not: discovering and adopting cameras and sensors, the notification history, logs, update checks, MQTT and process states. Credentials in the answers, stream passwords for example, are removed before the model sees them, and endpoints for login, configuration files, backups and downloads are never offered.\n\nThe assistant can also run a detection plugin (object, face, license plate or CLIP) on a fresh snapshot, a short sequence of snapshots or the picture of a recorded event.\n\nWith the NVR plugin it reaches your recordings: events and episodes in a time range, a summary of a day or a whole week, events searched by description, event pictures, known faces and when someone was last seen. Ask for the clip of a moment and it builds one with a download button, or sends it to your phone as a video notification. Faces the recognition could not name can be named, ignored or corrected in the chat. It also lists the license plates read in a period, tells when a camera is busiest and where in the picture the activity was, and shows the stretches a camera did not record.[^license]\n\nThe assistant also knows the user documentation: it looks up how a feature works and where it lives in the app, and points there when it cannot do something from the chat. It never asks for passwords in the chat.\n\nTools that change something (switching a sensor, running an automation, sending a notification, marking a favorite, adopting a device) are marked asks first. The chat shows what the assistant wants to do with which arguments, Edit lets you correct the values, and it runs after Allow. Tools marked admin are not offered to users with the user role."},{"page":"assistant/","title":"Assistant","heading":"Text alerts and watching a camera","anchor":"text-alerts-and-watching-a-camera","text":"With the NVR plugin the assistant can watch your cameras for a description: \"let me know when a delivery person comes\", \"alert me when a dog is in the garden\". It saves a text alert, shows you past events that would have fired so you can judge the wording, and from then on every new event whose picture looks like the description sends a push with the picture. The picture match calibrates itself on your recordings, and when the NVR plugin may use a model that model looks at the pictures of every hit and only lets a push through when it agrees, with its reason as the push text. Alerts can be limited to cameras, can get a fixed match threshold instead of the automatic one (lower fires more often) and have a cooldown between two pushes. To change one, ask the assistant. Settings → Assistant lists them under Text alerts. The menu of each one pauses or deletes it, or picks another model for the picture check than the one the NVR plugin has under Plugin access. An alert can also drive an automation: \"when the parcel alert fires, switch on the porch light\" becomes a flow with a System event trigger on the plugin's notifications.\n\n\"Keep an eye on the driveway for the next two hours\" is the same thing with an end: the alert deletes itself at that time. Every hit appears in the conversation with the picture and a button to the recording, and as a push; when the watch ends the assistant posts a short summary. Nothing is streamed to the model in between, it only looks at pictures when the cameras record something.\n\nBoth need a CLIP plugin, the same one the description search uses."},{"page":"assistant/","title":"Assistant","heading":"Automations and scheduled prompts","anchor":"automations-and-scheduled-prompts","text":"Automations can ask the assistant: the Ask the assistant action takes a question with the variables of the flow (\"Look at the picture from {{camera.name}}: is this a delivery?\"), optionally the picture of a snapshot node, and delivers the answer to one user as push, as a conversation, or only as a variable for the next nodes. A Profile of that user sets the model, tools and instructions. When nothing is worth telling, the assistant stays silent and nothing is sent.\n\nIt also builds automations from a description: \"when someone is on the terrace after 22:00, send me a push with a picture\". The assistant assembles the flow from the same node types the editor uses, shows you the nodes and connections, and creates it once you allow it. The result opens in the automation editor like any other flow. Changing or running an existing automation works the same way. This needs the admin role.\n\nA scheduled prompt is a question the assistant answers on a schedule, a summary of the day every evening for example. Ask for it in the chat or add one under Scheduled prompts with a title, the prompt, the time and the delivery: a push notification, a saved conversation, or both. An optional Profile runs it with that profile's model, tools and instructions. Model picks the model for this schedule alone, later through Change model in the schedule's menu; left empty, the profile's model or the default answers. Run now tries a schedule right away. Scheduled runs skip every tool that would ask for confirmation, and the answer comes in the language you used when you created the schedule. Up to 20 schedules per user.\n\nA push holds two short sentences that fit the lock screen. With push and conversation together, the push carries the opening sentence and the conversation the whole answer, in scheduled prompts and in Ask the assistant alike.\n\nThe answers come from a language model and can be wrong. Where it matters, check the recording."},{"page":"assistant/","title":"Assistant","heading":"Next steps","anchor":"next-steps","text":"Assistant setup — models, what plugins may use them, limits and the MCP endpoint.\n\n[^license]: The recordings tools build on recording (NVR), which requires an active camera.ui subscription."},{"page":"automations/blueprints","title":"Blueprints & store","heading":"Blueprints & store","anchor":"","text":"A blueprint is an automation saved as a file, so you can back it up, move it between servers, or share it with someone else."},{"page":"automations/blueprints","title":"Blueprints & store","heading":"Exporting","anchor":"exporting","text":"Open an automation and choose Export blueprint. camera.ui downloads a .blueprint.json file containing the flow's nodes and connections, but not its secrets, such as webhook keys."},{"page":"automations/blueprints","title":"Blueprints & store","heading":"Importing","anchor":"importing","text":"On the Automations list, choose Import blueprint and pick a .blueprint.json file. It is added as a new automation, disabled. Imported flows get fresh internal IDs, so importing the same blueprint twice won't clash."},{"page":"automations/blueprints","title":"Blueprints & store","heading":"Sharing","anchor":"sharing","text":"Export the automation and send the file; the other person imports it on their server."},{"page":"automations/blueprints","title":"Blueprints & store","heading":"Community blueprints","anchor":"community-blueprints","text":"Browse community automations on the Automations list opens the store of blueprints shared by the camera.ui community.\n\nA blueprint's page lists what it needs: required plugins (with a link to the plugin store if one is missing) and required inputs such as a camera, notification recipients or a sensor. Import then asks for your own cameras, sensors, plugins and recipients, plus plain values such as an MQTT topic, some prefilled with a default. The page doesn't list the plain values, so expect a few more fields during import. A blueprint with no references to bind imports directly. Either way it lands disabled.\n\nThe store is a public repository, cameraui/automations, that camera.ui reads at runtime. There is no upload from inside the app: to add one, open a pull request against that repository with your exported blueprint."},{"page":"automations/","title":"Building automations","heading":"Building automations","anchor":"","text":"Automations connect what happens at your cameras to actions: drag nodes onto a canvas and wire them into a flow. Automations are an admin feature."},{"page":"automations/","title":"Building automations","heading":"How a flow works","anchor":"how-a-flow-works","text":"A flow runs from left to right: a trigger starts it, optional conditions decide whether it continues, and actions do something. Utilities help pass data between steps.\n\nA new flow (+ on the Automations page) starts empty and disabled."},{"page":"automations/","title":"Building automations","heading":"Triggers","anchor":"triggers","text":"A flow can have more than one trigger.\n\n- Detection event. Something detected at a camera: motion, a person, vehicle, or animal, an audio event like a doorbell or glass breaking, a recognized face, or a license plate. Filter by object, confidence, and event phase (start, update, end).\n- Sensor change. A sensor's state changes, like a contact sensor opening. This also covers your cameras' detection sensors: motion, objects, faces, license plates, and classifiers. Face, plate, and classifier sensors carry what was recognized, so a flow can react to a specific person, car, plate, or result, for example a bird classifier that reports the species.[^detvssensor]\n- Schedule. A recurring time, set with a cron expression.\n- System event. Something in camera.ui itself, such as a camera connecting or disconnecting, a plugin starting or stopping, or a plugin sending a notification. The last one turns every plugin push into a trigger, a text alert of the NVR plugin for example: its title and data fields become variables, so an If/Else condition can pick the one you mean.\n- Webhook. An external service calls a URL to start the flow.\n- MQTT message. A message arrives on an MQTT topic. Set the topic (+ and # wildcards work) and choose how to match: any message, an exact payload, or a value at a JSON path like params.switch:0.output. Needs the MQTT connection set up in Settings.\n- Geofence. A user enters or leaves a location you define.\n- Manual. Started with the Run button, for testing.\n\nVirtual sensors are set with a Control sensor action and trigger through Sensor change."},{"page":"automations/","title":"Building automations","heading":"Conditions","anchor":"conditions","text":"- If / else. Branch on a comparison.\n- Switch. Branch on several possible values.\n- Sensor state. Check one or more sensors, combined with AND or OR. Each value can be a fixed value or a variable from an earlier step. For a detection sensor you can check a specific value, like a recognized name, a plate, or a classifier label. When a sensor holds several values at once, the check passes if any one matches, and matching ignores upper and lower case.\n- Time. Only continue within a time-of-day range and on chosen days."},{"page":"automations/","title":"Building automations","heading":"Actions","anchor":"actions","text":"- Send notification. Push an alert with a title, message, and severity.\n- Notifications on/off. Mute or unmute push. Under Applies to pick Everyone, One camera, or One user. Muting one camera leaves the others notifying. Muted alerts still land in the in-app bell and the events still show up on the timeline. Critical alerts always come through.\n- Capture snapshot. Take a fresh image from a camera.\n- Control sensor. Set an accessory, such as turning on a light or locking a door. Values can be fixed or taken from a variable.\n- Camera control. Change one or more of a camera's settings in one step: snooze detections, disable the camera, recording (on/off, mode, pre-buffer, and which streams it records), detection thresholds and timeouts, PTZ autotrack (on/off, which object types it tracks, return to home, minimum confidence), and snapshot refresh.\n- HTTP request. Call an external service.\n- MQTT publish. Send a message to an MQTT topic. Set the topic and the payload, and turn on Retain if the broker should keep the message for anyone subscribing later. Wildcards aren't allowed in the topic. Needs the MQTT connection set up in Settings.\n- Plugin call. Run a detection or analysis plugin on an image.\n- Ask the assistant. Hand the situation to the assistant and use its answer: pick the user it answers for, write the question with the flow's variables, optionally pass a picture, and deliver it as push, as a conversation, or only as a variable for the next steps.\n- Set variable and delay. Hold a value, or wait before the next step."},{"page":"automations/","title":"Building automations","heading":"Utilities","anchor":"utilities","text":"- Image input. Load an image (from a URL, a variable, or an upload) for an action to use.\n- Output. Collect results to view after the flow runs."},{"page":"automations/","title":"Building automations","heading":"Passing values between steps","anchor":"passing-values-between-steps","text":"Steps hand values on through variables. A node that produces something offers Add output variable in its settings: name it, and every step wired after it can use the value. Variables on a step lists what is available; if it is empty, the source node isn't connected yet.\n\nWrite a variable as {{name}} in any text field. Fields that only take one value, like a sensor value or a condition, let you pick the variable from a list instead of typing it. What each trigger and action provides, and which values it can take, is listed under Variables.\n\nSet variable stores a value for steps further down. Alias on a node prefixes its output variables, so result from two different plugins becomes cam1.result and cam2.result instead of clashing.\n\nA misspelled name is caught on save, with Unknown variable naming the one it could not resolve."},{"page":"automations/","title":"Building automations","heading":"Options and running","anchor":"options-and-running","text":"The toolbar holds the name, Enabled, and two behaviors:\n\n- Ignore repeated events, so a burst of triggers runs the flow once.\n- Wait for completion, so a new trigger waits until the current run finishes.\n\ncamera.ui disables an automation automatically if it points at a camera or sensor you've removed, with a notice to fix it. The list shows each automation's last run and lets you enable, disable or delete several at once.\n\nTo share automations, see Blueprints & store.\n\n[^detvssensor]: A Detection event fires on each raw event as it happens. A Sensor change reacts to the sensor's own state, which keeps the recognized names, plates, or labels for as long as the subject stays in view."},{"page":"automations/variables","title":"Automation variables","heading":"Automation variables","anchor":"","text":"Every trigger fills a set of variables that later steps use as {{event.label}}, in a notification, the body of an HTTP Request, or a condition. Every value is text: numbers come as digits, lists as comma-separated text, objects as JSON. How to wire steps and name your own variables is on Building automations.\n\nIf / Else and Switch suggest the values a variable can take for the trigger or sensor you picked."},{"page":"automations/variables","title":"Automation variables","heading":"Detection event","anchor":"detection-event","text":"| Variable | Value |\n|---|---|\n| event.id | ID of the event |\n| event.cameraId | ID of the camera |\n| event.state | active, or ended on Event End |\n| event.types | Everything the event contained so far, comma-separated[^types] |\n| event.type | First entry of event.types, usually what started the event: motion or audio |\n| event.label | The detected object (person, vehicle, animal), otherwise the audio event, otherwise event.type |\n| event.confidence | Best score from 0 to 1, 0 when nothing reported one (plain motion) |\n| event.faces | Recognized names, comma-separated |\n| event.plates | Recognized license plates, comma-separated |\n\nFaces, plates and the detected object travel with the segments, the stretches in which an object is tracked. Event Start, Event Update and Event End don't carry them: event.faces and event.plates stay empty and event.label falls back to the audio event or event.type. To send who or what was seen, set Trigger on to Segment Start, Segment Update or Segment End.\n\nAudio events are doorbell, glass_break, siren, speaking, gunshot, dog_bark, baby_cry, alarm, scream, cat, car_alarm and smoke_alarm."},{"page":"automations/variables","title":"Automation variables","heading":"Sensor change","anchor":"sensor-change","text":"| Variable | Value |\n|---|---|\n| sensor.property | The property that changed, one of the sensor's Properties |\n| sensor.value | The new value: true or false for on/off and detected states, a number for levels and readings, text for names and labels |\n| sensor.previousValue | The value before the change |\n| sensor.sensorType | The kind of sensor, such as contact, motion, object, face, licensePlate or temperature |\n| sensor.sensorId | ID of the sensor |\n| sensor.assignedCameraIds | IDs of the cameras the sensor belongs to, comma-separated |\n\nA value with several parts also appears field by field: a PTZ position as sensor.value.pan, a list of recognized names as sensor.value.0, sensor.value.1 and sensor.value.length."},{"page":"automations/variables","title":"Automation variables","heading":"System event","anchor":"system-event","text":"system.eventType holds the event picked in the trigger: system:started, system:shutdown, camera:added, camera:removed, camera:connected, camera:disconnected, camera:frameworker:started, camera:frameworker:stopped, camera:property:changed, sensor:added, sensor:deleted, plugin:started, plugin:stopped, plugin:error or plugin:notification.\n\n| Variable | Set by | Value |\n|---|---|---|\n| system.cameraId, system.cameraName | Camera events | The camera |\n| system.property | Property Changed | The settings group that changed: name, sources, detectionSettings, ptzAutotrack, recordingSettings, notificationSettings, zones, frameWorkerSettings or interfaceSettings |\n| system.pluginId, system.pluginName | Plugin events | The plugin |\n| system.status | Plugin Started, Plugin Stopped, Plugin Error | started, stopped or error |\n| system.sensorId, system.sensorType, system.sensorName | Sensor Added, Sensor Deleted | The sensor |\n\nPlugin sends a notification adds system.title, system.subtitle, system.body, system.tag, system.severity (info, warn, error or critical) and every data field the plugin attaches. The NVR plugin sets system.type to detection, episode, text-alert, text-alert-end, camera_online, camera_offline, storage_paused, storage_resumed, storage_path_rejected or cloud_reauth, and depending on the type system.cameraId, system.cameraName, system.eventId, system.episodeId, system.alertId and system.alertTitle."},{"page":"automations/variables","title":"Automation variables","heading":"Webhook","anchor":"webhook","text":"A webhook is called with POST and a JSON object as body.\n\n| Variable | Value |\n|---|---|\n| webhook.body | The whole body as JSON text |\n| webhook. | Each top-level text, number or true/false field: {\"room\": \"hall\"} gives webhook.room with hall |\n| webhook.headers | The request headers as JSON |\n| webhook.method | POST |"},{"page":"automations/variables","title":"Automation variables","heading":"MQTT message","anchor":"mqtt-message","text":"| Variable | Value |\n|---|---|\n| mqtt.topic | The topic the message arrived on, useful with wildcards |\n| mqtt.payload | The payload as text |\n| mqtt. | Each top-level text, number or true/false field of a JSON object payload |"},{"page":"automations/variables","title":"Automation variables","heading":"Geofence","anchor":"geofence","text":"| Variable | Value |\n|---|---|\n| geo.event | enter or leave |\n| geo.user | The user who moved |\n| geo.zone | Name of the zone |\n| geo.lat, geo.lon | The reported position |\n| geo.distance | Distance to the zone's center in meters, rounded |\n\nSchedule and Manual start a flow without variables."},{"page":"automations/variables","title":"Automation variables","heading":"Results of actions","anchor":"results-of-actions","text":"| Variable | Step | Value |\n|---|---|---|\n| snapshot | Capture Snapshot | The JPEG as base64 |\n| snapshot.base64 | Capture Snapshot | The JPEG as a data URL, data:image/jpeg;base64,… |\n| http.status | HTTP Request | Status code, such as 200 |\n| http.body | HTTP Request | The response as text, base64 for images and other binary data |\n| http.base64 | HTTP Request | A binary response as a data URL |\n| http.json, http. | HTTP Request | A JSON response as text, and each of its top-level fields |\n| plugin.detected | Plugin Call | true or false |\n| plugin.labels | Plugin Call | Found labels, comma-separated |\n| plugin.detections, plugin.result | Plugin Call | The detections and the full result as JSON |\n| plugin.durationMs | Plugin Call | Run time in milliseconds |\n| assistant.answer | Ask the assistant | The answer as text |\n| assistant.replied | Ask the assistant | false when the assistant found nothing worth telling |\n| camera.id, camera.name | Camera Control | The camera it changed |\n| previous.success | Most actions | true or false for the last step that ran |\n| previous.result | Most actions | snapshot_captured, snapshot_failed, detected, not_detected, no_response, assistant_replied or assistant_silent |"},{"page":"automations/variables","title":"Automation variables","heading":"Time and arithmetic","anchor":"time-and-arithmetic","text":"{{time.now}} is the current time in milliseconds since 1970, in any flow.\n\n{{= … }} calculates with numbers: {{= event.confidence * 100 }} gives the confidence in percent. It knows +, -, *, /, % and parentheses, rounds results with decimals to two places, and counts a variable that isn't a number as 0.\n\n[^types]: motion and audio, the objects person, vehicle and animal, face and license_plate, a classifier result such as bird, and the event types a camera reports itself: contact, doorbell, switch, light, siren, security_system, line-crossing, occupancy, smoke, leak, gas, carbonMonoxide, heat, cold, vibration, tamper, problem, power, lock and garage."},{"page":"cameras/add-camera","title":"Adding a camera","heading":"Adding a camera","anchor":"","text":"There are two ways to add a camera: let camera.ui discover it, or add it manually with its stream URL. Both start from the Cameras page."},{"page":"cameras/add-camera","title":"Adding a camera","heading":"Before you start","anchor":"before-you-start","text":"- The camera is on the same network as your server.\n- For network discovery, enable ONVIF in the camera's own settings.\n- Have the camera's username and password ready. For a manual add, also have its RTSP URL (from the camera's manual or app)."},{"page":"cameras/add-camera","title":"Adding a camera","heading":"Discover a camera","anchor":"discover-a-camera","text":"The Cameras page lists devices that have been discovered, ready for you to add. A camera shows up here when camera.ui's built-in discovery finds it on your network (ONVIF and other protocols), or when a plugin that manages cameras reports one. Cameras you have already added appear under Adopted cameras. Discovery and adding are for admins; other users open the same Cameras page as a plain list and tap through to any camera.\n\n1. Open Cameras. Discovery runs automatically. If a camera doesn't appear, select Rescan.\n2. Select a camera under Discovered. If asked, enter username and password (a few cameras use a PIN) and select Connect.\n3. Confirm camera shows the Name, Room and Sources camera.ui read from the camera. Adjust them if needed.\n4. Select Add camera. The camera moves to Adopted cameras.\n\nA plugin also tells camera.ui whether a camera runs on battery and whether it is a doorbell. A battery camera is added so it can sleep: hot mode off, new pictures Only on request, live view in Standby and Ad-hoc recording. A doorbell is added as a doorbell.\n\nThe eye icon hides a device; Show hidden devices brings it back."},{"page":"cameras/add-camera","title":"Adding a camera","heading":"Add a camera manually","anchor":"add-a-camera-manually","text":"For cameras that aren't discovered, or to set up streams yourself, select Add camera on the Cameras page.\n\n1. Give the camera a Name, and optionally assign it to a Room.\n2. Under Sources, set up at least one stream:\n - Enter a Source name.\n - Pick a Source role: High Resolution, Mid Resolution, or Low Resolution for streaming, or Snapshot for stills. At least one streaming role is required.\n - Paste the full stream URL including the protocol, for example rtsp://user:pass@192.168.1.50:554/stream. camera.ui detects the protocol from the URL and shows Detected protocol under the field. Use the (i) button next to the field for protocol-specific help, or Test camera source to preview a frame.\n3. Select Add source to add another URL to the same source. To add a second source with another role, for example a low-resolution stream for fast scrubbing, use the plus button below the Sources card.\n\nThe NVR plugin, if installed and enabled, is activated for every new camera automatically, whether you added it manually or adopted it from Discovered. Recording then starts on its own as long as your subscription has a free camera slot. You can change the mode or turn recording off per camera, see Recording."},{"page":"cameras/add-camera","title":"Adding a camera","heading":"Sources and roles","anchor":"sources-and-roles","text":"A camera can have several sources, each with a role:\n\n- High / Mid / Low Resolution are streaming tiers. camera.ui uses the one that fits the situation, such as a low-resolution stream in a multi-camera grid and a high-resolution one at full screen.\n- Snapshot provides still images.\n\nOne source is enough; a lower-resolution stream makes grids and playback lighter. Each source needs its own name within the camera; a name already in use is rejected with a note under the field. Once a source is saved its name is fixed."},{"page":"cameras/add-camera","title":"Adding a camera","heading":"Source options","anchor":"source-options","text":"- Hot mode. Keeps the connection to the camera always active, so there's no reconnect delay when a stream is opened.\n- Preload. Preloads the stream so the camera view opens with minimal delay.\n- Mute audio. Removes the audio track from this source. Off by default.\n- Disable two-way audio. Removes the talk channel from this source, camera.ui no longer requests it from the camera. Off by default.\n- Stream timeout. Seconds without video before the stream is reconnected. Left empty it is 5 seconds for a camera you entered yourself and 60 for a source a plugin provides, since the plugin already watches its camera.\n- Connection timeout. Seconds the camera gets for each step of connecting. Raise it for cameras that wake up slowly, a battery model for example. Empty means 5.\n\nWith hot mode and preload on, opening a stream is near-instant, whether the camera uses H.264 or H.265."},{"page":"cameras/add-camera","title":"Adding a camera","heading":"Connection protocols","anchor":"connection-protocols","text":"RTSP works with almost any IP camera and is the usual choice, while ONVIF powers discovery. The protocol list also covers specific brands and systems, some of which connect directly and others through a plugin. See Connection protocols for the full list.[^cloud]\n\n[^cloud]: Cameras that depend on a vendor cloud (such as Ring or Nest) can be connected, either directly or through a plugin, but they are not recommended and not covered by support. Use local RTSP / ONVIF cameras for a reliable experience."},{"page":"cameras/camview","title":"Camview","heading":"Camview","anchor":"","text":"Camview shows several cameras at once in a grid."},{"page":"cameras/camview","title":"Camview","heading":"Create a layout","anchor":"create-a-layout","text":"1. Open Camview and select New View.\n2. Give the view a name and pick a grid size, from a single camera up to 26.\n3. Select Next and fill the slots: drag cameras in from the list on the right, or on a narrow screen tap the plus in a slot."},{"page":"cameras/camview","title":"Camview","heading":"Arrange the grid","anchor":"arrange-the-grid","text":"Turn on Rearrange to drag tiles around. On a computer you can also resize a tile by its bottom-right corner, with two helpers:\n\n- Auto-arrange packs the tiles together with no gaps.\n- Fit aspect ratios sizes each tile to its camera's configured aspect ratio.\n\nOn phones the cameras sit in one column, so Rearrange only changes their order, and resizing and the rearrange helpers aren't available there. A view itself is saved to your account. The arrangement is kept in the browser you made it in, and phones and computers keep their own."},{"page":"cameras/camview","title":"Camview","heading":"Fit the picture to the card","anchor":"fit-the-picture-to-the-card","text":"In Rearrange, the Picture in the card button offers four choices:\n\n- Keep aspect ratio (default) and Fit inside show the whole picture, with black bars where the shapes differ.\n- Crop to fit fills the card and cuts off the edges.\n- Stretch to fit fills the card and distorts the picture.\n\nThe choice is saved with the view, so it follows your account to every device, and it applies to live and to playback. Expanding a tile, fullscreen and zoom always show the whole picture again. The button is on computers only, but a view keeps the fit you picked there when you open it on a phone."},{"page":"cameras/camview","title":"Camview","heading":"Control all cameras at once","anchor":"control-all-cameras-at-once","text":"A floating control in the corner acts on every camera in the view together:\n\n- Fullscreen the whole grid.\n- Mute or unmute all.\n- Two-way talk to all cameras at once.\n- Switch the streaming mode for all.\n- Set one activity mode for all (Always On, Standby, Activity), or Per Camera to let each camera keep its own activity mode. Per Camera is the default.\n- Change the resolution for all.\n- Turn the detection markers on or off."},{"page":"cameras/camview","title":"Camview","heading":"What a camera is seeing","anchor":"what-a-camera-is-seeing","text":"While a camera detects something its tile glows, and small icons name what was found: motion, person, vehicle, face, plate and the rest. Detections in the floating control switches both off for the whole view. The choice is saved with your account."},{"page":"cameras/camview","title":"Camview","heading":"Focus one camera","anchor":"focus-one-camera","text":"Click a tile to expand it to the full view, click it again to go back to the grid. A sleeping tile (moon icon) only wakes on that click, the next one expands or collapses it. A view with a single tile stays as it is.\n\nThe fullscreen button in a tile's control bar shows that camera alone. There the dots in the control bar also offer Shortcuts and a Timeline for this camera. If you play back recordings there, the other cameras pick up the same point in time when you leave fullscreen."},{"page":"cameras/camview","title":"Camview","heading":"Playback across the grid","anchor":"playback-across-the-grid","text":"Open the timeline to scrub through recordings for the whole view at the same time, with playback kept in sync across every camera. See Timeline & playback."},{"page":"cameras/camview","title":"Camview","heading":"Find objects across the grid","anchor":"find-objects-across-the-grid","text":"Switch the grid's timeline to Find or press F. Every tile can be picked then, each one freezes on its own while the others keep playing, and the matches of all picks form one list. The tile of a camera without object detection says No object detection. A scrub moves every frozen tile to the new moment. A tile in fullscreen has Find in its own timeline, a match of another camera opens that camera. See Find an object."},{"page":"cameras/camview","title":"Camview","heading":"Performance","anchor":"performance","text":"Camview streams each camera at its low resolution so a full grid stays smooth. A single view holds up to 26 cameras. Give your cameras a low-resolution source."},{"page":"cameras/floor-plan","title":"Floor plan","heading":"Floor plan","anchor":"","text":"The floor plan is a drawing of your home with the cameras and sensors on it, under Floor Plan in the menu. Everyone can look at it, drawing needs admin.\n\nIt is live: a camera lights up while it sees something, sensors show their state, and admins can operate them right on the plan. The drawing is also read by camera.ui itself. It knows which rooms connect, how long it takes to walk from one camera to the next, and which sensor sits at which door."},{"page":"cameras/floor-plan","title":"Floor plan","heading":"Drawing","anchor":"drawing","text":"Start with a level, one per floor. They live behind the button in the top left.\n\nThen drag pieces from the palette onto the canvas. On a phone the plus button opens the same palette.\n\n- Rooms. Drag New room in, then move and resize the rectangle. A room around a corner, an L-shaped yard for example, is drawn as several rectangles: draw the second one and set its Belongs to to the first, and the two count as one room.\n- Kind. A room is Inside, Outside, on the property, or Outside world. The last one is the street, the pavement, a neighbour's land: someone arriving from there was not seen anywhere before, so camera.ui does not look for an earlier sighting. Draw the gate or the front path as the place where they get in.\n- Connections. Rooms that only share a wall are not connected. Put a Door, an Open passage or Stairs on the shared wall, slide it along the wall and drag its dots to set the width. Between two levels the connection is always stairs.\n- Cameras. Drag a camera into the room it watches, then set Direction, Field of view and Range on the right. The cone on the plan follows, and the room you drop it in becomes its room in the camera settings. A camera without a room adds nothing to the spatial context.\n- Sensors. Same idea. A contact or a lock that reports on one specific door gets Belongs to passage, so its state is read as that door opening rather than as something happening in the room.\n- North. Drag the compass until it matches reality.\n- Note. Rooms, cameras, sensors and connections each take a free-text note for what the drawing cannot show. The AI reads it when it describes an event.\n\nCentimetre accuracy buys nothing. Rooms roughly the right size in roughly the right place, with the cameras pointing the right way, is enough.\n\nChanges are kept only when you select Save. The dots button holds undo, redo, Clear this level and Discard changes. The lock button freezes the plan, so panning around cannot move anything by accident."},{"page":"cameras/floor-plan","title":"Floor plan","heading":"Reading it","anchor":"reading-it","text":"- A camera's cone lights up while the camera sees something, with icons for what was detected.\n- Point at a camera for its live picture and Open camera.\n- Point at a sensor for its state. Admins also get an accessory's control there: a light toggles, an alarm panel offers its modes.\n- Blind spots hatches every part of a room that no camera covers."},{"page":"cameras/floor-plan","title":"Floor plan","heading":"What the plan is used for","anchor":"what-the-plan-is-used-for","text":"- Episodes use it to tell activity apart. Two things happening at the same time in different corners of the property stay separate stories, a quick walk from the hall to the drive stays one, and the title names the places. Two cameras on the same room count as two angles of one scene: the one that had the subject closest plays, and the player offers the other.\n- AI descriptions get the layout, the walking times and what nearby sensors read at the time as context.\n- Recordings gains a Rooms filter, which selects the cameras placed in those rooms."},{"page":"cameras/floor-plan","title":"Floor plan","heading":"Next steps","anchor":"next-steps","text":"Camera settings — the room a camera belongs to is set here too, and groups the home view.\nSensors — what a sensor is and how it gets to a camera."},{"page":"cameras/home","title":"Home","heading":"Home","anchor":"","text":"Home shows every camera you have access to as a card with a still image.\n\nAbove the cards, Recent Events is a row of recent moments you can scroll sideways. A visit with several phases shows one card per moment, like the timeline, and selecting a card opens it at that moment. Cards with a recognized face or a read license plate get arrows to browse the pictures, and resting your finger on a card (or hovering on a computer) plays a short preview. Episodes appear here too, as a mosaic card showing how many cameras took part. See Events & detections.\n\nThe filter button in the corner decides which cameras show which kinds of detection there: person, vehicle, animal, face and license plate, each with its own list of cameras. An event only disappears when everything detected in it is hidden, so someone walking by while a car passes still shows. Hidden events are still detected, recorded and found in Recordings. The choice belongs to your account.\n\nWith no cameras yet, Get started takes admins to add one."},{"page":"cameras/home","title":"Home","heading":"Camera cards","anchor":"camera-cards","text":"- The picture. A snapshot, not a live stream.\n- Status icons. A red camera icon next to the name means the camera is disabled. An amber moon means detections are snoozed for it.\n- Disabled cameras. The image is replaced by a dark tile with a crossed-out camera icon.\n\nSelecting a card opens the camera page with the live view, recordings, and the timeline.\n\nThree buttons sit in the bottom right of a card:\n\n- Refresh snapshot. Pulls a new picture from the camera right away.\n- Log. Opens that camera's log in a dialog, for admins. You can download it from there.\n- Settings. Opens the camera settings drawer, for admins.\n\nA disabled camera only offers Settings."},{"page":"cameras/home","title":"Home","heading":"Arranging the cards","anchor":"arranging-the-cards","text":"A floating control sits in the corner:\n\n- Group by room (with more than one camera) switches from one flat grid to sections per room, using the Room you set in the camera settings. Cameras without a room land in Default, which comes first, and the remaining rooms are sorted by name. Show all cameras switches back.\n- Edit layout / Lock layout unlocks the cards. Unlocked, you can drag them to a new place, and the eye button on a card hides it from Home or shows it again. Hidden cameras stay on the page, dimmed, until you lock the layout. The order is kept; in the grouped view you reorder within a room.\n\nHiding only changes Home. The camera keeps streaming, detecting and recording, its events still show in Recent Events, and selection mode leaves it out. Hidden cameras belong to your account.\n\nCards you never moved are sorted by name."},{"page":"cameras/home","title":"Home","heading":"Snapshots and how they refresh","anchor":"snapshots-and-how-they-refresh","text":"The card picture is a stored still image, reused across the app. When a camera takes a new one is set under New picture in the Snapshot section of the camera settings:\n\n- Regularly. A timer takes a picture every 10 seconds to 1 hour (60 seconds by default). Looking at the camera never wakes it.\n- When viewed. A new picture only when someone looks at the camera and the stored one is older than the age you set.\n- Only on request. The camera is left alone until an automation, Refresh snapshot or the API asks. Made for battery cameras.\n\nA picture taken by an automation or the refresh button shows up on the cards right away, in every mode. Disabled cameras are never asked for a new one.\n\nIf a camera is offline or slow to answer, the card keeps the last picture it had rather than going blank."},{"page":"cameras/home","title":"Home","heading":"The snapshot age badge","anchor":"the-snapshot-age-badge","text":"The badge in the top right corner of a card shows how old its picture is, counting up every second.\n\nIt reads now for the first second, then seconds (14s), minutes (3min), hours (2h), days (5d), months (2m), and years (1y).\n\nThe badge resets with every new picture. On Regularly it stays around the interval, and a badge climbing well past it means the camera is not delivering, usually because it is unreachable. On When viewed and Only on request a high value is normal until the next picture is taken.[^badge]"},{"page":"cameras/home","title":"Home","heading":"Selection mode","anchor":"selection-mode","text":"Admins can act on several cameras at once. Select the arrow button in the floating control to enter selection mode.\n\nIn selection mode a card ticks instead of opening the camera, and dragging and the per-card buttons are off.\n\nThe floating control now holds the bulk actions:\n\n- Disable selected cameras / Enable selected cameras. Disabling stops a camera's live stream and its detection, and its card goes dark. The camera stays configured.\n- Snooze detections for selected cameras / Resume detections for selected cameras. Snoozing keeps the camera streaming but pauses its detection.\n- Enable recording for selected cameras / Disable recording for selected cameras. Only appears when at least one camera on the page is set up for recording, and it only acts on those cameras. See Recording.\n- Remove selected cameras. Deletes the cameras.\n\nBulk actions run through the cameras one after another, so with many cameras picked it takes a moment."},{"page":"cameras/home","title":"Home","heading":"Next steps","anchor":"next-steps","text":"- Live view — watch a camera, with the player controls.\n- Camview — several cameras at once in a grid.\n- Camera settings — sources, room, detection, and everything per camera.\n\n[^badge]: The badge is only shown on the cards on Home, not in the player or in Camview."},{"page":"cameras/live-view","title":"Live view","heading":"Live view","anchor":"","text":"Open any camera to watch it live."},{"page":"cameras/live-view","title":"Live view","heading":"Player controls","anchor":"player-controls","text":"- Play / pause the live stream.\n- Mute / unmute for cameras with audio.\n- Two-way talk to speak through the camera (see below).\n- Fullscreen and Picture-in-Picture to pop the video into a floating window. In fullscreen the dots in the control bar switch Shortcuts, the Timeline over the video and the PiP Source overlay. On a phone held upright, the timeline sits at the bottom of the screen, below the picture.\n- Digital zoom by scrolling or pinching, up to 5×, with a minimap. Drag to pan when zoomed in. The crop holds while you scrub the timeline, until you double-click the picture or zoom back out.\n- Quality switches the source resolution (High, Mid, or Low Resolution).\n- Open camera opens the full camera page with recordings and the timeline.\n\nWhen you review recordings, the player also offers rewind, fast-forward, and playback speed. See Timeline & playback."},{"page":"cameras/live-view","title":"Live view","heading":"Detection boxes","anchor":"detection-boxes","text":"With Detections on, the player draws a box around everything the camera currently sees, with the type, the confidence and the track number. A camera that reports a detection without a position gets no box.\n\nAn object that has settled, a car parked in the driveway or a bin left at the kerb, keeps its box but gets a faded label and a timer counting how long it has been standing there. A settled object triggers no new events and no notifications, see stationary objects. It goes back to a normal box as soon as it moves again. On a small card the labels are left out and only the box remains."},{"page":"cameras/live-view","title":"Live view","heading":"Two-way talk","anchor":"two-way-talk","text":"The Intercom button sends your microphone to the camera's speaker, on cameras that support two-way audio."},{"page":"cameras/live-view","title":"Live view","heading":"Second stream overlay","anchor":"second-stream-overlay","text":"Give a source a PiP Source in the camera settings (Sources tab) and the player gets a Toggle PiP button. It shows that second stream as a small overlay on the video. Click the overlay to swap the two streams.\n\nThe PiP Source can also be a stream of another camera, the list groups them by camera. That fits a second lens of the same device, such as the tele lens of a dual-lens camera added as a camera of its own. Clicking that overlay opens the other camera with its timeline and recordings, at the same moment while you review footage, and the camera you came from becomes its overlay."},{"page":"cameras/live-view","title":"Live view","heading":"Streaming modes","anchor":"streaming-modes","text":"You set the streaming mode per camera in its settings, where a new camera starts on Auto. The player also has a button to switch between WebRTC and MSE while you watch.\n\n- Auto starts WebRTC and MSE together and keeps whichever connects first, so a network that blocks WebRTC still gets a picture. When the camera's codec cannot travel over WebRTC it goes straight to MSE.\n- WebRTC gives the lowest latency.\n- MSE is a broadly compatible fallback.\n- webrtc/tcp sends WebRTC over TCP only. Pick it in the camera settings if some networks block the ports WebRTC normally uses. It is not available from the player button.\n\nThe setting applies to the camera view. The small players elsewhere, the zone editor, shortcut previews, the floor plan hover and the event dialog, always run on Auto.\n\nAn MJPEG camera plays as MJPEG in every mode, in the browser, the mobile apps and the Apple TV app. The picture arrives as single JPEG frames, the sound, if the camera has any, over MSE. Recording takes MJPEG as it is."},{"page":"cameras/live-view","title":"Live view","heading":"Activity mode: Always On and Standby","anchor":"activity-mode-always-on-and-standby","text":"Each camera has an Activity mode that controls when its stream runs:\n\n- Always On keeps the stream live while the player is in view.\n- Standby keeps the stream off until you click the video. It stops again a few seconds after your last click or after the picture appears, whichever is later. It stays on while you use the intercom, the PTZ controls, fullscreen, Picture-in-Picture or a menu of the player.\n- Activity keeps the stream running while there is activity and lets it rest otherwise.\n\nThe setting applies to the camera view and to the Home Assistant camera card in live mode. Camview and the Home Assistant view card follow it too, unless they are set to one mode for the whole view. The small players elsewhere always stay on: the zone editor, shortcut previews, the floor plan hover and the event dialog.\n\nWhatever the mode, a player scrolled out of view stops after about 10 seconds and shows its last picture until you scroll back, then starts again. The intercom, the PTZ controls, fullscreen, Picture-in-Picture and an open player menu keep it running, as in Standby."},{"page":"cameras/live-view","title":"Live view","heading":"Shortcuts","anchor":"shortcuts","text":"Shortcuts are controls you place on the video to jump to another camera or operate a sensor. Turn on Edit Shortcuts on the player to add them. See Shortcuts."},{"page":"cameras/live-view","title":"Live view","heading":"More options","anchor":"more-options","text":"The options menu (the dots) toggles Zones, Heatmap and Edit Shortcuts, plus Detections on cameras that run motion or object detection. AI descriptions appears when the NVR plugin is installed. Export appears once the timeline is open, and saves the range you trim there. See Detection & AI.\n\nCameras with pan, tilt and zoom get their own bar at the top right of the player, with the home position, the presets saved on the camera, and the PTZ button for the on-screen controls. See PTZ & autotrack."},{"page":"cameras/live-view","title":"Live view","heading":"The camera page","anchor":"the-camera-page","text":"Open camera (or selecting a camera from the list) opens its full page: the live player, a Recordings tab, a list to switch between Cameras, Shares for admins, and a Timeline panel for jumping through footage. Drag the handle between the player and the tabs to give either more height; the browser keeps that split, a double-click on the handle resets it. The Settings cog (admins) opens the camera settings drawer, where a camera's sources, zones, PTZ, detection, and more live.\n\nTo watch several cameras at once, use Camview."},{"page":"cameras/protocols","title":"Connection protocols","heading":"Connection protocols","anchor":"","text":"When you add a camera manually, each stream URL begins with a protocol. camera.ui reads it from the URL and shows it under the field. The (i) button next to the field opens help for that protocol, with URL formats and examples.\n\nA URL with an unsupported protocol won't save. The (i) help only appears once camera.ui recognizes the protocol, which makes it a quick check for a URL."},{"page":"cameras/protocols","title":"Connection protocols","heading":"RTSP (recommended)","anchor":"rtsp-recommended","text":"RTSP is the universal choice and what most cameras provide. A URL looks like this:\n\nrtsp://username:password@192.168.1.123:554/path\n\nThe path after the address is camera-specific; the camera's manual or app has it. Many cameras offer a main (high-resolution) and a sub (low-resolution) stream; you can add each as a separate source."},{"page":"cameras/protocols","title":"Connection protocols","heading":"Other common protocols","anchor":"other-common-protocols","text":"| Protocol | Use it for |\n|---|---|\n| ONVIF | A standard most cameras support. Connects without knowing the RTSP path, and powers discovery. |\n| HTTP / HTTPS | Snapshot (JPEG), MJPEG, HTTP-FLV, or MPEG-TS streams. |\n| RTMP | RTMP streams. |\n| FFmpeg | Wrap any source FFmpeg can read. |"},{"page":"cameras/protocols","title":"Connection protocols","heading":"NVR and DVR systems","anchor":"nvr-and-dvr-systems","text":"| Protocol | Use it for |\n|---|---|\n| ISAPI | Hikvision two-way audio (add it alongside an RTSP source). |\n| DVRIP | Dahua and compatible recorders (also known as NetSurveillance / Sofia / XMeye). |"},{"page":"cameras/protocols","title":"Connection protocols","heading":"Brand and smart-home integrations","anchor":"brand-and-smart-home-integrations","text":"camera.ui also recognizes brand- and app-specific URLs, such as Tapo, Kasa, Doorbird, GoPro, Home Assistant, HomeKit, Ring, Nest, Wyze, and Tuya. Many of these depend on a vendor cloud.[^cloud] Paste the URL and use the (i) help for the exact setup of each."},{"page":"cameras/protocols","title":"Connection protocols","heading":"Tips","anchor":"tips","text":"- You can give one source multiple URLs. camera.ui combines them, which helps when streams carry different codecs (for example video on one and backchannel audio on another).\n- If you don't know your camera's URLs, try ONVIF or auto-discovery first.\n\n[^cloud]: Cameras that depend on a vendor cloud (such as Ring or Nest) can be connected, either directly or through a plugin, but they are not recommended and not covered by support. Use local RTSP / ONVIF cameras for a reliable experience."},{"page":"cameras/settings","title":"Camera settings","heading":"Camera settings","anchor":"","text":"Admins open a camera's settings drawer with the Settings (cog) button on the camera."},{"page":"cameras/settings","title":"Camera settings","heading":"Overview","anchor":"overview","text":"The camera's status and device info, plus a live view of its sensors and accessories. Read-only sensors (contact, temperature, motion, and so on) show their current state, and you can operate the controllable ones right here, such as turning a light on, locking a door, or arming a security system."},{"page":"cameras/settings","title":"Camera settings","heading":"Sources","anchor":"sources","text":"Manage the camera's stream sources as in adding a camera: each source's name, role (high, mid, low resolution, or snapshot), stream URLs, and options such as hot mode, preload, and mute audio (drops the audio track from that source). Each source also shows its live status, its stream URL and the codecs the camera itself sends, with profile, level and sample rate where the camera reports them, for example \"H.264 · High · 5.1\" or \"AAC · 16 kHz\"."},{"page":"cameras/settings","title":"Camera settings","heading":"Settings","anchor":"settings","text":"The main configuration form, grouped into sections:\n\n- General. Name, room, camera type, and whether the camera is disabled (also where you remove a camera). The room groups the home view and is the room the camera sits in on the floor plan. Pick one of your rooms or create one here. The delete button next to it removes the picked room: its cameras move to Default and the room leaves the floor plan. Moving a camera to another room takes it off the floor plan until you drag it into the new room there.\n- Branding. Manufacturer, model, and other device details.\n- Interface. Everything about how this camera is shown and played: the streaming mode and source, the playback source for recordings, the activity mode, and the display aspect ratio. For the ratio, pick a preset (16:9, 4:3, 1:1, and more) or type a custom width:height like 21:9.\n- Detection. Per-camera tuning for motion, object, audio, face, license plate and sensor detection: sensitivity, confidence, timeouts, triggers and stationary-object handling, plus a snooze switch. Every section has a reset button next to its heading that puts that section back to its defaults. The confidence values the AI backends use come from here, so a change applies right away. Which object types the camera reports is set in Zones. See Detection & AI.\n- Recording. Whether and how this camera is recorded: the mode, the pre-buffer, and which streams go to disk. An installed NVR plugin does the recording. See Recording.\n- Notifications. What this camera pushes to your phone: the camera's own on/off switch, video in push, which sounds and sensors notify, the cooldown and the notification speed. Which detections notify you is set in Zones, on the alert zones. See Notifications.\n- Autotrack. Let a PTZ camera follow detected objects automatically. See PTZ & autotrack.\n- Snapshot. When the camera takes a new still image: regularly, when viewed, or only on request. See Snapshots and how they refresh.\n- Zones. Draw motion, object, alert and privacy zones, and crossing lines. See Zones & privacy.\n- Frame Worker. Picks the decoder hardware (CUDA, VAAPI, Quick Sync, and more) and device for this camera. Auto chooses the best available. A second decoder selection applies while the camera decodes on an assigned worker, so host and worker can use different GPUs; on Same as server the worker uses the first selection. With a worker assigned, the Decoder line shows where the camera decodes right now. Main stream without GPU forces detection "},{"page":"cameras/settings","title":"Camera settings","heading":"Settings","anchor":"settings","text":"onto the high-resolution stream on CPU-only systems, where it costs real processing power; with a hardware decoder the high-resolution stream is already analysed when something is detected, so leave it off."},{"page":"cameras/settings","title":"Camera settings","heading":"Plugins","anchor":"plugins","text":"Enable plugins for this camera and choose which provides each of its sensors and accessories. This is also where you turn individual sensors on or off and adjust the settings a plugin offers per camera. See Set up sensors for the full walkthrough."},{"page":"cameras/shares","title":"Shares","heading":"Shares","anchor":"","text":"A share lets someone watch a single camera's live stream without a camera.ui account, using a link and an access code. Creating and managing shares is for admins."},{"page":"cameras/shares","title":"Shares","heading":"Creating a share","anchor":"creating-a-share","text":"From a camera, select Share Camera in the player toolbar, then set:\n\n- Source. Which stream quality to share (high, mid, or low resolution).\n- Expires after. How long the link stays valid.\n- Max Viewers. How many people can watch at the same time. Unlimited stops at 50.\n- Label. An optional name to recognize the share later.\n\nSelect Create. camera.ui gives you a Share Link and an Access Code. The access code is shown only once, so copy it before closing.\n\nThe viewer page starts with the sound off. The recipient can turn it on, and on cameras with two-way audio, use the microphone button to talk back.\n\nIn the background the viewer page stops the stream and starts it again on return, unless it plays in picture-in-picture. After a dropped connection it reconnects by itself. Someone over the Max Viewers limit sees Maximum number of viewers reached, and the page keeps trying until a seat is free.\n\nRequires camera.ui Cloud\nSharing needs your server connected to camera.ui Cloud (Settings → Remote) and online. The cloud hosts the link and helps the viewer find your server, but the video travels over a direct, end-to-end-encrypted connection between the viewer and your server. It never passes through camera.ui's servers, and the access code is checked only on your own server."},{"page":"cameras/shares","title":"Shares","heading":"Managing shares","anchor":"managing-shares","text":"A camera's Shares tab lists its active share links: the label, the shared source, when each was created, when it expires, and how many people are watching against the viewer limit. The link can be copied again, the access code can't be retrieved. Select Revoke to end a share immediately: everyone watching is cut off and sees This share link has expired. A share that runs out ends the same way."},{"page":"cameras/shortcuts","title":"Shortcuts","heading":"Shortcuts","anchor":"","text":"Shortcuts are controls you place directly on a camera's video. A shortcut points at another camera or a sensor, and what it does depends on whether you hover or click. Shortcuts are saved per camera and per user."},{"page":"cameras/shortcuts","title":"Shortcuts","heading":"Camera shortcuts","anchor":"camera-shortcuts","text":"A camera shortcut puts a neighbouring camera one move away:\n\n- Hover to preview that camera inline, without leaving the one you're watching. While reviewing a recording, the preview shows that camera at the same moment and follows along as you scrub.\n- Click to switch to that camera. When you're reviewing the past, it opens at the same timestamp."},{"page":"cameras/shortcuts","title":"Shortcuts","heading":"Sensor shortcuts","anchor":"sensor-shortcuts","text":"A sensor shortcut puts an accessory on the video:\n\n- Hover to read its current state, such as a contact sensor's open or closed, a temperature, or a lock's status.\n- Click to control it, for accessories that can be controlled: toggle a light or switch, lock or unlock, open a garage, sound a siren, ring a doorbell, or pick an arm state for a security system. Read-only sensors only show their state.\n\nControlling accessories requires admin. See Sensors & accessories for what each type does."},{"page":"cameras/shortcuts","title":"Shortcuts","heading":"Adding and editing shortcuts","anchor":"adding-and-editing-shortcuts","text":"1. On the camera, click the Shortcuts button in the toolbar (in fullscreen, in the menu of the control bar) to show your shortcuts, then click the pencil (Edit Shortcuts) that appears on the video. The toolbar button on its own only shows or hides the shortcuts you already have.\n2. Click anywhere on the video to drop a shortcut, then choose a Camera or a Sensor as its target.\n3. Drag a shortcut to reposition it.\n4. Long-press a shortcut to remove it."},{"page":"cameras/zones-and-masks","title":"Zones & privacy","heading":"Zones & privacy","anchor":"","text":"Zones mark parts of the camera picture and give that area a job. There are five kinds, each with one job and its own tab in the editor: Motion, Objects, Alerts, Privacy and Lines.\n\nOne rule runs through all of them: a kind only narrows things down once it exists. No motion zone means motion counts everywhere, no object zone means every object type counts everywhere, no alert zone means everything that is detected may alert you.\n\nCameras that detect on their own\nReolink AI, Eufy, ONVIF events and plugins that report detections tell camera.ui what they saw, usually without saying where. Without a box there is nothing to place, so no zone can judge such a report and only the type lists still apply:\n\n- Motion zones don't limit them. Their motion counts everywhere on that camera.\n- Object zones still drop the types you left off, but the area has no effect.\n- Alert zones shrink to a type selection. \"Vehicles in the driveway\" is not possible for such a camera, \"vehicles\" is.\n- Lines never apply, a crossing needs tracking and there is nothing to track.\n\nTwo ways out. Let camera.ui detect for the camera: pick an AI backend as its object sensor instead of the camera's own detection, if your hardware carries the extra stream. Or keep the camera as the sensor and add an object assist plugin, which finds the reported object in the frame. Either way the detection has a box from then on and every zone works as described here. Cameras that do send coordinates are not affected."},{"page":"cameras/zones-and-masks","title":"Zones & privacy","heading":"Opening the editor","anchor":"opening-the-editor","text":"Open a camera's settings and go to the Settings tab. Under Zones you see what the camera already has, each with its color, name and type (Motion zone, Object zone, Alert zone, Privacy zone or Line crossing).\n\nBelow the list, Edit zones opens the editor over the camera image.\n\nMotion, object and alert zones start out covering the whole image, so you drag the corners inward instead of drawing from scratch. A privacy zone is the exception: you click its points yourself."},{"page":"cameras/zones-and-masks","title":"Zones & privacy","heading":"Motion zones","anchor":"motion-zones","text":"Motion zones say where motion counts. Only motion inside a zone counts, in any of them if there are several. Motion is always judged by touch: as soon as movement reaches the zone, it counts."},{"page":"cameras/zones-and-masks","title":"Zones & privacy","heading":"Object zones","anchor":"object-zones","text":"Object zones say which object types count, and where. A zone means: only what is inside here counts, for the types listed on it.\n\nThe Labels list has two groups. Under Base sit the object types: person, vehicle, animal. Under Identification sit Recognize faces and Read plates, which decide whether what counts here also gets a name.\n\nA new object zone starts with nothing selected, which means every type counts in it and faces and plates are recognized as usual."},{"page":"cameras/zones-and-masks","title":"Zones & privacy","heading":"Combining zones","anchor":"combining-zones","text":"A zone only governs the types listed on it. Draw two zones, person on one and vehicle on the other, and a person counts in the first area while a vehicle counts in the second.\n\nGive a type more than one zone and it counts in any of them. A zone with no types selected governs every type, so as soon as one exists, everything has to be inside some zone.\n\nThere is no way to cut a hole out of a zone. To keep an area out of detection, draw the zone around it, or put a privacy zone over it with Detections inside set to Are dropped."},{"page":"cameras/zones-and-masks","title":"Zones & privacy","heading":"Which types survive","anchor":"which-types-survive","text":"Once every object zone carries a type list, those lists together are what the camera detects: a type that appears on none of them is dropped everywhere on that camera.\n\nRecognize faces and Read plates count as types here. If every object zone has a list and none of them carries Recognize faces, the camera stops recognizing faces entirely, not just in one corner."},{"page":"cameras/zones-and-masks","title":"Zones & privacy","heading":"Counting without recognizing","anchor":"counting-without-recognizing","text":"Drop both Recognize faces and Read plates and the zone still detects the person and the car, but no face lands in your face list and no name or plate reaches the event or the push.\n\nA zone that lists person and nothing else no longer recognizes faces. Tick Person and Recognize faces together to get the person counted and named; Recognize faces alone finds nothing.\n\nIt does not lower CPU use, it only holds the result back."},{"page":"cameras/zones-and-masks","title":"Zones & privacy","heading":"Match mode","anchor":"match-mode","text":"Counts as inside when decides what makes a detection count for the zone:\n\n- The object touches the zone. Any overlap is enough. This is the default.\n- The object is fully inside. The whole box has to sit in the zone. A car that just clips the edge no longer triggers it."},{"page":"cameras/zones-and-masks","title":"Zones & privacy","heading":"Alert zones","anchor":"alert-zones","text":"An alert zone decides what gets you notified and changes nothing else: detection, events and recordings are untouched by it.\n\nOnce a camera has an alert zone, the zones decide its alerts:\n\n- A type listed on a zone alerts while it is inside that zone.\n- The same type outside that zone stays quiet.\n- A type that no alert zone lists stays quiet everywhere on the camera.\n\nWith one alert zone over the driveway listing person and vehicle, someone on the road still lands on the timeline and in the recording, but only the driveway alerts. Animal is then on no zone, so animals stop alerting on that camera. Give a type a zone covering the whole image if it should keep alerting everywhere.\n\nAn alert zone with no types selected alerts on every type from inside it.\n\nIf you put a type on an alert zone that your object zones filter out, the editor says so."},{"page":"cameras/zones-and-masks","title":"Zones & privacy","heading":"Who exactly, and which plate","anchor":"who-exactly-and-which-plate","text":"A zone can also name the people it pushes for. The Labels list has a second group, Who or what exactly, with Faces and Plates. Ticking one adds a field below it and pulls the matching type into the zone: Faces brings person along, Plates brings vehicle.\n\nFaces is a list of who may push from inside the zone. It offers Unknown faces for everyone the recognition could not name, Any known face for anyone you have enrolled, and then each enrolled person by name. Pick nothing and every face pushes.\n\nPlates works the same way, except you type the plates yourself rather than pick them. Pick nothing and every plate pushes.\n\nBoth lists only ever hold back people and vehicles.\n\nSomeone the camera could not identify counts as an unknown face, whether the recognition failed or no face was ever captured. A person on the ignore list stays silent either way. And if the object zone over that area has Recognize faces switched off, nobody there is ever identified, so everyone counts as unknown."},{"page":"cameras/zones-and-masks","title":"Zones & privacy","heading":"Where it counts as inside","anchor":"where-it-counts-as-inside","text":"Alert zones add a third option, The object stands in the zone: only the bottom center of the box has to be inside. Good for a driveway seen at an angle, where a tall person leans well past the edge. The default here is The object is fully inside.\n\nOnce a type has been inside during a stretch of activity, that whole stretch alerts. Someone who crosses the driveway and then waits on the road still gets you the alert for that visit."},{"page":"cameras/zones-and-masks","title":"Zones & privacy","heading":"What still applies","anchor":"what-still-applies","text":"The master switch, the cooldown and quiet hours are separate from the zones and apply on top.\n\nA doorbell press, a contact sensor, a siren or an audio alert is not an object detection, so an alert zone never holds it back."},{"page":"cameras/zones-and-masks","title":"Zones & privacy","heading":"Privacy zones","anchor":"privacy-zones","text":"A privacy zone covers an area in black, for example a neighbour's window or a public pavement.\n\nDetections inside decides what happens to detections there:\n\n- Are dropped. Detections that sit completely inside the area are thrown away. Something that straddles the edge, like a person half in and half out, still counts. This is the default.\n- Still count. The area is hidden but still watched, so a person walking behind it is detected without being shown."},{"page":"cameras/zones-and-masks","title":"Zones & privacy","heading":"What is hidden, and what is not","anchor":"what-is-hidden-and-what-is-not","text":"The black area is not burned into the video.\n\nHidden:\n\n- Event and recording thumbnails, moment cards, face and plate crops, and episode mosaics.\n- Pictures attached to push notifications.\n- Snapshots: camera tiles, the camera list, the overview, the snapshot API, MQTT, HomeKit stills and the Home Assistant camera image.\n- Live view and playback in the camera.ui web interface, and in the mobile apps, which run that same interface. The area is drawn black on top of the video.\n- Live view, playback and the episode player in the Apple TV app, and the clip that loops on its Home screen, where the app draws the area black on top of the video.\n- The preview that plays while you hover an event or a recording, and a picture you take with the camera button in the player.\n\nNot hidden:\n\n- The recording on disk, and anything you export or download from it.\n- Video clips attached to push notifications.\n- RTSP, and everything that reads it: HomeKit live and HomeKit recordings, the Home Assistant stream, and other apps.\n- Picture-in-picture, which pops the bare video out of the page and leaves the black area behind.\n\nIf an area must never be filmed at all, point the camera elsewhere, or use the camera's own privacy mask if it has one, which blanks the area before the picture reaches camera.ui."},{"page":"cameras/zones-and-masks","title":"Zones & privacy","heading":"When a picture cannot be covered","anchor":"when-a-picture-cannot-be-covered","text":"Sometimes camera.ui cannot paint the black area, for example on a hardware frame it could not read back, or a pixel format it does not know. When a picture cannot be covered, at the bottom of the Privacy tab and valid for the whole camera, decides what happens then:\n\n- Send it anyway. The picture goes out uncovered. This is the default.\n- Send no picture. No picture is produced at all."},{"page":"cameras/zones-and-masks","title":"Zones & privacy","heading":"Lines (line-crossing)","anchor":"lines-line-crossing","text":"A line detects when something crosses it. Crossing direction picks when it fires:\n\n- A ↔ B both ways triggers in either direction.\n- A → B or B → A triggers on a crossing in one direction.\n\nA line can be limited to person, vehicle and animal; a new line starts with all three selected. A line never filters detection."},{"page":"detection/ai-backends","title":"Object detection & AI backends","heading":"Object detection & AI backends","anchor":"","text":"Object detection finds people, vehicles, and animals in the frame, and is the basis for faces, license plates, and semantic search. It runs on an AI backend you choose to match your hardware."},{"page":"detection/ai-backends","title":"Object detection & AI backends","heading":"Choosing a backend","anchor":"choosing-a-backend","text":"camera.ui ships six AI backends as plugins. Pick the one for your hardware and enable it per camera in Settings → Plugins (see Set up sensors):\n\n- CoreML. For Apple Silicon Macs; uses the GPU and Neural Engine.\n- OpenVINO. For Intel CPUs and GPUs.\n- ONNX. Cross-platform, on CPU or a supported GPU.\n- NCNN. A lightweight backend for supported hardware. (No semantic search.)\n- Coral. For a Coral Edge TPU, with a CPU fallback. Linux only.\n- Hailo. For a Hailo-8 / Hailo-8L accelerator. Linux only.\n\nCoreML, OpenVINO, ONNX, and NCNN provide object, face, and license-plate detection, and person re-identification and segmentation for finding an object. CoreML, OpenVINO, and ONNX also provide CLIP for semantic search. Coral and Hailo are edge-AI accelerator backends that provide object detection only (no faces, license plates, or CLIP)."},{"page":"detection/ai-backends","title":"Object detection & AI backends","heading":"Models and confidence","anchor":"models-and-confidence","text":"In a backend's plugin settings you choose a model for each task (object, face, license plate, segmentation, CLIP). Larger models are more accurate but heavier. camera.ui downloads the models it needs automatically.\n\nConfidence is not set here but per camera in Settings → Detection, for object, face and plate detection alike, and a change takes effect right away. Objects have one value per type, each between 0.3 and 1: Person Confidence, Vehicle Confidence and Animal Confidence.\n\nThe backend reports everything from 0.25 up. Below your value a detection never starts an event, a notification or a sensor state, it only helps camera.ui keep following an object it already tracks.\n\nWhich object types a camera reports is set in its object zones. Motion and audio are not affected.\n\nNext to the confidence values is Object timeout, in seconds (minimum 10, default 15). It only matters for cameras that report objects on their own, like many Reolink models: it caps how long such a detection stays active when the camera never sends an end signal. Frame-based backends ignore it.\n\nBelow it is Ignore stationary objects, on by default, see Stationary objects."},{"page":"detection/ai-backends","title":"Object detection & AI backends","heading":"Where detection looks","anchor":"where-detection-looks","text":"A detection model works on a small square, often 640 pixels. Squeezed into it, a 4K frame leaves a person at the end of the driveway a few pixels tall. camera.ui instead cuts the moving areas out of the full-resolution frame, several per frame if the movement is spread out, runs the model on those and maps the boxes back onto the picture, so small and distant objects get found. Nothing to set up.\n\nWhen nothing is moving, the whole frame goes through. The Zoom column in the metrics below shows how often a camera worked this way."},{"page":"detection/ai-backends","title":"Object detection & AI backends","heading":"What detection costs","anchor":"what-detection-costs","text":"Metrics → Cameras shows two tables. The first is the process table, one row per camera, with CPU and memory. The second, Detection, shows where the time goes:\n\n- Decode, the time for one frame: analysis stream first, then the high-resolution stream.\n- Processing, scaling to the model input and turning the results back into boxes. On a camera without an object detector it shows the scaling for motion.\n- Transport, the time the frame and the result spend on the way to the plugin and back.\n- Network, the bit rate of frames sent over the network to detection plugins on another machine, with those plugins behind it. See Worker assignments.\n- Analysed, frames per second analysed, again analysis stream then high-resolution stream.\n- Active, the share of time the camera analysed at all. A camera with a frame loop sits at 100%, a camera that reports motion itself only counts during events.\n- Detections, objects found per analysed frame.\n- Zoom, the share of object detections that looked at zoomed motion windows instead of the whole frame, with the number of windows per zoomed frame behind it.\n\nShow inference switches the same table to the time inside each detector: Motion, Object, Face, Recognition, Re-ID, Plate, Classifier and CLIP. Point at a cell to see the plugin, the model and the device it runs on, and a camera running on a worker names that worker. Values are averages since the camera started; use Reset after changing a model. Copy puts both views for every camera on the clipboard as text."},{"page":"detection/ai-backends","title":"Object detection & AI backends","heading":"Benchmark","anchor":"benchmark","text":"Benchmark loads the object detector of the cameras you pick with test frames and reports how many detections per second the machine manages, in total and per camera, with each camera's plugin, model and device. In the plugin at once is how many of the calls In flight the plugin held at the same time on average: at 1 it takes one detection after the other. Copy puts the result on the clipboard.\n\nDetection pauses on every camera while the run is going, including the ones you did not pick, and continues when it is done. Only admins can run it."},{"page":"detection/ai-backends","title":"Object detection & AI backends","heading":"Object assist","anchor":"object-assist","text":"Some cameras detect objects on their own but report only that they saw something, not where in the frame it is. Many Reolink models work this way. That leaves face and license-plate recognition nothing to zoom in on, so they rarely find anything.\n\nUnder the camera's Plugins tab, pick an Object Assist plugin below Object. It is a second detector that locates the person or vehicle in the frame, so face and plate recognition get a proper crop to work on. See Set up sensors."},{"page":"detection/ai-backends","title":"Object detection & AI backends","heading":"Camera detection as a second opinion","anchor":"camera-detection-as-a-second-opinion","text":"Detection needs more than one sighting, so a person seen only for a moment (behind a fence or a clothesline) is dropped as a flicker. If the camera also detects objects on its own (a camera plugin that reports people or vehicles, like the Reolink plugin or ONVIF analytics) while a backend here does the detection, a camera report of the same kind of object at the same moment confirms that glimpse, and the card shows its picture. The camera's report never creates a detection by itself. Nothing to set up."},{"page":"detection/ai-backends","title":"Object detection & AI backends","heading":"What you get","anchor":"what-you-get","text":"Detected objects appear on the live view as boxes, drive notifications, and become events you can browse and filter by type. They also feed face recognition, license plates, and semantic search. A heatmap overlay on the player shows where objects were detected most often over a time window."},{"page":"detection/audio","title":"Audio detection","heading":"Audio detection","anchor":"","text":"Audio detection listens to a camera's audio for specific sounds, such as glass breaking, an alarm, or a dog barking. It runs alongside video detection and can trigger events and notifications on its own. The camera needs to provide an audio stream."},{"page":"detection/audio","title":"Audio detection","heading":"Setup","anchor":"setup","text":"Enable the audio plugin for a camera in Settings → Plugins (see Set up sensors). The plugin has no settings of its own: it listens for the standard sound list and takes its confidence from the camera.\n\nThe sounds it recognizes are glass breaking, screaming, gunshots, alarms (smoke, car, siren), a doorbell, knocking, a dog bark, a cat, a baby crying, and speech."},{"page":"detection/audio","title":"Audio detection","heading":"Settings on the camera","anchor":"settings-on-the-camera","text":"Audio detection is tuned in Settings → Detection, per camera:\n\n- Min. volume (dBFS). Audio frames below this level are skipped. Ranges from -100 (silence) to 0 (maximum), default -40.\n- Audio confidence. Sounds the detector is less sure about than this are dropped (0 to 1, default 0.7).\n- Audio timeout. How long an audio event stays active after the last trigger (minimum 10 seconds)."},{"page":"detection/episodes","title":"Episodes","heading":"Episodes","anchor":"","text":"When several cameras see the same activity around the same time, camera.ui can bundle those sightings into one episode: a single title and a short story that follows the subject across cameras, in order. A person walking from the driveway to the front door becomes one episode instead of three separate events.\n\nThe story comes from AI descriptions. Without them an episode gets a title with the places and the recognized names instead, and forms only when something ties the sightings together without pictures: a face or plate recognized on both cameras, or a walk on the floor plan or one seen before between the cameras that fits the time in between with at most a short stop."},{"page":"detection/episodes","title":"Episodes","heading":"Turning it on","anchor":"turning-it-on","text":"In Settings → Recordings, open the Episodes tab and turn on Enabled. Every camera takes part unless its own Episodes toggle in the camera's settings is off; an episode forms once at least two different cameras saw related activity. With a drawn floor plan, camera.ui also checks whether the sightings fit together in space: unrelated things happening at the same time in different corners of the property become separate episodes instead of one mixed story, and the title names the places involved. The walk and the time in between then decide which sightings belong together, and the AI only leaves out a sighting that clearly shows someone else. Without a floor plan camera.ui learns the walks between cameras on its own, from faces and plates recognized on both and from the episodes it told, and uses those of the last 30 days the same way.\n\n- Join Window. A new event joins the open episode when it starts within this long after the last activity. Default 90 seconds.\n- Quiet Time. The episode closes and gets its story once it has been this quiet. Default 60 seconds.\n- Wrap Up After. Once an episode reaches this length it stops taking in new activity and closes at the next natural break: whatever is still happening finishes first, and later activity starts the next episode. Default 10 minutes.\n- Custom Instructions. Free-text guidance added to the built-in prompt for the story, for example what to watch for or how to phrase it.\n- Video in Push. Whether an episode push plays the episode clip, which switches cameras like the episode player. Off never attaches it, Always always does, and Camera setting attaches it when at least one camera in the episode has Video in Push turned on. Default Camera setting."},{"page":"detection/episodes","title":"Episodes","heading":"Where episodes show up","anchor":"where-episodes-show-up","text":"- Home. Episodes appear in the Recent Events row as a mosaic card, showing how many cameras took part.\n- Recordings. The same mosaic card appears in the list. A recording that belongs to an episode shows an Open episode button that jumps to it. Episode cards follow the camera, time range, Only with recording and Favorites only filters; any other filter (labels, triggers, attributes, vehicles, search) hides them.\n- Apple TV. Episodes are available in the Apple TV app too.\n- Push. An episode pushes once its story is ready, also from cameras with Send notifications turned off. With that switch off on every camera, you get pushes only for activity across several cameras. Alert zones and their face and plate filters still count: an episode whose sightings all lie outside them stays in Recordings without a push."},{"page":"detection/episodes","title":"Episodes","heading":"The episode player","anchor":"the-episode-player","text":"An episode plays as one sequence, switching cameras as the story moves and showing the current camera's name and time. The strip below the video has one block per camera stretch. Zoom and pan work as in the live view.\n\nWhen a second camera covers the same room on the floor plan, the cut plays whichever had the subject closest, and the control bar offers Second angle for the other view of that stretch."},{"page":"detection/episodes","title":"Episodes","heading":"Why an episode turned out this way","anchor":"why-an-episode-turned-out-this-way","text":"Admins open what went into an episode with the trace icon on its card and in the player: every event that was a candidate and whether it stayed or went, and for the ones that went the reason, from \"never moved\" to \"belongs to another episode\". Below that are the connections it drew between the remaining events, both AI passes with the pictures and instructions the model was given and what it answered, and the cut that came out, including a stretch that was dropped because another camera saw it better. Download bundle packs it together with the clip for a bug report."},{"page":"detection/events-and-detections","title":"Events & detections","heading":"Events & detections","anchor":"","text":"When detection fires, camera.ui groups what it finds into an event."},{"page":"detection/events-and-detections","title":"Events & detections","heading":"The pieces","anchor":"the-pieces","text":"- Event. Everything that happens during one continuous burst of activity at a camera. An event has a start and an end, and can update as it goes.\n- Segment. A stretch of an event where the AI kept seeing objects, with its own thumbnail. Objects leaving and coming back start a new segment, and an event where the AI never sees an object, like a doorbell press with nobody in view, has none.\n- Detection. Something the AI found in a segment, such as a person or a vehicle, with a confidence score.\n- Attribute. Extra detail on a detection, such as a recognized face, a license plate, or a semantic (CLIP) embedding used for search."},{"page":"detection/events-and-detections","title":"Events & detections","heading":"Triggers vs detections","anchor":"triggers-vs-detections","text":"- A trigger is a signal that something happened: motion, a doorbell, a contact sensor, an alarm sound. A trigger can start an event on its own.\n- A detection is what the AI actually recognized in the video, like an object, a face, or a plate.\n\nYou can filter and alert on a doorbell press (a trigger) or only when a person is seen (a detection)."},{"page":"detection/events-and-detections","title":"Events & detections","heading":"The picture an event carries","anchor":"the-picture-an-event-carries","text":"Every card, push and AI description shows one picture for the moment, and camera.ui picks the frame and the crop for it. It frames the subject, so a person arrives as a whole person, not as a close-up of a shoulder. Whatever moves in the picture at the same time is framed with it, whatever stands still is not. When a face is recognized later in the same event, the \"Unknown\" crop is dropped rather than kept alongside the name.\n\nA camera that reports detections without saying where in the frame they were, many Reolink and ONVIF cameras, gives nothing to frame. When camera.ui runs motion detection on the camera's picture itself, the moving part is framed instead, which is less precise. Otherwise the picture shows the whole scene. An object assist plugin gives those cameras real boxes."},{"page":"detection/events-and-detections","title":"Events & detections","heading":"Stationary objects","anchor":"stationary-objects","text":"Ignore stationary objects is on by default in a camera's Settings → Detection: once an object (a parked car, a bin) has held still for a while, camera.ui stops counting it as a detection, across later events too. Its box still shows in the live view. It counts again once it moves off the spot, or if the camera pans or tilts. A person who keeps leaving their spot and coming back keeps counting. Small movements while sitting or standing are not enough for that."},{"page":"detection/events-and-detections","title":"Events & detections","heading":"The detection trace","anchor":"the-detection-trace","text":"Every event keeps a record of what the detector actually saw. Admins open it from the dots menu on a card in Recordings or the trace icon in the event dialog. Opened from a card it starts at the moment the card shows, from the camera view at the moment on screen. For an event that ran for hours, type a clock time above the frame strip to jump there; Earlier frames loads what came before.\n\nIt steps through the event frame by frame with pictures from the recording, keeping only a few frames around each activity, and shows per frame the objects and the tracks they belong to, the motion boxes, the raw detections from 25 % confidence up, the camera's own detection reports, and the zones as they stood at the time. Where something was seen but did not become an event, the trace names the reason: under the confidence threshold, inside a privacy zone, outside the object zones, a label the zone does not accept, or a track too young to be confirmed. Faces and plates dropped by the camera's settings show dashed, with the setting they missed. When a less certain sighting kept a track going, the track shows that frame's confidence next to the last certain one. Each face read shows whom it matched, or who came closest when nobody did (both people when two came about equally close), and how many reads agree on the name. Every track carries its state, from tentative through active, stationary and lost to departed.\n\nFrames zoom like the live view (wheel, pinch, double tap). An event without a recording shows its detection data without frames. If the browser cannot decode the main stream, the pictures come from the smaller one and are matched by time instead of exactly.\n\nDownload bundle packs the whole trace into one file for a bug report. The video in it covers the whole event, or the 10 minutes around the selected frame when the event ran longer."},{"page":"detection/events-and-detections","title":"Events & detections","heading":"Where events show up","anchor":"where-events-show-up","text":"Events drive the rest of camera.ui:\n\n- Recordings — browse and filter past events.\n- Notifications — get alerted about the ones that matter.\n- Semantic search — find events by describing them."},{"page":"detection/faces","title":"Face recognition","heading":"Face recognition","anchor":"","text":"Face recognition spots faces on your cameras, recognizes people you have enrolled, and groups the ones it doesn't know yet.[^license] It builds on object detection, and the camera needs an AI backend in two places under its Plugins tab: Face finds the faces, Face Recognition turns each one into something camera.ui can compare. Without the second one faces are found but never named. If the camera detects objects itself without reporting where they are (many Reolink models), enable Object Assist so faces get a proper crop."},{"page":"detection/faces","title":"Face recognition","heading":"Enrolling a known face","anchor":"enrolling-a-known-face","text":"In the Faces view (sections Known Faces and Unknown Faces), Add Face takes a name and a clear photo of the person; Enroll rejects a photo without a face. From then on the person is recognized in new events, with their name as an attribute on detections."},{"page":"detection/faces","title":"Face recognition","heading":"Getting good results","anchor":"getting-good-results","text":"- Camera position. Mount a camera meant for faces at about head height, or with a shallow angle. Looking down steeply it sees forehead and hair, different people start to look alike, and recognition mixes them up.\n- Pictures per person. At least 10 pictures are recommended, from different situations (daylight, night, with a cap). Every further look up to the maximum helps. With fewer the person is often missed, their card and their dialog point it out.\n- Unclear faces get no name. A face seen from the side, from above, or blurred is neither named nor collected under Unknown Faces. The person is recognized in the next picture that shows them clearly.\n- Small faces. A face smaller than about 40 pixels in the camera picture is too small to recognize.\n- One picture is not enough. A person the camera follows gets a name once three pictures of the face agree, and camera.ui looks again whenever the face shows bigger or clearer, so a good view late in a long visit still counts. When the pictures disagree, the person stays unnamed rather than getting someone else's name. A picture about as close to two enrolled people counts for neither. On a camera whose own AI reports people, faces are only read on its reports, and one picture names them.\n- A name in doubt waits for a new picture. After two people cross, when someone starts moving again or comes back after the camera lost sight of them, and when a picture matches someone else, the name stays hidden until a new picture of the face confirms it. If that picture matches someone else too, the event gets a second face for that person, and the first keeps its name.\n- Two faces, one person. When two people are so close that the camera sees them as one person, each face gets only the name its own picture matches. The same goes for a face outside every detected person."},{"page":"detection/faces","title":"Face recognition","heading":"A person's pictures","anchor":"a-persons-pictures","text":"A person under Known Faces opens with their training pictures. A mark on a picture says what it does to recognition, tap it for the reason:\n\n- No clear face. Turned away, from above or blurred. Such a picture makes recognition worse, remove it.\n- Recognition can't link it. To recognition the picture looks no more like the person's other pictures than a stranger's, usually a strong profile or a view from above. It stays with the person but isn't used to recognize anyone. If it shows someone else, remove it.\n- Face partly visible. Keep it only while better pictures of that view are missing.\n- Also filed under … The same picture is enrolled for someone else as well. Keep it for the right person only.\n- Same as another picture. One of the two is enough.\n\nThe person's card already shows how many pictures carry a mark, in red when one of them is unclear or also filed under someone else.\n\nAssigning an unknown face warns when it is already filed under someone or only partly visible. Unclear faces never reach Unknown Faces in the first place."},{"page":"detection/faces","title":"Face recognition","heading":"Unknown faces","anchor":"unknown-faces","text":"Faces that don't match anyone enrolled collect under Unknown Faces, grouped automatically so the same person's shots cluster together.\n\n- Assign a group to a person to enroll everyone in it at once, or assign a single face.\n- Discard groups or faces you don't care about, or clear all ungrouped faces."},{"page":"detection/faces","title":"Face recognition","heading":"Correcting a recognition","anchor":"correcting-a-recognition","text":"When an event names the wrong person, open that face picture on its recording card, pick Correct person in the dots menu and assign the right name, or Mark as unknown. Only this face changes: other faces in the recording keep their names, even when they carry the same one. The crop is added to that person so recognition gets it right next time, unless it shows no clear face: then only the name changes. The dialog says so beforehand, and warns when the crop is only partly visible or already filed under someone else. Admins only."},{"page":"detection/faces","title":"Face recognition","heading":"Ignoring a face","anchor":"ignoring-a-face","text":"Ignore a face you never want to see again (a passing stranger, the mail carrier) and camera.ui stops collecting it.\n\n- Ignore a face or a whole group. Unknown faces and groups each have an ignore button; selection mode ignores several at once.\n- New sightings are dropped. Once someone is ignored, their later shots don't come back as unknown faces.\n- Ignored faces sit in their own list. They collect under Ignored Faces at the bottom of the Unknown Faces section.\n- Restore to undo. Restore puts an ignored face back among the unknown faces. New sightings of that person come back once none of their other pictures is still ignored."},{"page":"detection/faces","title":"Face recognition","heading":"Settings","anchor":"settings","text":"In Settings → Recordings, face recognition has:\n\n- Max Training Images. How many photos to keep per person (5 to 40, default 20). At the limit the picture most like another one of the same person makes way.\n- Max Unknown People. How many unknown groups are kept (10 to 500). Once the limit is hit, the group that was seen longest ago is dropped. Raise it for busy outdoor cameras.\n\nPer camera, Settings → Detection has two face settings:\n\n- Face confidence. Faces the AI is less sure about than this are ignored for events and automations (0 to 1, default 0.5).\n- Recognize faces. How certain the match against your enrolled people has to be before someone is named: Strict, Balanced (default) or Relaxed. Strict gives fewer wrong names, Relaxed recognizes people more often."},{"page":"detection/faces","title":"Face recognition","heading":"Changing the recognition model","anchor":"changing-the-recognition-model","text":"The model is a setting of the AI backend (Face Embedding Model), shared by all its cameras. After a change camera.ui embeds the enrolled pictures again by itself. Re-embed faces, bottom left in the Faces view and admins only, starts it by hand. Recognition is patchy until it finishes. Pictures in which the new model finds no clear face are removed, and the dialog names the people this affected.\n\nTo try a model, open the AI backend's page and its Face Recognition tab: drop a picture and it shows who camera.ui takes it for and how sure it is."},{"page":"detection/faces","title":"Face recognition","heading":"Rescanning","anchor":"rescanning","text":"After enrolling someone new, select Rescan existing events to re-check past recordings and tag them with the newly known face.\n\n[^license]: Face recognition (matching faces against your enrolled people) builds on recording and requires an active camera.ui subscription. Detecting that a face is present is part of the free detection."},{"page":"detection/find-object","title":"Find an object","heading":"Find an object","anchor":"","text":"Open Find in the timeline, tap a person or a vehicle in the picture, and camera.ui lists the moments that show it.[^license]"},{"page":"detection/find-object","title":"Find an object","heading":"Picking an object","anchor":"picking-an-object","text":"While the Find view of the timeline is open, every picture can be picked. A tap or a click freezes the picture at that frame, pauses the player and picks the object at that spot with the detection assigned to the camera. Outlines then mark every person and vehicle in the frozen picture, and each further tap adds a pick.\n\n- Find this object in the player's control bar (in the dots menu on a small player) or F opens and closes the view. In Camview F also opens the grid's timeline.\n- Press and hold a picture, with a finger or the mouse, to freeze it and pick from any view, then Find opens.\n\nA frozen picture plays on with Continue at its top, play, going live, a match you open, Esc or End search. The picks stay unless you end the search. Scrubbing the timeline moves a frozen picture to the new moment, so you can pick a better frame of someone who looked away.\n\nOnly admins can pick, and only with the NVR plugin and object detection assigned to the camera. A camera without object detection has no Find, in Camview its tile says No object detection while Find is open. The picture is read with the models and confidences set for the camera. When the camera's own detection cannot read a picture, the AI backend set as its Object Assist reads it; without one the search says so.\n\nWith an AI backend as Segmentation in the camera's Plugins tab, the object gets its outline instead of a box, and where people or vehicles overlap, a click picks the one under the pointer."},{"page":"detection/find-object","title":"Find an object","heading":"What a search goes by","anchor":"what-a-search-goes-by","text":"A search goes by everything the picture offers at once and lists each moment once.\n\n- A person. By name when the face was recognized, otherwise by the face compared with the faces of other moments. Also by clothing and build, which finds people who look alike: this needs an AI backend as Person Re-ID in the camera's Plugins tab and finds the moments of cameras that have one. Someone in other clothes looks like someone else to it, and moments from before the camera had Person Re-ID have no clothing to compare. Cameras that report their objects themselves (with or without Object Assist) store no clothing either.\n- A vehicle. By its plate when one was read, with plates read one character off, and by its look: vehicles that look alike. The look needs a CLIP backend, see Semantic search.\n\nHow well people are found by name depends on face recognition."},{"page":"detection/find-object","title":"Find an object","heading":"The matches","anchor":"the-matches","text":"The Find view shows each pick with its crop and the matches of all picks in one list, one card per moment, the surest first: the name or the plate, then the face, then the look-alikes by clothing or look, best match first. With several picks each card shows the pick it came from. Where a match is a comparison, a slider from More precise to Broader sets how close it has to be. Removing a pick drops its matches, End search removes all of them.\n\nWhile a search runs, the timeline shows only the matching events. Turning off Matches only in the timeline's filter menu shows all events again.\n\n- A card of a camera on screen plays that moment there.\n- A card of another camera opens that camera at the moment, with its pick in Find.\n- In the episode player Find is a column beside the player (on a phone below it). A match inside the episode plays there, one outside opens as a recording.\n\nShow all in Recordings lists the matches of all picks in Recordings, where the camera, room and time range filters narrow them and End search returns to the normal list."},{"page":"detection/find-object","title":"Find an object","heading":"Search by picture","anchor":"search-by-picture","text":"In Recordings, Search by picture (admins only) takes a picture of a person or a vehicle: drop it on the field or anywhere on the list, or choose one. Click the person or the vehicle in it, and the list shows the matches as above. The picture is read with the plugins and models of the camera that has most of them assigned."},{"page":"detection/find-object","title":"Find an object","heading":"Trying the models","anchor":"trying-the-models","text":"The AI backend's plugin page has a Person Re-ID and a Segmentation tab. Person Re-ID takes a picture of a person and searches the recordings for people who look alike, Segmentation outlines every person, vehicle and animal in a picture."},{"page":"detection/find-object","title":"Find an object","heading":"Vehicle color and type","anchor":"vehicle-color-and-type","text":"Vehicles in the Recordings filters narrows the list to colors and types: car, van, truck, bus, motorcycle, bicycle. camera.ui reads both from the picture of every vehicle it records, older recordings included, and needs CLIP for it. A reading can be wrong, a gray car in the shade may count as black.\n\n[^license]: Finding objects and the vehicle filter build on recording, which requires an active camera.ui subscription."},{"page":"detection/genai-descriptions","title":"AI descriptions","heading":"AI descriptions","anchor":"","text":"A generative-AI model writes a short summary of each moment in an event: a title, what happened, a notification-friendly line, and a threat level. It names people, plates, and detected movement.\n\nNames and plates come from what face recognition and plate reading actually confirmed, not from what the model thinks it can read in the picture."},{"page":"detection/genai-descriptions","title":"AI descriptions","heading":"Choosing a provider","anchor":"choosing-a-provider","text":"The model comes from Settings → Assistant: allow the NVR plugin under Plugin access and pick one of your models. The plugin sends its requests to camera.ui, so the key stays on the server. Any provider the assistant supports works, as long as the model reads pictures; the picture test in the model list tells you.\n\nDescriptions are written in the Answer language of the assistant settings, in English when it follows the interface, and look at up to four pictures per event. Enable them per camera with the camera's AI descriptions toggle."},{"page":"detection/genai-descriptions","title":"AI descriptions","heading":"Moments and Episodes","anchor":"moments-and-episodes","text":"The model feeds two features, each with its own tab in the recording settings:\n\n- Moments. Enabled turns on AI descriptions of events. Custom Instructions adds free-text guidance to the built-in prompt. Model Timeout is how long a description may take before the push goes out without it: 60 seconds by default, raise it for a slow local model. How the descriptions reach your phone is set per camera, see Notifications.\n- Episodes. Bundles related events across cameras into one story, with its own Custom Instructions. See Episodes."},{"page":"detection/genai-descriptions","title":"AI descriptions","heading":"Privacy","anchor":"privacy","text":"With a local model (Ollama, a local OpenAI-compatible server, a plugin model), everything stays on your own hardware. With a cloud provider, event snapshots are sent to that provider to generate the description.[^license]\n\nA drawn floor plan adds text context to the prompts: how the rooms connect, walking times between cameras, and what nearby sensors read at the time.\n\n[^license]: AI descriptions build on recording, which requires an active camera.ui subscription."},{"page":"detection/","title":"Detection & AI","heading":"Detection & AI","anchor":"","text":"Detection runs on your own hardware and picks up movement, people and vehicles, faces, license plates, and sounds. The results become events you can browse, get notified about, and search."},{"page":"detection/","title":"Detection & AI","heading":"How detection works","anchor":"how-detection-works","text":"Detection is layered:\n\n1. Motion runs continuously and cheaply. It just notices that something changed.\n2. When a trigger fires, the heavier AI wakes up. It runs object detection (people, vehicles, animals), then looks closer at what it found: faces on the people it sees, license plates on the vehicles, and a semantic fingerprint for search. Motion is the usual trigger. A detected sound wakes the AI too, and so can another sensor, for example a door contact.\n\nWhile an event is active, the heavier analysis (objects, faces, plates, and search) switches to the camera's high-resolution stream for sharper crops, then drops back to the lighter stream once the scene goes quiet.\n\nflowchart LR\n cam[Camera frame] --> motion{Motion?}\n cam -. parallel .-> audio[Audio]\n sensor[Sensor triggere.g. door contact] --> obj\n motion -- no --> idle[Heavy AI stays idleunless another trigger fires]\n motion -- yes --> obj[Object detectionperson · vehicle · animal]\n audio -- sound --> obj\n obj -- person --> face[Faces]\n obj -- vehicle --> plate[License plates]\n obj --> clip[CLIP]\n obj --> ev[(Event)]\n face --> ev\n plate --> ev\n audio --> ev"},{"page":"detection/","title":"Detection & AI","heading":"What you can detect","anchor":"what-you-can-detect","text":"- Motion — movement in the frame.\n- Objects — people, vehicles, and animals.\n- Faces — recognize known people and group unknown ones.\n- License plates — read plate numbers.\n- Audio — sounds like glass breaking, alarms, or a dog barking.\n- Semantic search — find moments by describing them in words.\n- Find an object — click a person or vehicle and get the moments that show it.\n- AI descriptions — a written summary of what happened.\n- Episodes — related sightings across cameras told as one story."},{"page":"detection/","title":"Detection & AI","heading":"Plugins do the work","anchor":"plugins-do-the-work","text":"Detection is provided by plugins you enable per camera: a motion engine and an AI backend that matches your hardware. You choose and tune them in a camera's settings, see Set up sensors.\n\nEach detection becomes part of an event. See Events & detections for how those are structured, and Recording (NVR) for browsing them.\n\nThe detection models themselves learn from frames the community labels and contributes. See Model training."},{"page":"detection/license-plates","title":"License plates","heading":"License plates","anchor":"","text":"License-plate recognition reads plate numbers from vehicles. It builds on object detection: a vehicle is detected, then its plate is located and read by OCR. Enable an AI backend with license-plate support on the camera. If the camera detects objects itself without reporting where they are (many Reolink models), enable Object Assist so plates get a proper crop."},{"page":"detection/license-plates","title":"License plates","heading":"Setup","anchor":"setup","text":"In the backend's plugin settings, license plates use two models, a detector that finds the plate and an OCR model that reads the characters. camera.ui downloads them automatically. Each has its own confidence on the camera in Settings → Detection, next to a length filter:\n\n- Plate detection. Plates found with less confidence than this are ignored (0 to 1, default 0.3).\n- Plate reading. Readings the OCR is less sure about than this are ignored (0 to 1, default 0.9).\n- Min. plate length. Readings shorter than this are dropped as fragments (default 4 characters)."},{"page":"detection/license-plates","title":"License plates","heading":"Where plates appear","anchor":"where-plates-appear","text":"A read plate is attached to its vehicle as an attribute. You'll find it:\n\n- On the event, alongside the vehicle.\n- In Recordings, where you can filter by the License plate attribute or search for a specific plate.\n- In notifications and AI descriptions, for example \"Plate ABC-123\"."},{"page":"detection/motion","title":"Motion detection","heading":"Motion detection","anchor":"","text":"Motion is the first and cheapest layer of detection. It watches for change in the frame and, when it sees movement, wakes the heavier AI. On its own it can also trigger recordings and notifications."},{"page":"detection/motion","title":"Motion detection","heading":"Choosing a motion engine","anchor":"choosing-a-motion-engine","text":"camera.ui offers several motion plugins. Enable one for a camera in its settings under the Plugins tab (see Set up sensors):\n\n- Rust Motion. Fast and efficient; a good default for most setups.\n- OpenCV. Several algorithms with fine-grained tuning.\n- OpenCL. GPU-accelerated, for very low CPU use.\n- WASM Motion. Cross-platform WebAssembly.\n- Pam Diff. Lightweight pixel-difference.\n- SMTP. For cameras that send motion alerts by email instead of analysing the video."},{"page":"detection/motion","title":"Motion detection","heading":"Tuning sensitivity","anchor":"tuning-sensitivity","text":"Each engine exposes its own settings (such as area, threshold, and blur) in its plugin settings, with a reset to defaults. Two settings live on the camera itself, in Settings → Detection:\n\n- Motion resolution. Low is fastest, medium is balanced, high is most accurate.\n- Motion timeout. How long motion stays \"active\" after movement stops (minimum 10 seconds).\n\nIf a camera is too sensitive (a busy road, a swaying tree), limit where motion counts with zones.\n\nA camera's on-screen timestamp can trigger motion too, mostly at night and at higher motion resolutions. Motion events with nothing in frame are the sign: draw a motion zone that leaves out the timestamp corner."},{"page":"detection/motion","title":"Motion detection","heading":"The cascade","anchor":"the-cascade","text":"When motion fires, it triggers the heavier detectors (object, face, and so on). You can also let other sensors start this cascade, for example a door contact sensor triggering object detection, under Sensor triggers in the camera's detection settings."},{"page":"detection/semantic-search","title":"Semantic search","heading":"Semantic search","anchor":"","text":"Semantic search lets you find moments in your recordings by describing them in plain words, like \"person with a red jacket\" or \"delivery truck\", instead of filtering by type. It is powered by CLIP."},{"page":"detection/semantic-search","title":"Semantic search","heading":"What you need","anchor":"what-you-need","text":"Semantic search needs an AI backend that provides CLIP (CoreML, OpenVINO, or ONNX) enabled on your cameras. As those cameras record, camera.ui makes every person, vehicle and animal it detects searchable. Events without one of those, like plain motion, won't show up in the results.[^license]"},{"page":"detection/semantic-search","title":"Semantic search","heading":"Searching","anchor":"searching","text":"In Recordings, open AI Search and describe what you're looking for. camera.ui ranks events by how well they match, and you can raise Min. Match Score to keep only close matches. The other filters in the sidebar narrow the matches too, and scrolling down loads more of them.\n\nIf the count keeps its + at the end of the list, more pictures fit the description than one search reads, and matches in the chosen cameras or time range can be missing. Raise Min. Match Score until the + goes away.\n\nThe percentage is scaled per CLIP model, so a Min. Match Score means the same after a model change."},{"page":"detection/semantic-search","title":"Semantic search","heading":"Changing the CLIP model","anchor":"changing-the-clip-model","text":"A backend's plugin settings offer more than one CLIP model. Embeddings are only comparable within the model that produced them, so a search only looks at events embedded with a model your enabled CLIP plugins still serve. Older events stay on disk but drop out of the results until they are re-embedded.\n\nReindex search, bottom left in Recordings and admins only, does that. It picks the events whose embeddings came from a model none of your plugins serves any more and works through them in the background. Search keeps working while it runs, and you can leave the page. Stop ends it after the current event, and starting it again continues where it stopped. Events whose pictures are gone cannot be re-embedded and are counted as failed.[^reindex]\n\n[^license]: Semantic search builds on recording, which requires an active camera.ui subscription.\n\n[^reindex]: Without a CLIP plugin the button does not appear."},{"page":"detection/training","title":"Model training","heading":"Model training","anchor":"","text":"camera.ui's detection models are trained on real footage from real installations, contributed by the community. The Training view collects sample frames from your cameras, you label and verify them, and you decide which ones go to the shared training pool. Nothing is uploaded on its own."},{"page":"detection/training","title":"Model training","heading":"How frames are collected","anchor":"how-frames-are-collected","text":"During detection events each camera saves a frame when something new shows up: a new object, a changed position, a fresh face or license plate. An unchanged scene doesn't repeat, however long the event runs. Of the few seconds around such a moment it keeps one frame and prefers objects the detector was unsure about, small ones and animals over large, obvious ones; about one time in ten, picked at random, it keeps the first frame instead, so ordinary scenes stay in the mix. camera.ui keeps at most 200 frames per camera: once full, the oldest unreviewed frames make room, and unreviewed frames are deleted after 14 days either way. Verified frames stay until you submit or delete them.\n\nA privacy zone is blacked out of the frame before it is saved, so nothing behind one is ever collected or submitted.\n\nCollection is on by default and stays on your disk. To stop it, open the gear menu in the Training view and turn off Collect candidates. The view is only visible to admins."},{"page":"detection/training","title":"Model training","heading":"Labeling frames","anchor":"labeling-frames","text":"Opening a frame starts the editor. Drag to draw a box, tap a box or its label to change it, double tap to zoom. On a keyboard, the question mark button in the editor lists the shortcuts.\n\nShow boxes of this camera's previous image (V) outlines the boxes you last had on a frame of the same camera, the plus on one takes it over, Shift + V takes all. Hide boxes (H) hides every box and outline so the picture underneath shows.\n\nDetections below the camera's detection threshold show as dotted outlines with their score. The plus on one labels it; leave the ones that show nothing.\n\nWhat makes a frame useful:\n\n- Label everything you see. Every person, vehicle, animal and package in the frame, not just what triggered the event, parked cars and vehicles cut off at the edge of the picture included. An unlabeled object teaches the model that it isn't one. The boxes camera.ui drew for you include objects outside your detection zones and labels you never turned on; they belong in the data, leave them in.\n- Pictures of people are not people. A person on a poster, a screen or a photo gets no box. Remove the one camera.ui drew there.\n- Keep boxes tight. The box hugs the object, without a margin.\n- Include hidden parts. Box a partly hidden object in full, as far as you can judge its extent.\n- Faces and license plates get their own boxes, on top of the person or vehicle box.\n- Type the plate text. With a license plate box selected, a text field takes the plate as printed. Optional, and the ground truth for plate reading.\n- False alarms are valuable. If the frame shows none of the objects, delete all boxes and verify it empty. That is exactly how the model unlearns a false trigger.\n\nVerify marks the frame as done. Only verified frames can be submitted."},{"page":"detection/training","title":"Model training","heading":"Submitting to the pool","anchor":"submitting-to-the-pool","text":"The submit button at the bottom right sends every verified frame of the selected camera, whatever the list filter shows. To send only some, use Select candidates. Submitted frames leave the local list. Submitting requires your server to be signed in to camera.ui Cloud.\n\nMy contributions in the toolbar lists everything you have submitted, with the frame, its labels, and whether it is still waiting (Submitted) or already used (In training). Images and labels are tied to your cameraui.com account; deleting a contribution removes it from the pool."},{"page":"home-assistant/app","title":"Home Assistant app","heading":"Home Assistant app","anchor":"","text":"You can run the full camera.ui server inside Home Assistant OS as an app. There are two apps in the repository: camera.ui, the full server with its own panel in the Home Assistant sidebar, and camera.ui Worker, a helper that lends this machine's compute to a camera.ui server running elsewhere. Both are supported on amd64 and aarch64 hosts."},{"page":"home-assistant/app","title":"Home Assistant app","heading":"Install","anchor":"install","text":"1. In Home Assistant, open Settings → Apps → App Store.\n2. From the store's menu (top right), choose Repositories and add https://github.com/cameraui/homeassistant-apps.\n3. Find camera.ui in the store and click Install."},{"page":"home-assistant/app","title":"Home Assistant app","heading":"The camera.ui app","anchor":"the-cameraui-app","text":"The camera.ui app runs the complete server: the UI, the streaming engine, the NVR, and any plugins you install. It runs on the host network, which it needs for WebRTC live view, HomeKit pairing, and camera discovery.\n\nOnce started, the app adds a camera.ui entry to the Home Assistant sidebar. That panel, and the app's Open Web UI button, both open the interface through Home Assistant's ingress proxy, so there is no separate login and no certificate warning.[^direct] The panel signs you in according to your Home Assistant user:\n\n- Admins. Home Assistant admins are signed in as the homeassistant user, a master account. That user can't be renamed.\n- Other users. Anyone else who opens the panel gets a camera.ui account of their own with the user role, named after their Home Assistant username with ha- in front, ha-anna for example. When Home Assistant removes or disables that user or makes it an admin, camera.ui signs the account out everywhere the next time someone uses the panel.\n\nSign-out and password are not offered in the panel, the username can't be changed there, and API tokens are offered only to the master.\n\nOn first start the app downloads the camera.ui server and the default plugin, which needs an internet connection and takes a few minutes.[^firstboot] Follow the first boot in the app's Log tab.\n\n[^direct]: The app also listens on https://:3443 directly over the host network. Reaching it that way shows a self-signed certificate warning once.\n\n[^firstboot]: If first boot fails to fetch the server, check that the host can reach the internet and resolve DNS."},{"page":"home-assistant/app","title":"Home Assistant app","heading":"Options","anchor":"options","text":"- default_plugins. Comma-separated plugins installed on first boot. Defaults to @camera.ui/camera-ui-nvr, the NVR plugin.\n- recordings_path. Where the NVR plugin writes recordings. Defaults to /media/camera.ui. Paths under /media show up in the Home Assistant media browser.\n- internal_avahi. Runs the app's own mDNS stack for HomeKit pairing and camera discovery. On by default. Turn it off to use the host's mDNS instead."},{"page":"home-assistant/app","title":"Home Assistant app","heading":"Hardware acceleration","anchor":"hardware-acceleration","text":"The app passes GPU, AI accelerator, and codec devices through to the container.\n\n- Intel / AMD (VA-API). VA-API drivers ship on amd64 only, covering Intel Quick Sync and AMD Mesa VA-API decoding. aarch64 hosts do not get VA-API.\n- Coral Edge TPU. The Coral runtime is included for both architectures. USB Coral works through the app's USB access; PCIe Coral uses the passed-through /dev/apex_* devices.\n- Hailo. The Hailo device (/dev/hailo0) is passed through, but the Hailo runtime is not part of the image.[^hailo]\n- NVIDIA. Not available in the Home Assistant app. Use Docker on a plain Linux host for an NVIDIA setup.\n\nThe Hardware acceleration page explains how the streaming engine picks a device and how to check that it works.\n\n[^hailo]: Install the Hailo plugin to add the runtime. The app image itself only passes the device through."},{"page":"home-assistant/app","title":"Home Assistant app","heading":"Recordings and the media browser","anchor":"recordings-and-the-media-browser","text":"The /media directory is mapped into the app with read-write access. With the default recordings_path of /media/camera.ui, recordings written by the NVR appear in Home Assistant's Media browser."},{"page":"home-assistant/app","title":"Home Assistant app","heading":"Backups and updates","anchor":"backups-and-updates","text":"Home Assistant backs up the app's /data directory, which holds your configuration, database, and TLS certificates. Three subtrees are excluded from backups: the downloaded server, the Python environment, and installed plugins. These are re-downloaded on restore, so restoring a backup needs an internet connection, the same as a first start. camera.ui's own scheduled backups in their default folder and unfinished restore uploads are left out as well.\n\nThe server updates itself from within the app, so day-to-day updates do not need a new app image. Update the app itself when a new version is published to refresh the base image (OS, drivers, and launcher).\n\nLeave the supervisor's Watchdog toggle on for the app so Home Assistant restarts it if it stops unexpectedly.[^watchdog]\n\n[^watchdog]: Watchdog is a Home Assistant setting on the app's info page, not a camera.ui option."},{"page":"home-assistant/app","title":"Home Assistant app","heading":"Worker app","anchor":"worker-app","text":"The camera.ui Worker app turns this Home Assistant machine into a worker for a camera.ui server (the master) running somewhere else. It lends this machine's CPU, GPU, and Coral to the master for decoding, detection, and plugins. It has no UI and no sidebar panel of its own and connects out to the master. It passes through the same hardware as the full app.\n\nSet it up on the master first, then here:\n\n1. On the master, enable workers and set the master address, then generate a one-time pairing code. Generating a code and pairing both fail unless workers are enabled there. See Workers.\n2. Install the camera.ui Worker app on this machine.\n3. Set master_address to the master's IP or hostname and pairing_code to the code you just generated.\n4. Start the app. It exchanges the code for its own credentials on first connect and reconnects on its own afterwards, so the code is entered only once.\n\n- master_address. IP or hostname of the camera.ui master. Required.\n- pairing_code. The one-time code generated on the master. Required for the first connection. A code is valid for 15 minutes and can be used once. It may stay set after pairing; once the worker was removed on the master, enter a new code and restart the app.\n- worker_name. Name shown for this worker on the master. Optional. Without it, the worker uses the app's host name.\n- master_api_port. The master's HTTPS API port. Optional. Defaults to 3443.\n- internal_avahi. Runs the app's own mDNS stack. Off by default, the worker finds the master without it. Two avahi daemons on one machine, next to the camera.ui app for example, keep changing their host names. Turn it on only if a plugin on this worker needs avahi."},{"page":"home-assistant/app","title":"Home Assistant app","heading":"Next steps","anchor":"next-steps","text":"- Hardware acceleration — how the streaming engine uses your GPU and accelerators, and how to verify it.\n- Workers — masters, workers and pairing.\n- Worker assignments — which cameras and plugins run on a worker, and failover.\n- Docker — run the server outside Home Assistant, including NVIDIA setups."},{"page":"home-assistant/card","title":"Dashboard cards","heading":"Dashboard cards","anchor":"","text":"Three cards put camera.ui on a Home Assistant dashboard: a camera, a saved camview view, and a strip of recent events. A click opens the camera.ui dialog with the live picture and the timeline, inside Home Assistant.\n\nThe cards are served by your camera.ui server, so they always match the version you run. Home Assistant keeps a copy, so a dashboard still renders while the server is down."},{"page":"home-assistant/card","title":"Dashboard cards","heading":"Before you start","anchor":"before-you-start","text":"Set up the integration first. It registers the cards for you, so they appear in the card picker with no resource to add by hand.\n\nWho may use them is an integration option: Administrators only by default, or all Home Assistant users. See card access."},{"page":"home-assistant/card","title":"Dashboard cards","heading":"camera.ui Camera","anchor":"cameraui-camera","text":"One camera, as the snapshot tile you know from the home view or as the full live player.\n\ntype: custom:cameraui-card\nentity: camera.front_door\nmode: snapshot\nclick: popup\n\n- entity. Required. A camera.ui camera entity.\n- mode. snapshot (default) shows the tile with its refreshing picture, live runs the player in the camera's activity mode.\n- click. What a click does: popup (default) opens the camera.ui dialog, ha jumps to the camera in the sidebar panel, none does nothing.\n- title. Overrides the name shown on the card.\n- fit. contain (default) shows the whole picture, cover fills the card and crops.\n- controls and toolbar. Turn the player controls or the toolbar above them off.\n- detection_indicator. Turn off the marker that lights up while the camera detects something."},{"page":"home-assistant/card","title":"Dashboard cards","heading":"camera.ui View","anchor":"cameraui-view","text":"A camview view as a widget: the cameras you arranged there, in the same layout.\n\ntype: custom:cameraui-view-card\nview: Garden\n\n- view. The name of the view to show. Without it the card asks you to pick one.\n- rearrange. Set to false to hide the rearrange button, so the layout cannot be changed from the dashboard.\n- activity_mode. per-camera (default) runs each camera in its own activity mode. always-on, activity or standby sets one mode for the whole view."},{"page":"home-assistant/card","title":"Dashboard cards","heading":"camera.ui Events","anchor":"cameraui-events","text":"The recent events strip, with thumbnails. A click opens the event.\n\ntype: custom:cameraui-events-card\nentities:\n - camera.front_door\n - camera.driveway\n\n- entities. Limits the strip to these cameras. Left out, it shows every camera."},{"page":"home-assistant/card","title":"Dashboard cards","heading":"Next steps","anchor":"next-steps","text":"- Home Assistant integration — entities, automations, card access and the media browser.\n- Camview — build the views the view card shows."},{"page":"home-assistant/import","title":"Import from Home Assistant","heading":"Import from Home Assistant","anchor":"","text":"The Home Assistant plugin brings Home Assistant's own sensors and controls into camera.ui. It is the reverse of the integration: Home Assistant devices come into camera.ui, where you can assign them to cameras and use them as detection triggers."},{"page":"home-assistant/import","title":"Import from Home Assistant","heading":"Setup","anchor":"setup","text":"Install and enable the Home Assistant plugin from the Plugins page, then open its settings:\n\n- Home Assistant URL. The address of your Home Assistant, for example http://homeassistant.local:8123.\n- Access Token. A long-lived access token from your Home Assistant profile, under Security.\n\nRunning camera.ui as the Home Assistant app? Leave both empty. The plugin connects to Home Assistant on its own, no token needed."},{"page":"home-assistant/import","title":"Import from Home Assistant","heading":"What you can import","anchor":"what-you-can-import","text":"The plugin offers every Home Assistant entity it can map, under Discovered on the Sensors page, with its name, entity id, type and room. Nothing comes over until you pick it.\n\n- Sensors. Motion, occupancy, contact, doorbell, smoke, leak, gas, carbon monoxide, and other supported types become camera.ui sensors.\n- Controls. Locks, garage doors, alarm panels, switches, lights, and sirens come in as controls. Operating one in camera.ui operates it in Home Assistant.\n\nUnsupported entity types never show up. To keep specific entities out of the offer as well, list them under Excluded Entities."},{"page":"home-assistant/import","title":"Import from Home Assistant","heading":"Using imported sensors","anchor":"using-imported-sensors","text":"Adopted sensors show up in the Sensors view like any other. Assigned to a camera, one works as a detection trigger like a native sensor: a Home Assistant motion detector or door contact can start a camera event. Imported controls appear in the camera's Overview and in automations. On the Sensors page each imported sensor shows its Home Assistant entity id as its ID, to tell apart entities with the same friendly name and to search by. The entity id is only a label: camera.ui identifies the entity by its registry id, so renaming it in Home Assistant keeps the sensor and its assignments. If you delete the entity in Home Assistant, the sensor stays and is marked as removed until you delete it here too; if Home Assistant is down, it just shows as unavailable.\n\nAdopted entities are remembered by camera.ui, not by the plugin, so they survive restarts of both."},{"page":"home-assistant/import","title":"Import from Home Assistant","heading":"Running the integration too","anchor":"running-the-integration-too","text":"You can import from Home Assistant and run the integration at the same time. The plugin never re-imports the cameras and sensors that camera.ui itself exports to Home Assistant.\n\nImported sensors are never sent back. A sensor pulled in from Home Assistant is never exported to Home Assistant again, not by this plugin and not by the integration, even if you export it to another bridge like HomeKit."},{"page":"home-assistant/","title":"Home Assistant","heading":"Home Assistant","anchor":"","text":"camera.ui and Home Assistant meet in two independent ways: the app runs camera.ui inside Home Assistant, the integration connects a camera.ui server to Home Assistant and surfaces its cameras and sensors as HA entities. Use either or both."},{"page":"home-assistant/","title":"Home Assistant","heading":"Running vs connecting","anchor":"running-vs-connecting","text":"- The app runs camera.ui. It installs the camera.ui server as a Home Assistant OS app, so your cameras, recordings, and detection live on the same box as HA.\n- The integration connects camera.ui. It talks to a camera.ui server (the app, or a Docker/desktop install anywhere on your network) and creates one HA device per camera with live streams, sensors, and PTZ.\n\nThe app alone creates no entities: add the integration as well. If camera.ui already runs elsewhere, you only need the integration."},{"page":"home-assistant/","title":"Home Assistant","heading":"Getting entities in: pick one transport","anchor":"getting-entities-in-pick-one-transport","text":"Two independent transports bring camera.ui cameras and sensors into Home Assistant:\n\n1. The integration (recommended). A custom component that pushes entities to HA over a local connection. No broker needed.\n2. MQTT discovery. An opt-in on the camera.ui server's MQTT bridge that publishes Home Assistant discovery configs to your broker.\n\nDo not run both\nThe two do not merge: running both leaves you with two copies of every camera, device and sensor. Use the integration unless you specifically want to route camera.ui through an existing MQTT broker."},{"page":"home-assistant/","title":"Home Assistant","heading":"The integration","anchor":"the-integration","text":"Live camera changes arrive by push, and it re-checks the camera list once a minute as a backstop.\n\nYou get, per camera:\n\n- A live camera entity with streaming. Snapshots and the live source (high, mid, or low RTSP) come straight from camera.ui.\n- Motion and object-detection sensors. Object sensors cover the labels person, vehicle, animal, and package.\n- Sensors across the binary_sensor, sensor, switch, light, siren, lock, cover, and alarm_control_panel domains. Switches, lights, sirens, locks, covers, and alarm panels are controllable from Home Assistant; contact and measurement sensors are read-only.\n- A PTZ service, cameraui.ptz, for continuous, stop, move, absolute, preset, and home actions.\n\nOn top of that, the integration registers its dashboard cards, embeds the full camera.ui web UI as a sidebar panel, and fires a cameraui_event on the HA event bus for every detection, with device triggers wired up for the automation editor. Setup can start from zeroconf: camera.ui advertises itself over mDNS, so host and port are pre-filled and you only enter an access token.\n\nSee Integration for the full setup."},{"page":"home-assistant/","title":"Home Assistant","heading":"MQTT discovery","anchor":"mqtt-discovery","text":"For setups that already run an MQTT broker. It needs no custom component and no HACS: the camera.ui server publishes retained discovery configs, and Home Assistant's built-in MQTT integration picks them up.\n\nYou turn it on in the camera.ui server's MQTT settings (enable HA discovery). You get status, motion, and object binary sensors, a snapshot camera image, and the same controllable sensor domains as the integration. What you do not get: a live stream (snapshot only), the PTZ service, the sidebar panel, the auto-registered cards, or the cameraui_event bus events. In return it adds a dedicated per-camera connectivity sensor.\n\nMQTT discovery is documented with the rest of the broker settings on the MQTT admin page."},{"page":"home-assistant/","title":"Home Assistant","heading":"Integration vs MQTT discovery","anchor":"integration-vs-mqtt-discovery","text":"| | Integration | MQTT discovery |\n|---|---|---|\n| Needs an MQTT broker | No | Yes |\n| Custom component / HACS | Required | Not needed |\n| Live camera stream | Yes | Snapshot only |\n| Motion & object sensors | Yes | Yes |\n| Controllable sensors | Yes | Yes |\n| PTZ service | Yes | No |\n| Per-camera connectivity sensor | No separate one | Yes |\n| Embedded web UI panel | Yes | No |\n| Dashboard cards auto-registered | Yes | No |\n| cameraui_event + device triggers | Yes | No |\n| Discovery | zeroconf (mDNS) | over MQTT |"},{"page":"home-assistant/","title":"Home Assistant","heading":"Bringing Home Assistant sensors in","anchor":"bringing-home-assistant-sensors-in","text":"The integration and MQTT both send camera.ui out to Home Assistant. The Home Assistant plugin goes the other way: it imports Home Assistant's own sensors and controls into camera.ui, where you can assign them to cameras and use them as detection triggers. It does not clash with either transport.\n\nSee Import from Home Assistant for setup."},{"page":"home-assistant/","title":"Home Assistant","heading":"The dashboard cards","anchor":"the-dashboard-cards","text":"Three cards put a camera, a camview view or the recent events on your dashboards, and a click opens the camera.ui dialog with the timeline. The integration registers them.\n\nSee Dashboard cards for the options."},{"page":"home-assistant/","title":"Home Assistant","heading":"Next steps","anchor":"next-steps","text":"- App — run the camera.ui server inside Home Assistant OS.\n- Integration — connect a camera.ui server and get cameras, sensors, and PTZ as HA entities.\n- Dashboard cards — put cameras, a view or recent events on your dashboard.\n- Import from Home Assistant — bring Home Assistant's own sensors and controls into camera.ui."},{"page":"home-assistant/integration","title":"Home Assistant integration","heading":"Home Assistant integration","anchor":"","text":"The camera.ui integration connects an existing camera.ui server to Home Assistant. Your cameras, their sensors, motion and object detection, PTZ controls and the full camera.ui interface all show up inside Home Assistant.\n\nIt talks to the server directly over your local network, and events arrive by push. No MQTT broker or cloud service is involved. The integration's domain is cameraui. To run camera.ui itself inside Home Assistant OS, see the App page."},{"page":"home-assistant/integration","title":"Home Assistant integration","heading":"Install","anchor":"install","text":"The integration is distributed through HACS as a custom repository and needs Home Assistant 2026.3 or newer.\n\n1. In HACS, add cameraui/homeassistant-integration as a custom Integration repository.\n2. Install camera.ui from HACS.\n3. Restart Home Assistant."},{"page":"home-assistant/integration","title":"Home Assistant integration","heading":"Discovery","anchor":"discovery","text":"camera.ui advertises itself on the local network over mDNS (_camera-ui._tcp.local.). When Home Assistant sees it, a camera.ui found card appears under Settings → Devices & services. Open it and enter your API token; the host and port are filled in from discovery."},{"page":"home-assistant/integration","title":"Home Assistant integration","heading":"Manual setup","anchor":"manual-setup","text":"If discovery doesn't reach the server (a different subnet, for example), add it by hand:\n\n1. Go to Settings → Devices & services → Add integration and search for camera.ui.\n2. Enter the Host, Port (default 3443) and your API token."},{"page":"home-assistant/integration","title":"Home Assistant integration","heading":"API token","anchor":"api-token","text":"Both paths need an API token. Create one in camera.ui under Settings → Account → API tokens.\n\nUse a token from an admin account. Controlling camera.ui sensors from Home Assistant (locks, covers, lights, sirens, alarm panels, PTZ) needs admin rights; with a viewer token the entities show up but their controls are rejected.\n\nSetup checks the token against the server: a rejected token gives an authentication error, an unreachable server a connection error.[^ssl]\n\n[^ssl]: The server is reached over HTTPS. The integration does not verify the certificate, so a self-signed camera.ui certificate works without extra setup."},{"page":"home-assistant/integration","title":"Home Assistant integration","heading":"What you get","anchor":"what-you-get","text":"The integration creates one Home Assistant device per camera and keeps the list in sync. It re-reads the camera list every 60 seconds, and a camera you remove in camera.ui is removed from Home Assistant automatically."},{"page":"home-assistant/integration","title":"Home Assistant integration","heading":"Cameras","anchor":"cameras","text":"Each camera device carries a camera entity that streams live and serves snapshots. The device is labeled with the manufacturer camera.ui and links back to the server's web interface."},{"page":"home-assistant/integration","title":"Home Assistant integration","heading":"Sensors","anchor":"sensors","text":"Every exposed sensor in camera.ui is mirrored into Home Assistant on the matching platform, whether it belongs to a camera or not:\n\n| camera.ui sensor | Home Assistant platform |\n|---|---|\n| Binary / contact | binary_sensor |\n| Measurement | sensor |\n| Switch | switch |\n| Light | light |\n| Siren | siren |\n| Lock | lock |\n| Cover | cover |\n| Alarm system | alarm_control_panel |\n\nCamera hardware (a camera's own spotlight, siren or battery) lands on the camera's device. Every other sensor becomes a device of its own, linked below its camera when it is assigned to exactly one. Whether a sensor comes across at all is the Expose sensor toggle on the Sensors page. Camera hardware has no toggle, it always follows its camera.\n\nThe controllable ones work both ways: a switch and a siren turn on and off, a light turns on and off (and dims, if it reports brightness), a cover opens and closes, a lock locks and unlocks, and an alarm panel arms home, away or night and disarms.\n\nAdding, renaming, re-assigning, exposing or deleting a sensor in camera.ui updates the matching Home Assistant entity live, without a restart. A sensor whose plugin is offline shows as unavailable."},{"page":"home-assistant/integration","title":"Home Assistant integration","heading":"Motion and object detection","anchor":"motion-and-object-detection","text":"Every camera gets a motion binary sensor that turns on while a detection is active.\n\nCameras with object detection also get one binary sensor each for person, vehicle, animal and package. Each turns on while that object is in view and clears when the detection ends.[^objectclass]\n\nIf the connection to camera.ui drops, the entities become unavailable. Once it is back, a detection that is still running carries on without a second start. One that ended in the meantime ends then: motion and object sensors turn off, recognition sensors clear and cameraui_event fires end.\n\n[^objectclass]: Motion and object binary sensors are registered with the motion device class, so they present in Home Assistant as motion sensors rather than as a per-object class."},{"page":"home-assistant/integration","title":"Home Assistant integration","heading":"Recognition sensors","anchor":"recognition-sensors","text":"Cameras with the matching detector get value sensors for face, license plate and classification. The sensor's value is the recognized labels, and the raw detections are available in its attributes."},{"page":"home-assistant/integration","title":"Home Assistant integration","heading":"Updates","anchor":"updates","text":"Home Assistant also gets update entities for the camera.ui server and its plugins. Available updates show up in Home Assistant's updates list and install from there. The update dialog offers to create a backup first, which writes a camera.ui backup and stops the update if that fails. A server update that a worker would not survive stops with the worker's name: update that worker first, or start the update on camera.ui's Updates page and confirm it there (Updating). Turn Allow updates from Home Assistant off in the integration options to keep the updates visible without an install button."},{"page":"home-assistant/integration","title":"Home Assistant integration","heading":"PTZ","anchor":"ptz","text":"Pan, tilt and zoom is exposed as a service, cameraui.ptz, rather than as an entity. Target one or more camera entities and pick an action:\n\n- continuous. Move at a set speed until stopped, using pan, tilt and zoom as speeds.\n- stop. Stop a continuous move.\n- move. Move by a relative step.\n- absolute. Go to an absolute position.\n- home. Return to the home position.\n- preset. Recall a saved preset by name.\n\nThe pan, tilt and zoom fields run from -1 to 1. The preset action needs a preset name. If none of the targeted cameras has PTZ, the service reports an error.\n\naction:\n - service: cameraui.ptz\n target:\n entity_id: camera.driveway\n data:\n action: preset\n preset: gate"},{"page":"home-assistant/integration","title":"Home Assistant integration","heading":"Device triggers","anchor":"device-triggers","text":"Each camera device offers three triggers in the automation editor:\n\n- Detection started. A detection began on the camera.\n- Detection ended. The detection cleared.\n- Object recognized. A face, plate or classification was recognized during a detection."},{"page":"home-assistant/integration","title":"Home Assistant integration","heading":"The event bus","anchor":"the-event-bus","text":"The integration also fires a cameraui_event on the Home Assistant event bus, several times across one detection's lifecycle:\n\n| state | When |\n|---|---|\n| start | A detection begins. |\n| object | A new object label appears during the detection. |\n| recognized | A face, plate or classification is recognized during the detection. |\n| end | The detection clears. |\n\nEach event carries these fields:\n\n| Field | Contents |\n|---|---|\n| camera_id | The camera's id. |\n| camera_name | The camera's name. |\n| state | One of the values above. |\n| detection_types | The detection types involved. |\n| event_id | The id shared by all events of one detection. |\n| labels | Object labels seen. |\n| faces | Recognized faces. |\n| plates | Recognized license plates. |\n| classifications | Other recognized attributes. |\n| attributes | The full raw attribute list. |\n\nFilter on state to act on a specific point in the lifecycle:\n\nautomation:\n - alias: Notify on recognized face\n trigger:\n - platform: event\n event_type: cameraui_event\n event_data:\n state: recognized\n action:\n - service: notify.mobile_app\n data:\n message: >-\n {{ trigger.event.data.faces | join(', ') }}\n seen at {{ trigger.event.data.camera_name }}\n\nautomation:\n - alias: Turn on light when a person appears\n trigger:\n - platform: event\n event_type: cameraui_event\n event_data:\n state: object\n condition:\n - \"{{ 'person' in trigger.event.data.labels }}\"\n action:\n - service: light.turn_on\n target:\n entity_id: light.driveway\n\nThere is no device trigger for the object state, so use the event bus for that one.[^objecttrigger]\n\n[^objecttrigger]: The three device triggers map to start, end and recognized. To react to a specific object label appearing mid-detection, listen for cameraui_event with state: object as shown above."},{"page":"home-assistant/integration","title":"Home Assistant integration","heading":"Sidebar panel","anchor":"sidebar-panel","text":"Setup adds a camera.ui entry to the Home Assistant sidebar that embeds the full camera.ui interface. The panel is admin only: it appears for administrator accounts and stays hidden for everyone else."},{"page":"home-assistant/integration","title":"Home Assistant integration","heading":"Dashboard cards","anchor":"dashboard-cards","text":"Three cards come with the integration and register themselves: a camera, a camview view, and a strip of recent events. See Dashboard cards for their options."},{"page":"home-assistant/integration","title":"Home Assistant integration","heading":"Card access","anchor":"card-access","text":"The integration's options decide who may use them: Administrators only, the default, or All Home Assistant users. The cards reach camera.ui through Home Assistant using the integration's own token, so opening them up hands every Home Assistant user what that token can see. Viewer token takes a second camera.ui token that is used for everyone who is not a Home Assistant administrator, which is how you point them at a restricted camera.ui account instead.\n\nAll dashboards that use one token share its connection limit. With many wall displays, a Viewer token gives the non-administrators a limit of their own."},{"page":"home-assistant/integration","title":"Home Assistant integration","heading":"Recordings in the media browser","anchor":"recordings-in-the-media-browser","text":"Events that have a recording appear in Home Assistant's Media browser under camera.ui: first the cameras, plus an All cameras entry, then the days, then the events themselves, each with a thumbnail and named by its time and what was detected. A click plays the clip, and it starts while the export is still running. Any entry can be sent to a TV or a speaker with media_player.play_media.\n\nClip quality in the options picks what those clips use: the sub stream, small and quick, or the main stream at full quality."},{"page":"home-assistant/integration","title":"Home Assistant integration","heading":"Next steps","anchor":"next-steps","text":"- App — run camera.ui itself as a Home Assistant OS app.\n- Dashboard cards — configure the cards on a dashboard.\n- Sensors — assign sensors to a camera so they reach Home Assistant."},{"page":"","title":"What is camera.ui?","heading":"What is camera.ui?","anchor":"","text":"camera.ui is a self-hosted platform for your security cameras. It combines live viewing, continuous and event-based recording, on-device AI detection, and smart-home integration in one interface, all on hardware you own.\n\nThere is no mandatory cloud, and no third party holds a copy of your recordings. Remote access is optional.\n\nNew here?\nThe Quick start takes you from install to recording in seven steps. For the first run in detail see Getting started.\n::: info Work in progress\nIf something is missing or unclear, tell us on Discord or open an issue on GitHub."},{"page":"","title":"What is camera.ui?","heading":"What you can do","anchor":"what-you-can-do","text":"- Watch live. Low-latency streaming over WebRTC and MSE, multi-camera grids, two-way talk, PTZ control, and snapshots. A floor plan of your home shows which camera is seeing something right now.\n- Record around the clock.[^license] Continuous, event-based, or ad-hoc recording, with a scrubbable timeline, synchronized multi-camera playback, and MP4 / timelapse export.\n- Detect what matters. Motion, object, face, license-plate, and audio detection, all running locally on your own hardware, plus semantic search and AI-generated event descriptions.\n- Stay informed.[^license] Per-camera push notifications, alerts limited by area, object type or the people you name, quiet hours, and critical alerts that always get through.\n- Connect your cameras. Add cameras over ONVIF and dozens of other protocols, and bridge them into your smart home via Apple HomeKit or Home Assistant (over MQTT)[^smarthome].\n- Extend it. A plugin system lets you add cameras, detectors, and integrations from an in-app store."},{"page":"","title":"What is camera.ui?","heading":"Live demo","anchor":"live-demo","text":"Try the live demo before installing."},{"page":"","title":"What is camera.ui?","heading":"Run it your way","anchor":"run-it-your-way","text":"- Desktop app for macOS, Windows, and Linux. The easiest way to get started, running the server and viewer in one app.\n- Server for always-on setups: Docker, Proxmox, or bare-metal Linux.\n- Mobile apps for iOS and Android.\n\nNot sure which to pick? See Which installation?."},{"page":"","title":"What is camera.ui?","heading":"How it works","anchor":"how-it-works","text":"One server connects to your cameras, records, runs detection and serves the web interface. The desktop and mobile apps are viewers. More in How it works."},{"page":"","title":"What is camera.ui?","heading":"Next steps","anchor":"next-steps","text":"- Getting started — set up camera.ui for the first time\n- Add a camera — connect your first camera\n- Recording (NVR) — configure 24/7 and event recording\n- Detection & AI — set up motion and object detection\n\n[^license]: An active camera.ui subscription covers recording (NVR) and the features built on it, such as playback, export, face recognition, semantic search, and AI descriptions, plus push notifications. Live view and real-time detection are free.\n[^smarthome]: Google Home and Alexa support is planned."},{"page":"install/desktop","title":"Desktop app","heading":"Desktop app","anchor":"","text":"The desktop app is the recommended way to run camera.ui on macOS and Windows. One download installs server and viewer together. It also runs on Linux, though there a Docker or Proxmox server is the better choice (see Which installation?)."},{"page":"install/desktop","title":"Desktop app","heading":"Download","anchor":"download","text":"Installers are below and on the download page. The links always point to the latest release.\n\n| Platform | Download |\n|---|---|\n| macOS (Apple Silicon) | camera.ui-arm64.dmg |\n| macOS (Intel) | camera.ui-x64.dmg |\n| Windows 10 / 11 | camera.ui-x64.exe |\n| Linux (AppImage) | x64 · arm64 |\n| Linux (.deb) | amd64 · arm64 |"},{"page":"install/desktop","title":"Desktop app","heading":"macOS","anchor":"macos","text":"Drag camera.ui from the .dmg into Applications. The app is signed and notarized, so it opens without warnings."},{"page":"install/desktop","title":"Desktop app","heading":"Windows","anchor":"windows","text":"The .exe installs for the current user, no administrator rights needed.\n\nThe app is not code-signed yet, so SmartScreen may warn. Select More info → Run anyway."},{"page":"install/desktop","title":"Desktop app","heading":"Linux","anchor":"linux","text":"Install the .deb:\n\nsudo apt install ./camera.ui-*.deb\n\nOr make the .AppImage executable and run it:\n\nchmod +x camera.ui-*.AppImage\n./camera.ui-*.AppImage\n\nPick the .AppImage if you want automatic updates\nOnly the .AppImage updates itself. A .deb install stays on its version until you install a newer .deb."},{"page":"install/desktop","title":"Desktop app","heading":"First launch: choose how to run","anchor":"first-launch-choose-how-to-run","text":"On first launch the app asks \"How should camera.ui run?\":\n\n- Server. Runs the camera.ui server on this machine. The all-in-one option.\n- Worker. Takes on part of the work for another camera.ui server on your network. Needs the main server's address and a pairing code from its Workers settings. See Worker setup.\n- Viewer · Direct. Connects to an existing server by address.\n- Viewer · Cloud. Signs in via cloud.cameraui.com to reach a server through camera.ui Cloud.\n\nServer mode continues with first-run setup. Worker mode has no window, only the tray icon and a status screen."},{"page":"install/desktop","title":"Desktop app","heading":"Starting without the picker","anchor":"starting-without-the-picker","text":"The mode can also come from the command line, then the picker is skipped:\n\n| Mode | Options |\n|---|---|\n| Server | --mode=runtime |\n| Worker | --mode=worker --master=192.168.1.10 --pairing-code=XXXX-XXXX, optionally --worker-name=nas |\n| Viewer · Direct | --mode=viewer-direct --server=192.168.1.10:3443 |\n| Viewer · Cloud | --mode=viewer-cloud |\n\nThe app stores the mode as if it had been picked. Started with a different --mode while it runs, it switches over, the same mode again changes nothing.[^macosargs] Values always follow an =. A missing or wrong value is noted in the log, and the app starts as if the option were not there."},{"page":"install/desktop","title":"Desktop app","heading":"The tray icon","anchor":"the-tray-icon","text":"The tray icon holds the app's own settings:\n\n- Open At Login. Starts camera.ui at login, straight to the tray without a window.[^autostart]\n- Close to tray. Off by default. When on, closing the window only hides it and a server on this machine keeps running.[^closetotray]\n- Switch mode…. Changes how the app runs, without reinstalling.\n- Quit. Stops the app, and in Server mode the server with it.\n\nOn Windows, left-click the tray icon to open the window, right-click for the menu.\n\nGNOME shows a tray icon only with the AppIndicator extension. Switch mode… also sits in the app menu under File, and starting the app with --switch-mode opens the mode picker: in the running instance if one is up, otherwise on that launch.[^macosargs] Both also work for a worker, which has no window of its own.\n\n[^autostart]: macOS and Windows only.\n[^closetotray]: Windows and Linux only. On macOS closing the window always only hides it, so the app and its desktop notifications keep running until you quit it.\n[^macosargs]: On macOS, start it with open -n -a camera.ui --args --mode=runtime. Without -n a running app only comes to the front and never sees the options."},{"page":"install/desktop","title":"Desktop app","heading":"macOS permissions","anchor":"macos-permissions","text":"On macOS 15 and newer, allow local network access on first launch, otherwise the app can't reach your cameras. Two-way talk asks for microphone access, and the geofence trigger's \"My location\" button in Automations for location access."},{"page":"install/desktop","title":"Desktop app","heading":"Updates","anchor":"updates","text":"The app checks for updates at startup and while it runs. It installs one only after a click, on its start screen or on the Updates page from any device. The bundled server updates with the app.\n\nOn Linux only the .AppImage updates itself; a .deb is updated from the download page."},{"page":"install/desktop","title":"Desktop app","heading":"Where your data lives","anchor":"where-your-data-lives","text":"In Server mode, settings and recordings live in the app's per-user data directory:\n\n- macOS: ~/Library/Application Support/camera.ui/camera.ui/volume\n- Windows: %APPDATA%\\camera.ui\\camera.ui\\volume\n- Linux: ~/.config/camera.ui/camera.ui/volume\n\nThe recording settings can move recordings to another disk, an external drive for example. Avoid a network share, see Storage & retention.\n\nSee Backup & restore to keep a copy."},{"page":"install/docker","title":"Docker","heading":"Docker","anchor":"","text":"Docker is the recommended way to run the camera.ui server on Linux. The image is built on Ubuntu 24.04 and downloads the server itself on first start, so server updates don't need a new image. There is one image per hardware target, published as ghcr.io/cameraui/camera.ui."},{"page":"install/docker","title":"Docker","heading":"Before you start","anchor":"before-you-start","text":"You need a Linux host with Docker Engine and the Compose plugin.\n\nDocker Desktop won't work\nDocker Desktop on macOS and Windows can't use host networking, which camera.ui relies on for mDNS and WebRTC. On those platforms, use the desktop app instead."},{"page":"install/docker","title":"Docker","heading":"Quick start","anchor":"quick-start","text":"Save this as docker-compose.yml:\n\nname: cameraui\n\nservices:\n cameraui:\n image: ghcr.io/cameraui/camera.ui:latest\n container_name: cameraui\n restart: unless-stopped\n stop_grace_period: 30s\n network_mode: host\n environment:\n - TZ=Europe/Berlin\n - CAMERAUI_DOCKER_AVAHI=true\n volumes:\n - cameraui-data:/data\n\nvolumes:\n cameraui-data:\n\nStart it:\n\ndocker compose up -d\n\nThen open https://:3443. The first boot downloads the server and takes a few minutes (docker compose logs -f shows progress). The certificate is self-signed, so the browser warns on first visit. Next: Getting started.\n\nFirst boot needs internet\nOn first start the container downloads the server from the npm registry. If your host can't resolve it, add public DNS resolvers (1.1.1.1, 8.8.8.8) to the service."},{"page":"install/docker","title":"Docker","heading":"Hardware acceleration","anchor":"hardware-acceleration","text":"The default image (latest) runs detection and video processing in software. For acceleration, layer the override of the flavor that matches your hardware on top of the base file.\n\n| Flavor | Tag | Hardware acceleration | Arch |\n|---|---|---|---|\n| CPU | latest | software | amd64 + arm64 |\n| Intel | intel | Quick Sync / VA-API + OpenCL | amd64 |\n| NVIDIA | nvidia | NVENC / NVDEC + CUDA 13 | amd64 |\n| NVIDIA (TensorRT) | nvidia-tensorrt | NVENC / NVDEC + CUDA 13 + TensorRT | amd64 |\n| NVIDIA (CUDA 12) | nvidia-cuda12 | NVENC / NVDEC + CUDA 12 | amd64 |\n| AMD | amd | Mesa VA-API + OpenCL | amd64 |\n\nSave the matching override next to your docker-compose.yml:\n\n-group\n\n [docker-compose.intel.yml]\nname: cameraui\nservices:\n cameraui:\n image: ghcr.io/cameraui/camera.ui:intel\n devices:\n - /dev/dri:/dev/dri\n\n [docker-compose.nvidia.yml]\nname: cameraui\nservices:\n cameraui:\n image: ghcr.io/cameraui/camera.ui:nvidia\n environment:\n - NVIDIA_VISIBLE_DEVICES=all\n - NVIDIA_DRIVER_CAPABILITIES=all\n deploy:\n resources:\n reservations:\n devices:\n - driver: nvidia\n count: all\n capabilities: [gpu, compute, video, utility]\n\n [docker-compose.amd.yml]\nname: cameraui\nservices:\n cameraui:\n image: ghcr.io/cameraui/camera.ui:amd\n devices:\n - /dev/dri:/dev/dri\n\nboth files together (Intel shown):\n\ndocker compose -f docker-compose.yml -f docker-compose.intel.yml up -d\n\nThe NVIDIA flavors also require the NVIDIA Container Toolkit on the host. nvidia ships CUDA 13 and needs an NVIDIA driver 580 or newer, which covers RTX 50xx cards. nvidia-cuda12 keeps CUDA 12 for older drivers and pairs with the ONNX Legacy plugin. nvidia-tensorrt adds the TensorRT runtime (about 2 GB) for the ONNX plugin's tensorrt provider. Ready-made compose files for every flavor are in the cameraui/docker repository.\n\nHost drivers, device passthrough for AI accelerators (Coral, Hailo, Intel NPU) and verification: Hardware acceleration."},{"page":"install/docker","title":"Docker","heading":"Networking","anchor":"networking","text":"Host networking (the compose default) is recommended: camera.ui needs it for mDNS / Bonjour (HomeKit pairing, ONVIF discovery) and WebRTC live view. Without it, publish the ports explicitly."},{"page":"install/docker","title":"Docker","heading":"Storage for recordings","anchor":"storage-for-recordings","text":"By default, recordings live in the /data volume. For a separate disk, bind-mount it and point the NVR at it:\n\n volumes:\n - cameraui-data:/data\n - /mnt/recordings:/recordings\n environment:\n - CAMERAUI_PLUGIN_NVR_STORAGE_PATH=/recordings\n\nUse a dedicated local disk for /recordings, not a network share. See System requirements."},{"page":"install/docker","title":"Docker","heading":"Workers","anchor":"workers","text":"A worker is a second machine that takes over camera work (decoding, plugins) from your main server. It runs no UI and no streaming engine of its own. It uses the same image as the server, started in worker mode with CAMERA_UI_WORKER=true.\n\nEnable workers on the main server and generate a pairing code there first. Compose file and environment variables: Worker setup, camera assignment: Worker assignments.\n\nCAMERA_UI_WORKER_CAPABILITIES narrows what the worker takes on: frameDecoding for decoding cameras, pluginHost for running plugins. Leave it unset and the worker offers both."},{"page":"install/docker","title":"Docker","heading":"Ports","anchor":"ports","text":"camera.ui uses these ports (already covered by host networking):\n\n| Port | Proto | Purpose |\n|---|---|---|\n| 3443 | tcp | HTTPS UI / API |\n| 2000 | tcp | go2rtc |\n| 2001 | tcp | RTSP |\n| 2002 | tcp | SRTP |\n| 2003 | tcp | RTMP |\n| 2004 | tcp/udp | WebRTC |\n| 1883 | tcp | MQTT broker, only if you enable the built-in one |\n\nWebRTC media runs over UDP on 2004, with TCP on the same port as fallback. Without host networking, map both."},{"page":"install/docker","title":"Docker","heading":"Data & backups","anchor":"data-backups","text":"All state lives in the cameraui-data volume: config, database, recordings, and TLS certificates. See Backup & restore.\n\nTo install plugins from a private registry or a mirror, mount your .npmrc to /root/.npmrc, see Private registry or mirror."},{"page":"install/docker","title":"Docker","heading":"Updating","anchor":"updating","text":"Pulling a new image updates only the image (OS, GPU libraries, launcher), not the server installed in the volume. Update the server from the Updates page, or run cameraui update-server -H /data in the container and restart it. The image:\n\ndocker compose pull\ndocker compose up -d\n\nSee Updating for details."},{"page":"install/hardware-acceleration","title":"Hardware acceleration","heading":"Hardware acceleration","anchor":"","text":"camera.ui accelerates two things: the video pipeline (decoding camera streams for detection, live view and snapshots, and encoding for streaming) and AI inference (object detection and everything built on it).\n\nVideo acceleration is automatic on every platform. camera.ui probes the hardware at runtime, picks the best available backend and falls back to software if there is none. AI accelerators are automatic too once the hardware is reachable; on Docker that means passing the device through (below)."},{"page":"install/hardware-acceleration","title":"Hardware acceleration","heading":"What runs where","anchor":"what-runs-where","text":"| Platform | Video pipeline (automatic) | AI backends |\n|---|---|---|\n| macOS (desktop app, Intel & Apple Silicon) | VideoToolbox (Apple GPU + media engine) | CoreML (Apple GPU + Neural Engine), ONNX |\n| Windows (desktop app, x64) | NVIDIA (CUDA/NVENC), Intel (QSV), and any GPU via Direct3D 11/12 | OpenVINO (Intel GPU/NPU), ONNX (NVIDIA CUDA) |\n| Linux x64 (Docker, bare-metal, Proxmox) | NVIDIA (CUDA/NVENC), Intel (VA-API/QSV), AMD (VA-API), plus Vulkan/OpenCL | OpenVINO (Intel GPU/NPU), ONNX (CUDA/TensorRT), NCNN (Vulkan), Coral, Hailo |\n| Linux arm64 (Rockchip) | Rockchip (RKMPP) | Coral, Hailo, NCNN (CPU/NEON) |\n\nThe table shows what camera.ui can use. The matching GPU driver still has to be installed on the host. A backend whose driver is missing is skipped silently, never an error.\n\nThe desktop app needs no setup: macOS ships video acceleration with the OS, Windows uses the system graphics driver. Each detection plugin logs what every model runs on (Loaded model: …) and shows it under Active Hardware in its plugin settings.\n\nThe rest of this page is for Linux and Docker, where hardware has to cross the container boundary in three steps:\n\n1. Host driver. The kernel-side driver must be installed on the host (the only part a Docker image can never ship).\n2. Device passthrough. The device node goes into the devices: section of your compose file.\n3. Verify. The container tells you what actually arrived.\n\nGPUs are matched by picking the right image flavor. AI accelerators work with any flavor: the Coral runtime is baked into every image, and the Hailo runtime ships with its plugin. The one exception is the Intel NPU, whose user-space driver is bundled in the intel flavor only."},{"page":"install/hardware-acceleration","title":"Hardware acceleration","heading":"Check your host","anchor":"check-your-host","text":"The cameraui/docker repository ships a host helper that diagnoses your machine and installs the drivers that need more than a package manager:\n\ncurl -fsSLO https://raw.githubusercontent.com/cameraui/docker/main/scripts/host/cameraui-host.sh\nsudo bash cameraui-host.sh check # read-only: drivers, docker runtime, device nodes\nsudo bash cameraui-host.sh nvidia # install the NVIDIA Container Toolkit\nsudo bash cameraui-host.sh hailo # build + install the Hailo PCIe kernel driver\nsudo bash cameraui-host.sh coral # install the gasket/apex driver (PCIe/M.2 Coral)\n\ncheck prints a ✓/✗ line for every accelerator class with the fix for anything missing. Run it first."},{"page":"install/hardware-acceleration","title":"Hardware acceleration","heading":"Verify from the container","anchor":"verify-from-the-container","text":"On every start the container logs which device nodes it can actually see:\n\n[setup] accelerator devices: /dev/dri ✓ · /dev/kfd ✗ · /dev/accel ✗ · /dev/apex_0 ✓ · /dev/hailo0 ✗ · /dev/bus/usb ✗ · nvidia ✗\n\nIf a device shows ✗ here but exists on the host, it is missing from your compose devices: list. The detection plugins additionally log what they found (Available devices: …) and what each model actually runs on (Loaded model: … (AUTO -> CPU)), and show it under Active Hardware in their plugin settings."},{"page":"install/hardware-acceleration","title":"Hardware acceleration","heading":"Quick reference","anchor":"quick-reference","text":"| Hardware | Accelerates | Host driver | Pass to the container |\n|---|---|---|---|\n| Intel / AMD GPU | Video (VA-API/QSV) + OpenCL/Vulkan inference | in the kernel (i915/amdgpu) | /dev/dri |\n| NVIDIA GPU | Video (NVENC/NVDEC) + CUDA | NVIDIA driver + Container Toolkit | via the toolkit runtime |\n| Coral Edge TPU (PCIe/M.2) | Object detection | gasket/apex (coral command) | /dev/apex_0 |\n| Coral Edge TPU (USB) | Object detection | none | /dev/bus/usb |\n| Hailo-8 / Hailo-8L | Object detection | hailo_pci + firmware (hailo command) | /dev/hailo0 |\n| Intel NPU (Core Ultra) | OpenVINO inference | intel_vpu kernel module + firmware | /dev/accel (intel flavor) |\n| AMD ROCm | Compute | ROCm stack | /dev/kfd + /dev/dri |"},{"page":"install/hardware-acceleration","title":"Hardware acceleration","heading":"Intel / AMD GPU","anchor":"intel-amd-gpu","text":"The driver is part of the kernel, nothing to install on the host. Use the intel or amd flavor and pass /dev/dri:\n\n devices:\n - /dev/dri:/dev/dri\n\nOlder Intel iGPUs (Gen8 to Gen11, e.g. UHD 630)\nIntel's current OpenCL runtime only covers Gen12 and newer. The intel flavor therefore bundles Intel's legacy runtime alongside it, so OpenVINO and OpenCL inference also work on Gen8 to Gen11 iGPUs (Broadwell through Comet Lake). Only chips older than Gen8 (Haswell and earlier) have no OpenCL runtime at all: there inference falls back to CPU, and a Coral or Hailo is the way to fast detection. Video decoding is fully accelerated on all of them either way."},{"page":"install/hardware-acceleration","title":"Hardware acceleration","heading":"NVIDIA GPU","anchor":"nvidia-gpu","text":"Two host-side pieces: the NVIDIA driver (e.g. sudo ubuntu-drivers install) and the Container Toolkit, which injects the driver libraries into containers. sudo bash cameraui-host.sh nvidia installs the toolkit and registers it with Docker. Then use the nvidia flavor override from the Docker page. Setting NVIDIA_DRIVER_CAPABILITIES=all also enables the OpenCL and Vulkan paths the detection plugins can use.\n\nOutside Docker (bare-metal Linux, Windows), CUDA detection with the ONNX plugin additionally needs the CUDA 13 libraries and cuDNN 9 installed system-wide from NVIDIA. In Docker, the nvidia image ships them and needs an NVIDIA driver 580 or newer; the nvidia-cuda12 image keeps CUDA 12 for older drivers and pairs with the ONNX Legacy plugin. The nvidia-tensorrt image additionally ships the TensorRT runtime for the ONNX plugin's tensorrt provider, which roughly doubles detection throughput at the cost of about twice the VRAM; outside Docker, install TensorRT 10 yourself if you want it. The line between the two runs at Turing: GTX 16xx / RTX 20xx and newer use the ONNX plugin on the CUDA 13 images, GTX 900 and 10xx (Maxwell, Pascal, Volta) only work with the ONNX Legacy plugin on nvidia-cuda12, and RTX 50xx runs exclusively on the CUDA 13 side. Cards older than GTX 900 are not supported."},{"page":"install/hardware-acceleration","title":"Hardware acceleration","heading":"Coral Edge TPU","anchor":"coral-edge-tpu","text":"The runtime library (libedgetpu) is already in every image flavor. Only the host side differs by form factor:\n\n- PCIe / M.2. Needs the gasket/apex kernel driver. sudo bash cameraui-host.sh coral installs it (a maintained community build, since Google's original package no longer compiles on kernels ≥ 6.13). Then pass the device:\n\n devices:\n - /dev/apex_0:/dev/apex_0\n \n\n- USB. No driver needed, just pass the USB bus:\n\n devices:\n - /dev/bus/usb:/dev/bus/usb\n \n\nWithout a reachable TPU the Coral plugin runs on CPU and says so in its log. Check its Available devices: line if detection seems slow.\n\nTo see which device detection actually runs on, run the benchmark in Metrics → Cameras: its per-camera page names the runtime, the model and the device. See What detection costs."},{"page":"install/hardware-acceleration","title":"Hardware acceleration","heading":"Hailo-8 / Hailo-8L","anchor":"hailo-8-hailo-8l","text":"The kernel driver is built from source on the host: sudo bash cameraui-host.sh hailo installs build dependencies, compiles the driver, installs the firmware and udev rules. Reboot afterwards so the firmware loads, then pass the device:\n\n devices:\n - /dev/hailo0:/dev/hailo0\n\nThe userspace runtime ships inside the Hailo plugin."},{"page":"install/hardware-acceleration","title":"Hardware acceleration","heading":"Intel NPU (Core Ultra)","anchor":"intel-npu-core-ultra","text":"The intel flavor bundles the NPU user-space driver (Level Zero + the Intel NPU UMD and compiler); other flavors do not. On the host you need the intel_vpu kernel module (upstream since Linux 6.8) and its firmware; if /dev/accel is missing, see intel/linux-npu-driver. Then pass the device through:\n\n devices:\n - /dev/accel:/dev/accel\n\nThe OpenVINO plugin then lists NPU in its Available devices: log line and its device setting."},{"page":"install/","title":"Which installation?","heading":"Which installation?","anchor":"","text":"camera.ui has one server that does the work, and apps (desktop, mobile, browser) that connect to it. Which install method fits depends on the platform the server runs on."},{"page":"install/","title":"Which installation?","heading":"The quick answer","anchor":"the-quick-answer","text":"- macOS or Windows → the desktop app (recommended). Runs the server and the viewer together, fit for 24/7 operation.\n- Linux → Docker (or Proxmox), with GPU acceleration.\n- Phone only → the mobile apps. They still need a server running somewhere."},{"page":"install/","title":"Which installation?","heading":"Choose a method","anchor":"choose-a-method","text":"| Method | Runs on | Recommended for |\n|---|---|---|\n| Desktop app | macOS, Windows, Linux | macOS & Windows. The easiest way; server + viewer in one |\n| Docker | Linux (x86-64 / arm64) | Linux servers, mini-PCs, NAS, and homelabs, with GPU acceleration |\n| Proxmox | Proxmox VE | Homelabs already running Proxmox |\n| Linux (bare-metal) | Linux | A Linux box without Docker |\n| Mobile apps | iOS, Android | Watching on the go (connects to your server) |\n\nKnown-good hardware\nFor always-on recording, a small Intel N100 mini-PC (with Docker) or an Apple Silicon Mac (with the desktop app) are both well-suited as low-power servers. See System requirements."},{"page":"install/","title":"Which installation?","heading":"A few notes","anchor":"a-few-notes","text":"- One server is enough. Desktop app, mobile apps and browser all view the same server. From outside your network, use remote access.\n- The desktop app can also be just a viewer. Install it in Viewer mode to watch a server running elsewhere. See Desktop app."},{"page":"install/linux","title":"Linux (bare-metal)","heading":"Linux (bare-metal)","anchor":"","text":"For always-on servers without Docker: camera.ui installs from npm and runs as a systemd service. Ubuntu 24.04 (or newer) is the tested baseline. See System requirements."},{"page":"install/linux","title":"Linux (bare-metal)","heading":"Prerequisites","anchor":"prerequisites","text":"- 64-bit Linux (x64 or arm64) with systemd\n- Node.js 22.18 or newer, 24.22.1 or newer recommended, e.g. via NodeSource:\n\n curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash -\n sudo apt-get install -y nodejs\n \n\nFFmpeg, go2rtc and a portable Python runtime for the detection plugins are downloaded and managed by camera.ui."},{"page":"install/linux","title":"Linux (bare-metal)","heading":"Install","anchor":"install","text":"sudo npm install -g camera.ui\nsudo cameraui install --user cameraui\n\ncameraui install creates the service user if it doesn't exist, registers a systemd service named cameraui and starts it on boot. The first start downloads the server and takes a few minutes (sudo cameraui logs --user cameraui shows progress). The web UI then runs on https://:3443.\n\nIf the service user needs GPU access for hardware acceleration, add it to your distribution's video/render groups. --group video only applies when the install creates the user. For an existing user, run sudo usermod -aG video cameraui and restart the service."},{"page":"install/linux","title":"Linux (bare-metal)","heading":"Worker mode","anchor":"worker-mode","text":"To run this machine as a worker for another camera.ui server, add --worker to the install command:\n\nsudo cameraui install --user cameraui --worker\n\nThe flag is stored with the service, so the machine comes back up as a worker after a reboot. Generate a pairing code on the main server, put the snippet into this machine's config file as described on the workers page, then run sudo cameraui restart --user cameraui."},{"page":"install/linux","title":"Linux (bare-metal)","heading":"Managing the service","anchor":"managing-the-service","text":"cameraui status # service state\nsudo cameraui logs --user cameraui # tail the logs\nsudo cameraui restart --user cameraui # also: start / stop\nsudo cameraui update-server # update to the latest server release\nsudo cameraui update-server 1.2.3 # or pin a specific version\n\nThe service runs as its own user, so everything except status needs sudo and the same --user you installed with. If you installed with -H , pass that instead.\n\nThe Updates page does the same from the web interface; update-server is the CLI equivalent."},{"page":"install/linux","title":"Linux (bare-metal)","heading":"Updating Node.js","anchor":"updating-nodejs","text":"Below Node.js 22.18 the server keeps running, logs a warning and holds back server updates. Run the NodeSource commands from Prerequisites again, then restart the service (sudo cameraui restart --user cameraui). The first start on a new Node.js major version reinstalls the server for it, which takes a few minutes. A worker needs the same on its own machine."},{"page":"install/linux","title":"Linux (bare-metal)","heading":"Updating the launcher","anchor":"updating-the-launcher","text":"The camera.ui package from npm is the launcher: it writes the service, starts the server and rolls back a server update that does not start. The Updates page does not update it. Do that on each machine:\n\nsudo npm install -g camera.ui\nsudo cameraui reinstall\n\nreinstall writes the service again with the current launcher and keeps the user, data directory and worker setting of the installed service."},{"page":"install/linux","title":"Linux (bare-metal)","heading":"Data","anchor":"data","text":"Everything lives in the service user's home under ~/.camera.ui (config, database, recordings, TLS certs). That's the directory to back up. A different location can be set with -H during install."},{"page":"install/linux","title":"Linux (bare-metal)","heading":"Hardware acceleration","anchor":"hardware-acceleration","text":"Video acceleration is automatic, but on bare metal the host provides the drivers that the Docker images would otherwise bundle:\n\n- Intel GPU: sudo apt install intel-media-va-driver-non-free (VA-API/QSV). For OpenVINO or OpenCL inference on the GPU, also install the OpenCL runtime: sudo apt install ocl-icd-libopencl1 intel-opencl-icd\n- AMD GPU: sudo apt install mesa-va-drivers\n- NVIDIA: the regular driver (e.g. sudo ubuntu-drivers install); the Container Toolkit is Docker-only, you don't need it here. CUDA detection with the ONNX plugin additionally needs the CUDA 13 libraries and cuDNN 9 on the system (from NVIDIA's apt repo: sudo apt install cuda-libraries-13-2 cudnn9-cuda-13); the ONNX Legacy plugin wants CUDA 12 instead. The Docker images ship these, bare metal does not\n- Coral Edge TPU: the libedgetpu runtime plus (for PCIe/M.2) the gasket kernel driver, see the Coral section\n- Hailo: the kernel driver, see the Hailo section\n\nNo device passthrough is needed. The detection plugins log what they found (Available devices: …)."},{"page":"install/linux","title":"Linux (bare-metal)","heading":"Uninstall","anchor":"uninstall","text":"sudo cameraui uninstall # removes the systemd service\nsudo npm remove -g camera.ui\n\nThe data directory (~/.camera.ui) is left untouched."},{"page":"install/mobile","title":"Mobile apps","heading":"Mobile apps","anchor":"","text":"The iOS and Android apps are viewers for live view, recordings and notifications. They connect to a server running elsewhere (the desktop app or a server install)."},{"page":"install/mobile","title":"Mobile apps","heading":"Download","anchor":"download","text":"- iOS / iPadOS. Currently in beta. Join through TestFlight: install Apple's TestFlight app, then open the invite link on your device.[^appstore]\n- Android. Currently in beta. Join the beta group with the Google account you use in the Play Store, become a tester, then install from Google Play.[^androidbeta]\n\n| Platform | Requirement |\n|---|---|\n| iOS / iPadOS | 17.4 or newer |\n| Android | 8.0 or newer (API 26) |"},{"page":"install/mobile","title":"Mobile apps","heading":"Connecting to your server","anchor":"connecting-to-your-server","text":"Sign in with your camera.ui account (Google, Apple, or email) and your paired servers appear. The app picks the route itself:\n\n- At home. On the server's network it connects directly, so live view starts faster. It trusts the server's certificate for local addresses automatically.\n- Away. Off the network, it goes through camera.ui Cloud.\n\nOn the server side this needs:\n\n- Remote access via camera.ui Cloud enabled.\n- The server paired to your account."},{"page":"install/mobile","title":"Mobile apps","heading":"Notifications","anchor":"notifications","text":"A signed-in app receives push notifications for events. What you get alerted about is set under Notifications.[^license]"},{"page":"install/mobile","title":"Mobile apps","heading":"Biometric login","anchor":"biometric-login","text":"Settings → Account → Biometric login signs you in with Face ID or Touch ID instead of your password. The credentials are kept in the device's secure storage; sign out and back in once after enabling it so they get stored. Turning it off removes them."},{"page":"install/mobile","title":"Mobile apps","heading":"App lock","anchor":"app-lock","text":"App lock with Face ID / Fingerprint in the app's settings asks for biometrics (or the device passcode as fallback) at every start, and again after a few minutes in the background. A brief switch away doesn't re-prompt. It is independent of biometric login."},{"page":"install/mobile","title":"Mobile apps","heading":"Updates","anchor":"updates","text":"The apps update through the App Store and Google Play (during the beta: TestFlight on iOS, the Play Store testing track on Android), plus over-the-air updates to the interface. See Updating.\n\nIf the server or its NVR plugin is older than the app needs, the app shows Update recommended with the version it needs and the one installed. Update server and plugin on the Updates page. Got it hides the notice for these versions.\n\n[^license]: An active camera.ui subscription covers recording (NVR) and the features built on it, such as playback, export, face recognition, semantic search, and AI descriptions, plus push notifications. Live view and real-time detection are free.\n[^appstore]: The app moves to the App Store with the public release.\n[^androidbeta]: Google requires testers to stay enrolled for at least 14 days before the app can go public, so please stay opted in. For beta builds, turn on Beta updates under Settings → System in the app."},{"page":"install/proxmox","title":"Proxmox","heading":"Proxmox","anchor":"","text":"camera.ui runs on Proxmox VE inside an LXC container that hosts Docker. One command on the Proxmox host sets up everything: the container, Docker, and camera.ui.\n\nWhy an LXC and not a VM?\nAn LXC can share the iGPU: /dev/dri stays available to the host and other containers at the same time, while a VM with PCIe passthrough monopolizes the GPU, and an LXC has less overhead. A VM is the better choice for NVIDIA cards (see below)."},{"page":"install/proxmox","title":"Proxmox","heading":"Quick start","anchor":"quick-start","text":"Run this on the Proxmox host (as root):\n\ncurl -fsSLO https://raw.githubusercontent.com/cameraui/docker/main/proxmox/install-cameraui-lxc.sh\nbash install-cameraui-lxc.sh\n\nThe script downloads the Ubuntu 24.04 LXC template, creates an unprivileged container with nesting (privileged containers can no longer load Docker's AppArmor profile on current Proxmox releases), installs Docker inside it, deploys camera.ui via Docker Compose and prints the URL of the web UI. Tested with Proxmox VE 9.\n\nEnvironment variables:\n\n| Variable | Default | Meaning |\n|---|---|---|\n| CTID | next free ID | container ID |\n| CT_HOSTNAME | cameraui | container hostname |\n| CORES / RAM_MB / DISK_GB | 4 / 4096 / 32 | container resources |\n| BRIDGE | vmbr0 | network bridge (container gets its IP via DHCP) |\n| STORAGE | auto-detect | rootfs storage (prefers local-lvm, local-zfs, local, otherwise the first active storage that can hold a container rootfs) |\n| TEMPLATE_STORAGE | local | storage the LXC template is downloaded to (needs content type \"Container template\") |\n| FLAVOR | cpu | cpu, intel or amd (picks the image flavor); nvidia is experimental |\n| GPU_PASSTHROUGH | 1 when flavor ≠ cpu | pass /dev/dri into the container |\n| TZ | host timezone | timezone inside the container |\n\nExample for an Intel machine: FLAVOR=intel bash install-cameraui-lxc.sh"},{"page":"install/proxmox","title":"Proxmox","heading":"Getting a shell in the container","anchor":"getting-a-shell-in-the-container","text":"The script creates the container without a root password, so the Proxmox console and SSH won't let you in. From the host shell:\n\npct enter \n\nSingle commands:\n\npct exec -- docker logs cameraui\n\nConsole and SSH work once you set a password:\n\npct exec -- passwd"},{"page":"install/proxmox","title":"Proxmox","heading":"Hardware acceleration","anchor":"hardware-acceleration","text":"- Intel / AMD iGPU. Set FLAVOR=intel (or amd). The script passes the GPU render node through the container into Docker, nothing else to configure. Details: Hardware acceleration.\n- NVIDIA. Use a VM with PCIe passthrough instead of an LXC. Inside it, follow the standard Docker setup with the nvidia flavor. An LXC would need the host driver and the container's user-space libraries in permanent version lockstep. The VM does monopolize the card. If other services on the host need the same GPU (Ollama, Jellyfin), see NVIDIA in an LXC below.\n- AI accelerators (Coral, Hailo). Install the host driver on the Proxmox host, then pass the device node (/dev/apex_0, /dev/hailo0) into the container."},{"page":"install/proxmox","title":"Proxmox","heading":"NVIDIA in an LXC (experimental)","anchor":"nvidia-in-an-lxc-experimental","text":"FLAVOR=nvidia shares the card between the container and other services on the host, which a VM cannot do. The catch is a hard version coupling: the NVIDIA user-space libraries inside the container must exactly match the host's kernel driver, after every driver update.\n\nIt needs a working NVIDIA driver on the Proxmox host first (.run installer with --dkms plus pve-headers; nvidia-smi on the host must work), and the installed driver version must exist on download.nvidia.com. Then:\n\nFLAVOR=nvidia bash install-cameraui-lxc.sh\n\nThe script passes all /dev/nvidia* device nodes into the container, installs the matching user-space driver (--no-kernel-module) plus the NVIDIA Container Toolkit (with no-cgroups=true, because an unprivileged container cannot manage device cgroups), and registers a boot-time sync service that re-installs the matching user space after host driver updates. Verify with:\n\npct exec -- docker exec cameraui nvidia-smi\n\nExperimental\nThis path depends on NVIDIA's installer archive and on the host driver staying healthy; after a host driver update the container needs a reboot to re-sync. If it breaks, the VM route always works. Reports and fixes are welcome in the docker repo."},{"page":"install/proxmox","title":"Proxmox","heading":"Recordings on a dedicated disk or NAS","anchor":"recordings-on-a-dedicated-disk-or-nas","text":"Recordings default to the container's own volume. For a bigger disk, NAS share or merged pool, hand that storage into the container and set the NVR's storage path to it.\n\nUnprivileged containers cannot mount NFS/CIFS shares themselves. Mount the share on the Proxmox host and hand it into the container as a mount point:\n\npct set -mp0 /mnt/nas/recordings,mp=/mnt/recordings\n\nThen set the NVR Storage Path to /mnt/recordings (Settings → Recordings → Storage), or see the storage section of the Docker page.\n\nFUSE mounts (mergerfs, rclone) need extra care\nA FUSE mount (such as a mergerfs pool merging several drives) does not propagate into an (unprivileged) container the way a normal filesystem does. If the FUSE mount isn't active on the host before the container starts, the container sees the empty mountpoint on the host's root disk instead of the pool. Recordings then land on the small OS disk and get rotated away within minutes.\n\nMount the pool on the host before the container starts (e.g. via an fstab entry or a systemd unit ordered Before=pve-container@.service), and make sure the mount is shared into the container. Then always verify from inside the container."},{"page":"install/proxmox","title":"Proxmox","heading":"Verify your storage size","anchor":"verify-your-storage-size","text":"After setting a custom storage path, check that the container sees the full volume:\n\npct exec -- df -h /mnt/recordings\n\nThe reported size must match your real disk or pool. The small container root size means the mount didn't propagate. The NVR also logs its resolved storage and size on every start, and shows a warning in Settings → Recordings when the volume is unexpectedly small."},{"page":"install/proxmox","title":"Proxmox","heading":"Verify","anchor":"verify","text":"On every start the container logs which devices actually arrived. See Verify from the container. The camera.ui UI is available at https://:3443 (the script prints the IP at the end)."},{"page":"install/proxmox","title":"Proxmox","heading":"Updating","anchor":"updating","text":"The server updates from the Updates page. The image (OS, GPU libraries, launcher) updates inside the container:\n\npct exec -- sh -c 'cd /opt/cameraui && docker compose pull && docker compose up -d'\n\nOlder versions of the script wrote /opt/cameraui/docker-compose.yml without stop_grace_period. If yours lacks it, add stop_grace_period: 30s to the cameraui service once, as in the Docker quick start, so Docker gives camera.ui time to shut down."},{"page":"install/tv","title":"Apple TV","heading":"Apple TV","anchor":"","text":"The Apple TV app puts your cameras on the big screen: a live wall, your events with playback, and your sensors.[^license] It connects to a server running elsewhere (the desktop app or a server install)."},{"page":"install/tv","title":"Apple TV","heading":"Download","anchor":"download","text":"Currently in beta. Join through TestFlight: install Apple's TestFlight app on your Apple TV, then open the invite.[^appstore]\n\n| Platform | Requirement |\n|---|---|\n| Apple TV | tvOS 17 or newer |"},{"page":"install/tv","title":"Apple TV","heading":"Connecting to your server","anchor":"connecting-to-your-server","text":"The app connects straight to your server, not through camera.ui Cloud.\n\nOn the same network the app lists your server by itself. You can also add one by address:\n\n- On your network. Enter the server's local address, for example https://192.168.1.20:3443.\n- From anywhere. Enter your own public address, set up with a custom domain and a reverse proxy.\n\nSign in with your username and password, plus a two-factor code if you use one, or paste an API token.[^token]\n\nThe app verifies the server's certificate. From the first sign-in on it accepts only the certificate it saw then, others signed by the server's own certificate authority, and publicly trusted ones. For any other certificate the status at the top shows Certificate changed: choose Sign out under Settings and add the server again to accept the new one.\n\nWhen the server no longer accepts the sign-in, after a password change for example, the status at the top shows Signed out. Choose Sign out under Settings and add the server again. The same applies after a backup restore as a copy, and after a move when the server's address changed.\n\nYou can add several servers and switch between them under Settings → Switch server. With a single server the app opens it right away. With several it starts on the server picker, unless Open at launch in one server's settings is set to This server."},{"page":"install/tv","title":"Apple TV","heading":"Around the app","anchor":"around-the-app","text":"- Home. The latest activity up top, plus camera and storage status and today's event count. Selecting a notification opens its recording. Recent activity follows the Recent Events filter of your account.\n- Camview. A live wall of your cameras. Grid or mixed layout, rearrangeable tiles, fullscreen per camera. Fill wall drops the borders and fills the screen, Whole picture letterboxes each camera in its tile instead of cropping it. Both are remembered per view.\n- Events. Your events grouped by day, with filters for persons, faces, vehicles, animals, doorbell, sensors, sounds, and motion. Selecting one plays it back from that moment.\n- Episodes. Episodes show up as mosaic cards among your events and on the Home screen. Opening one plays the whole story and switches cameras by itself. Left and right jump between cameras.\n- Talk back. In the fullscreen player, the microphone button speaks through the camera, if the camera supports two-way audio.\n- PTZ. A camera that can pan, tilt or zoom gets a move button in the player. Hold a direction to keep moving, a short press nudges. Home position and presets appear when the camera has them. Admin accounts only. See PTZ & autotrack.\n\nPlayback follows each camera's playback source, episodes included. On Auto a gap in the high recording plays the next quality that has footage, the same as in the web interface."},{"page":"install/tv","title":"Apple TV","heading":"Sensors","anchor":"sensors","text":"The Sensors page lays out every sensor as a tile, grouped by type, with its current state.[^sensors] Switching them takes an admin account, other accounts see the states only.\n\n- Click to toggle. Lights, switches, sirens, locks and garages flip on select.\n- Hold for more. Holding select, or pressing play/pause, opens brightness, volume, alarm modes, or lock and garage controls.\n- Live. Sensors and states update as they change on the server."},{"page":"install/tv","title":"Apple TV","heading":"Updates","anchor":"updates","text":"During the beta the app updates through TestFlight.\n\nIf the server or its recording plugin is older than the app needs, the app shows an Update needed screen naming what to update. Update it on the server, then reopen the app.\n\n[^license]: An active camera.ui subscription covers recording (NVR) and the features built on it, such as playback. Live view is free.\n[^appstore]: The app moves to the App Store with the public release.\n[^token]: Create an API token in camera.ui under Settings, Account, API tokens.\n[^sensors]: These are the same sensors as the web Sensors page. Detection sensors tied to a single camera stay hidden here."},{"page":"install/updating","title":"Updating","heading":"Updating","anchor":"","text":"camera.ui has two parts that update: the server and the app you installed it with. Both are handled on the Updates page, whichever way you installed."},{"page":"install/updating","title":"Updating","heading":"The Updates page","anchor":"the-updates-page","text":"Updates in the navigation collects everything that can be updated, in three sections: Server, Plugins and Workers. A row with a newer version opens that version's release notes, Check for updates looks again. Admins only.\n\nUpdate all installs workers first, then plugins, and the server last, because installing the server restarts it. If a worker fails, the server is skipped, since a worker left on the old version cannot connect to the new one, and the plugins stopped for the server step start again. A failed item can be retried on its own. Only one run happens at a time, so the button waits while a plugin or worker update you started by hand is running.\n\nBefore the server installs a new version, from Update all or on its own, camera.ui checks the workers. A worker that would no longer connect afterwards, because it is offline or cannot be updated from here, is named in a dialog, and Update anyway goes ahead without it.\n\nA row can also say why it cannot be updated from here. Updates with the desktop app means an app too old to install its own updates remotely, so update it on its machine. No launcher, update it on its own machine means a worker that runs without the camera.ui service, which is what installs new versions. Update it once on its own machine means a worker on a release too old to take updates from the master. Requires Node.js … means the new version needs a newer Node.js than this machine runs, see Updating Node.js.\n\ncamera.ui checks for new versions every 10 minutes and notifies you when the server, a plugin or a worker has one."},{"page":"install/updating","title":"Updating","heading":"Updating the server","anchor":"updating-the-server","text":"How the server updates depends on the install method:\n\n- Docker or bare-metal. The Updates page installs the new version and restarts the server. Pulling a new Docker image does not update the server (see Updating the Docker image). On bare metal the launcher is updated by hand, see Updating the launcher.\n- Desktop app. The app and the server move together, so the Server row is the app itself. See Updating the desktop app.\n\nWith Docker or bare metal, a server update needs about 2 GB free on the disk with camera.ui's data, on workers too. With less it does not start, and the message says how much is free.\n\nA pre-release notification is titled Beta update."},{"page":"install/updating","title":"Updating","heading":"Beta updates","anchor":"beta-updates","text":"With Beta updates on in Settings → System, the server offers pre-release versions, and Manage on that page lists them to install a specific one. The switch reaches everything that follows this server: the desktop app moves to the beta feed, a server on a beta pulls its desktop workers onto the matching beta, and on the mobile app it also turns on beta over-the-air updates of the interface. Pre-releases can be less stable. Once you are on a pre-release, the server keeps offering the next beta even if you turn the switch back off. Install a stable version from Manage to leave the beta track."},{"page":"install/updating","title":"Updating","heading":"Beta versions of plugins","anchor":"beta-versions-of-plugins","text":"Plugins have their own switch, and the server's Beta updates does not reach them. Beta versions in the gear menu next to the search on the Plugins page adds pre-releases to each plugin's version picker and offers them as updates. Turned off, an installed beta stays until you pick another version. See Plugins."},{"page":"install/updating","title":"Updating","heading":"Updating the desktop app","anchor":"updating-the-desktop-app","text":"The desktop app checks for updates at startup and every 10 minutes while it runs. At startup it offers a found update on its start screen for 30 seconds, then starts on the current version. A found update also appears as the Server row on the Updates page: installing it downloads the update and relaunches the app, also from another device. The app never installs an update without a click.\n\nOne exception on Linux: only the .AppImage updates itself. The .deb is updated by installing the new .deb from the download page."},{"page":"install/updating","title":"Updating","heading":"Updating the Docker image","anchor":"updating-the-docker-image","text":"The server and the Docker image update separately. The launcher installs the server into the volume on first start and keeps that version across every later pull.\n\nTo update the image (base OS, GPU libraries, launcher):\n\ndocker compose pull\ndocker compose up -d\n\nTo update the server, use the Updates page, or run cameraui update-server -H /data in the container and restart it. The -H /data matters: without it the update lands outside the data volume and the restart boots the old version again."},{"page":"install/updating","title":"Updating","heading":"Updating the mobile apps","anchor":"updating-the-mobile-apps","text":"The mobile apps update through the App Store and Google Play (during the beta: TestFlight on iOS, the Play Store testing track on Android), and also receive over-the-air updates to the interface."},{"page":"intro/architecture","title":"How it works","heading":"How it works","anchor":"","text":"camera.ui is server-centric: one server, running on your hardware, does all the work. It connects to your cameras, streams and records video, runs detection, and serves the interface. The browser and the mobile apps are viewers of that server. The desktop app can be a viewer too, or run the server itself."},{"page":"intro/architecture","title":"How it works","heading":"One self-contained server","anchor":"one-self-contained-server","text":"The camera.ui server bundles everything it needs to run: the web interface, a built-in streaming engine (our own go2rtc build) for live video, and its own video processing for decoding and recording. Your settings, cameras, users, and events live in a small local database on the same machine.\n\nNothing leaves your network unless you turn on remote access. There is no external database, and no cloud account is required to run a server."},{"page":"intro/architecture","title":"How it works","heading":"The big picture","anchor":"the-big-picture","text":"flowchart LR\n cam[CamerasRTSP · ONVIF]\n\n subgraph server[camera.ui server · your hardware]\n direction TB\n g[Streaming engine] --> fw[Frame workerone per camera]\n fw --> det[Detectionmotion → objects → faces · platesaudio in parallel]\n det --> nvr[(NVR pluginrecordings + push)]\n g --> api[Web UI + API]\n nvr --> api\n end\n\n cam --> g\n api --> viewers[Viewersweb · desktop · mobile]\n api -. optional .-> remote[Remote accessCloud · tunnel]\n remote -. from anywhere .-> viewers"},{"page":"intro/architecture","title":"How it works","heading":"From camera to notification","anchor":"from-camera-to-notification","text":"1. Streaming. The streaming engine connects to each camera and turns its feed into browser-friendly live video (WebRTC and MSE). Streams are kept \"warm\" so live view and snapshots load instantly.\n2. Per-camera analysis. Every camera gets its own frame worker, a background process that decodes the video and runs detection. A problem with one camera does not affect the others.\n3. Layered detection. Cheap motion detection runs first and only wakes the heavier AI on movement: object detection, then faces, license plates, classification, and semantic (CLIP) analysis. Audio is analyzed in parallel. This \"cascade\" keeps CPU and GPU use low.[^detect]\n4. Events & recording. When detection fires, the server builds an event with segments, thumbnails, and the objects, faces, or plates it found. The NVR plugin records the footage, stores it, and serves it back for playback.[^license]\n5. Notifications. Events can trigger push notifications and run automations.[^license]"},{"page":"intro/architecture","title":"How it works","heading":"Plugins make it extensible","anchor":"plugins-make-it-extensible","text":"Much of camera.ui is delivered by plugins from the in-app store. Each plugin runs in its own process, so a failing plugin can't take down the server, and it restarts automatically after a crash.\n\nPlugins provide:\n\n- Camera sources. ONVIF and other camera protocols.\n- Detectors. Motion engines and the AI backends (CoreML, ONNX, OpenVINO, NCNN, Coral, Hailo).\n- Recording & notifications. The NVR plugin records your footage and sends push notifications.\n- Smart-home bridges. Apple HomeKit.\n\nLearn more under Plugins."},{"page":"intro/architecture","title":"How it works","heading":"Apps: desktop, mobile, web","anchor":"apps-desktop-mobile-web","text":"Every app shows the same interface, but their roles differ:\n\n- The desktop app can be the server itself (all-in-one), a viewer that connects to another server, or a worker that helps another server with decoding, detection, or a plugin. You choose on first launch, and can switch anytime. See Scaling across machines.\n- The mobile apps and the browser are always viewers.\n\nHow viewers reach the server:\n\n- On your network, the browser and a desktop viewer connect directly.\n- The mobile apps connect directly over your local network at home, and through camera.ui Cloud when you're away.[^cloud-optional]\n- A browser away from home uses whichever remote access option you set up: camera.ui Cloud, a Cloudflare tunnel, or your own domain.\n\nYou can also save more than one server as an Instance and switch between them from the same app."},{"page":"intro/architecture","title":"How it works","heading":"Scaling across machines","anchor":"scaling-across-machines","text":"Extra machines can join as workers. A worker decodes some cameras, or runs an entire plugin (for a detector that needs hardware the main server lacks). Cameras and plugins assigned to a worker fall back to the main server while the worker is offline and move back once it reconnects. See Workers."},{"page":"intro/architecture","title":"How it works","heading":"Reaching it from outside","anchor":"reaching-it-from-outside","text":"From outside your network you can use camera.ui Cloud, a Cloudflare tunnel, a custom domain or port-forwarding. All are optional. See Remote access.\n\n[^detect]: Detection needs a detection plugin that matches your hardware (CoreML, ONNX, OpenVINO, NCNN, or an edge accelerator like Coral or Hailo). See Detection & AI.\n[^license]: An active camera.ui subscription covers recording (NVR) and the features built on it, such as playback, export, face recognition, semantic search, and AI descriptions, plus push notifications. Live view and real-time detection are free.\n[^cloud-optional]: camera.ui Cloud is optional. On your own network everything stays local, and your server never has to connect to the cloud. See Reaching it from outside."},{"page":"intro/getting-started","title":"Getting started","heading":"Getting started","anchor":"","text":"From a running server to your first camera. If the server isn't installed yet, start with Installation."},{"page":"intro/getting-started","title":"Getting started","heading":"1. Open camera.ui","anchor":"1-open-cameraui","text":"camera.ui runs over HTTPS. How you open it depends on how you installed it:\n\n- Desktop app. It opens automatically when you launch it (in Server mode it starts the server for you).\n- Server (Docker, Proxmox, bare-metal). Open https://:3443 in your browser, or https://localhost:3443 on the same machine.\n\nFirst-visit certificate warning\ncamera.ui uses a self-signed certificate, so the browser warns on the first visit. Continue with Advanced → Proceed. To get rid of the warning, install the certificate from Settings → System → Certificate."},{"page":"intro/getting-started","title":"Getting started","heading":"2. Sign in","anchor":"2-sign-in","text":"The first time, sign in with the default account:\n\n- Username: admin\n- Password: admin\n\nYou'll replace these in the next step."},{"page":"intro/getting-started","title":"Getting started","heading":"3. First-run setup","anchor":"3-first-run-setup","text":"The first sign-in opens a setup wizard:\n\nWelcome → Appearance → Account → Finish"},{"page":"intro/getting-started","title":"Getting started","heading":"Welcome","anchor":"welcome","text":"- New Installation. Set up a fresh server.\n- Restore from Backup. Upload a backup archive from another camera.ui install. camera.ui shows what it holds and restores it after you confirm, see Restoring.\n- From Another Server. Enter the move code of the old server and fetch its backup directly, see Moving to another server."},{"page":"intro/getting-started","title":"Getting started","heading":"Appearance","anchor":"appearance","text":"Theme (light, dark, or follow the system) and language."},{"page":"intro/getting-started","title":"Getting started","heading":"Account","anchor":"account","text":"Your own username, password and optional avatar. This account replaces the default admin / admin.\n\nChoose a strong password\nThis is an administrator account. Use a strong, unique password, especially if you plan to reach camera.ui from outside your network."},{"page":"intro/getting-started","title":"Getting started","heading":"Finish","anchor":"finish","text":"Enjoy! saves and signs you out. Sign back in with the username and password you just set."},{"page":"intro/getting-started","title":"Getting started","heading":"4. Add your first camera","anchor":"4-add-your-first-camera","text":"After signing in you land on the Home screen. Select Get started, or open Cameras.\n\nThe Cameras page scans your network and lists the cameras it finds under Discovered. Select one, enter its username and password, and it's added. If your camera isn't found, use Add camera to enter its stream details by hand.\n\nFor the full walkthrough, see Adding a camera."},{"page":"intro/getting-started","title":"Getting started","heading":"Getting around","anchor":"getting-around","text":"The sidebar on the left is grouped by purpose. Your daily pages sit at the top. A Manage group holds cameras, sensors, faces, automations, plugins and instances. A System group keeps the diagnostic pages and starts collapsed. On a phone the same groups show up in the menu.\n\nThe pencil at the top of the sidebar starts edit mode: drag pages to another spot or group, hide settings pages with the eye, and confirm with the check. Reset to defaults brings back the original layout. The layout is saved to your account and follows you to every browser and device.\n\nIn Settings → Appearance you can also add the settings pages to the sidebar as their own group, and choose which groups can collapse."},{"page":"intro/getting-started","title":"Getting started","heading":"Next steps","anchor":"next-steps","text":"- Live view — watch your cameras live\n- Recording (NVR) — turn on 24/7 or event recording\n- Detection & AI — add motion and object detection\n- Remote access and the mobile apps — reach your cameras from anywhere"},{"page":"intro/quick-start","title":"Quick start","heading":"Quick start","anchor":"","text":"The short path from install to a camera that detects and records. Each step links to the page with the details."},{"page":"intro/quick-start","title":"Quick start","heading":"1. Install","anchor":"1-install","text":"- macOS or Windows. The desktop app.\n- Linux. Docker: save the compose file from that page and run docker compose up -d. Proxmox users take the Proxmox script.\n\nThe desktop app opens camera.ui by itself. Otherwise open https://:3443. The certificate is self-signed, so the browser warns on the first visit."},{"page":"intro/quick-start","title":"Quick start","heading":"2. Sign in","anchor":"2-sign-in","text":"Sign in with admin / admin. The setup wizard asks for theme and language, then for your own username and password, which replace the default account. Sign in again with the new account. See Getting started."},{"page":"intro/quick-start","title":"Quick start","heading":"3. Add a camera","anchor":"3-add-a-camera","text":"Open Cameras. Cameras found on your network show up under Discovered: select one, enter its username and password, confirm. Discovery needs ONVIF turned on in the camera. A camera that isn't found is added with Add camera and its RTSP URL. Without an RTSP URL, a brand plugin from the store brings the camera in, Reolink for example:[^cloud] install it, set it up on its page, and its cameras appear under Discovered. See Adding a camera."},{"page":"intro/quick-start","title":"Quick start","heading":"4. Install the detection plugins","anchor":"4-install-the-detection-plugins","text":"The NVR plugin for recording is already installed. Under Plugins → Search plugins add:\n\n- A motion engine. Rust Motion fits most setups.\n- An AI backend for your hardware: CoreML on Apple Silicon, OpenVINO on Intel, ONNX on everything else.\n\nSee Object detection & AI backends for the other options."},{"page":"intro/quick-start","title":"Quick start","heading":"5. Link the server to your account","anchor":"5-link-the-server-to-your-account","text":"Recording and the mobile apps run on a camera.ui account, created at cloud.cameraui.com.\n\n1. Settings → Remote Access → Register server, then sign in. The same screen pairs the mobile apps.\n2. Settings → Recordings, card License & Cloud: click the cloud icon and sign in. The license activates on its own.\n\nSee Turning on recording."},{"page":"intro/quick-start","title":"Quick start","heading":"6. Turn on detection","anchor":"6-turn-on-detection","text":"In the camera's settings, open the Plugins tab and the Detection category. Set Motion to Rust Motion and Object to your AI backend. Detection starts right away, sensitivity and confidence are under Detection in the same settings. See Set up sensors."},{"page":"intro/quick-start","title":"Quick start","heading":"7. Check recording","anchor":"7-check-recording","text":"With an active license every camera records around the clock in all stream qualities, as long as your plan has a free camera slot. To record only events, switch the camera to Event in its recording settings. See Recording modes."},{"page":"intro/quick-start","title":"Quick start","heading":"Next steps","anchor":"next-steps","text":"- Live view — watch your cameras\n- Notifications — pushes for what your cameras see\n- Zones & privacy — limit where detection counts\n- Remote access — reach camera.ui from outside your network\n\n[^cloud]: There are also plugins for cameras that depend on a vendor cloud, such as Eufy, Tuya, Wyze or Ring. They work, but these cameras are not recommended and not covered by support. Use local RTSP / ONVIF cameras for a reliable experience."},{"page":"intro/requirements","title":"System requirements","heading":"System requirements","anchor":"","text":"What camera.ui needs depends on how you run it and what you ask it to do. Live-viewing a couple of cameras is light; recording many cameras around the clock with AI detection needs real hardware."},{"page":"intro/requirements","title":"System requirements","heading":"At a glance","anchor":"at-a-glance","text":"| | Minimum | Recommended |\n|---|---|---|\n| CPU | 4 modern cores | Apple Silicon (M1+), Intel N100, or better |\n| RAM | 8 GB | 16 GB |\n| Hardware acceleration | none (software) | a recent GPU / accelerator, or Apple Silicon |\n| Disk (app) | ~16 GB SSD | SSD |\n| Recordings | a separate, dedicated disk, sized to your cameras and retention | |\n\nKnown-good hardware\nA small Intel N100 mini-PC or an Apple Silicon Mac (M1 or newer) comfortably runs a typical setup (several cameras with recording and AI detection) at low power.\npushes you higher: more cameras, higher resolutions, continuous recording, and especially AI detection, which is far lighter with a supported GPU, accelerator, or Apple Silicon (see below)."},{"page":"intro/requirements","title":"System requirements","heading":"Running the server","anchor":"running-the-server","text":"You can run the camera.ui server on Linux, macOS, or Windows:\n\n- Linux. In Docker, on Proxmox, or bare-metal. Ubuntu 24.04 (or newer) is the tested baseline. A bare-metal install only needs Node.js 22.18 or newer, since camera.ui manages everything else (FFmpeg, go2rtc, a portable Python runtime) itself; the Docker images bundle everything.\n- macOS. Through the desktop app in Server mode. Apple Silicon Macs (M1 and newer) run it at low power with full hardware acceleration, including the Neural Engine.\n- Windows. Through the desktop app in Server mode."},{"page":"intro/requirements","title":"System requirements","heading":"Hardware acceleration","anchor":"hardware-acceleration","text":"Detection and video processing run in software on any CPU, but a GPU, accelerator, or Apple Silicon lowers CPU use significantly.\n\n| Platform | Acceleration | Notes |\n|---|---|---|\n| Apple Silicon | GPU + Neural Engine + VideoToolbox | macOS, via the desktop app |\n| Windows | Native GPU video acceleration | desktop app · x86-64 |\n| Intel | Quick Sync / VA-API (+ OpenCL) | intel Docker flavor · x86-64 |\n| NVIDIA | NVENC / NVDEC + CUDA | nvidia Docker flavor · x86-64 |\n| AMD | Mesa VA-API (+ OpenCL) | amd Docker flavor · x86-64 |\n| CPU only | software | cpu flavor · x86-64 + arm64 |\n| Coral Edge TPU | dedicated TPU | via the Coral plugin · Linux · x64 + arm64 |\n| Hailo-8 / Hailo-8L | dedicated NPU | via the Hailo plugin · Linux · x64 + arm64 |\n\nOn Linux, pick the Docker flavor that matches your GPU. On macOS and Windows, the desktop app uses your GPU for hardware-accelerated video automatically. Your AI detection plugin should match your hardware: CoreML on Apple Silicon, an Intel / NVIDIA backend, or a Coral / Hailo edge accelerator on Linux."},{"page":"intro/requirements","title":"System requirements","heading":"Storage for recordings","anchor":"storage-for-recordings","text":"How much space recordings need depends on your camera count, resolution and bitrate, recording mode, and how long you keep footage.\n\nUse a dedicated local disk for recordings, separate from the system disk and not a network share (NAS). Continuous recording writes constantly, and the recordings folder also holds the databases that index your events, which network shares cannot lock reliably. See Storage & retention.\n\ncamera.ui shows each camera's recording rate per day and lets you cap usage with the Quota and Retention settings."},{"page":"intro/requirements","title":"System requirements","heading":"Desktop app","anchor":"desktop-app","text":"The desktop app runs on macOS, Windows, and Linux. On macOS, version 15 or newer is recommended, since the operating system requires it for the app to reach cameras on your local network. In Server mode the desktop app runs the full server, so the requirements above apply; as a viewer it's lightweight."},{"page":"intro/requirements","title":"System requirements","heading":"Mobile apps","anchor":"mobile-apps","text":"The mobile apps are viewers with minimal requirements:\n\n- iOS 17.4 or newer\n- Android 8.0 or newer (API 26)"},{"page":"intro/requirements","title":"System requirements","heading":"Cameras","anchor":"cameras","text":"Almost any IP camera works: anything that provides an RTSP or ONVIF stream (H.264 or H.265). See Connection protocols for the full list.[^cloud]\n\n[^cloud]: Cameras that depend on a vendor cloud (such as Ring or Nest) can be connected, either directly or through a plugin, but they are not recommended and not covered by support. Use local RTSP / ONVIF cameras for a reliable experience."},{"page":"notifications/","title":"Notifications","heading":"Where alerts arrive","anchor":"where-alerts-arrive","text":"- In the app. Every alert lands in the in-app list (the bell), on every device.\n- Desktop app. While it's running, including in the tray, it shows native system notifications for the server it's connected to, except while its window is in front. No cloud or account needed.\n- Phone (push). The mobile app gets push even when it's closed, delivered end-to-end encrypted through camera.ui Cloud.[^license]"},{"page":"notifications/","title":"Notifications","heading":"Getting push on your phone","anchor":"getting-push-on-your-phone","text":"Phone push needs all of these. If one is missing, alerts only land in the in-app bell:\n\n1. The native app. Push only reaches the mobile app from the App Store or Play Store. A browser or the web app (PWA) shows the in-app bell, but never push.\n2. camera.ui Cloud. Push is delivered through camera.ui Cloud, so the server needs it enabled and paired to your account. It also needs an active subscription.\n3. On, and synced. In the app, open Settings → Notifications, turn the master switch on, and tap Sync this device once. Repeat on every phone, for every server you connect to.\n\nPush repairs itself after a reinstall. Reinstalling the app, clearing its data or a new phone resets the push token, and the app re-registers with the server in the background. If it can't (the phone's notification permission is off, say), a Push notifications stopped working note with a Fix button appears on any screen. Re-sync in Settings → Notifications does the same. The This device row only asks for a sync when the server really doesn't know this phone.\n\nMute from the push. Press and hold a camera push and pick Mute this camera for 1 hour or Mute all cameras for 1 hour. Both apply to this phone only, and alerts about camera.ui itself still come through."},{"page":"notifications/","title":"Notifications","heading":"Push stays private","anchor":"push-stays-private","text":"Your server seals every alert before it leaves your network. camera.ui Cloud, Google and Apple only pass the sealed envelope along; the camera name, the alert text and the link inside are unreadable to all three. The thumbnail takes no detour at all: your phone loads it straight from your server. When Video in Push is on, the clip is delivered the same way: it stays inside the sealed push and streams from your own server, so the cloud never sees a frame."},{"page":"notifications/","title":"Notifications","heading":"Your notification settings","anchor":"your-notification-settings","text":"Settings → Notifications belongs to your account and applies on every device you sign in on.[^device]\n\n- Master switch. All notifications on or off. Off for a new account, and the other sections only appear once it's on.\n- This device. On a phone, tap Sync this device once to start receiving push. In the desktop app, this switches native notifications on or off for that computer alone.\n- Quiet hours. Silence non-critical alerts during a daily window. Critical alerts still come through, and the in-app list stays current.\n- System notifications. Alerts about camera.ui itself: server and app updates, plugin updates, an update that failed, a plugin crashing or recovering, a worker with cameras or plugins that is offline for two minutes and comes back, remote access that is down for ten minutes and comes back,[^remote] and a scheduled backup that failed. Each one has its own switch.[^admin]\n- Plugin notifications. Alerts that plugins send, such as a camera going offline or storage filling up. Each plugin has its own switch. Tapping a camera offline or online alert opens that camera's log, where the reason for the outage is usually shown. The NVR's storage, license and cloud sign-in alerts open Settings → Recordings.\n- Devices. The phones and other targets your alerts go to. Rename one, switch Active off to pause it, or remove it to unregister it for good."},{"page":"notifications/","title":"Notifications","heading":"What each camera alerts you about","anchor":"what-each-camera-alerts-you-about","text":"Each camera has its own notification settings, in the camera's settings under Notifications:[^nvr]\n\n- Send notifications. The camera's own switch. Off means this camera never pushes, while its events still land on the timeline and in the app. On by default.\n- Which objects alert you. Set by the camera's alert zones, not here. Without an alert zone the camera alerts on everything it detects. Draw one to narrow it down by type and by area.\n- Which faces and plates alert you. Also set by the alert zones. A zone can name the people it pushes for, with entries for unknown faces and for anyone enrolled, and the same for plates. Nothing selected notifies for everyone.\n- Notify for sounds. Which sounds, such as alarms or glass breaking.\n- Notify for sensors. A doorbell, a contact sensor, a siren, or a security system.\n- Notification speed. How long to wait for a good picture. Immediate sends right away, with a picture only if one is ready. Balanced (the default) waits up to 2 seconds, Best up to 4. A recognized face always sends immediately, and the picture is updated quietly afterwards.\n- Cooldown (seconds). A quiet period after an alert. 30 seconds by default, up to 600. Doorbell and alarms bypass it.\n- Video in push. Attach a short clip of the moment to the push. On iPhone it plays right in the notification, on Android the expanded notification animates the clip frame by frame,[^androidframes] and the in-app alert opens with a player. Uses the lowest recorded quality. Off by default."},{"page":"notifications/","title":"Notifications","heading":"Pushes with an AI description","anchor":"pushes-with-an-ai-description","text":"With AI descriptions on, the alert carries what the AI saw instead of the bare \"person detected\".[^ai] This is Moment Pushes in the camera's settings, and the alert waits for the description. Doorbell rings and alarms still arrive immediately, and when the model does not answer in time the plain detection push goes out instead.\n\nPush Timing decides how often a busy event reaches you:\n\n- Per Moment. Each moment pushes as its description is ready. This is the default.\n- Per Event. The descriptions collect and one push goes out after the scene has been quiet for a while. Tapping it opens the beginning of the event.\n\nAn alert that is already on your lock screen is updated quietly when something better arrives, a recognized name or a sharper picture, without buzzing again."},{"page":"notifications/","title":"Notifications","heading":"Critical alerts","anchor":"critical-alerts","text":"Critical alerts, like a doorbell ring or an alarm, are delivered immediately and ignore both the cooldown and quiet hours.\n\n[^license]: Push notifications to the mobile apps require an active camera.ui subscription.\n[^device]: The This device row is the exception. It only affects the phone or computer you are on.\n[^admin]: Only admins see the system notifications section.\n[^remote]: camera.ui checks its connection to the camera.ui Cloud and its own Cloudflare tunnel. A custom domain behind your own proxy is not checked.\n[^nvr]: Sending the push is the NVR plugin's job, so a camera without one saves the settings and starts using them once a plugin is installed.\n[^ai]: AI descriptions come from the NVR plugin. Turn them on in its settings.\n[^androidframes]: Android notifications cannot play video, so the current app pulls frames from the clip and cycles them. Tapping still opens the recording."},{"page":"plugins/homekit","title":"HomeKit","heading":"HomeKit","anchor":"","text":"The HomeKit plugin adds your cameras and sensors to Apple Home. Cameras stream and record through the Home app across your Apple devices; sensors appear as accessories you can see and control there."},{"page":"plugins/homekit","title":"HomeKit","heading":"Setup","anchor":"setup","text":"1. Install and enable the HomeKit plugin.\n2. Assign it to the cameras you want in Home, on the plugin's Cameras tab or from a camera's Plugins tab.\n3. Open a camera's HomeKit settings. Each camera is added to Home individually and has its own QR code and PIN.\n4. In the Apple Home app, choose Add Accessory and scan the camera's QR code, or enter its PIN.\n\nTo pair on the device camera.ui is open on, choose More options in the Home app and type the PIN; the copy button next to it copies the PIN.\n\nHomeKit Secure Video recording works once you've set it up in Home."},{"page":"plugins/homekit","title":"HomeKit","heading":"Secure Video on iOS 27","anchor":"secure-video-on-ios-27","text":"Cameras with an HEVC (H.265) main stream use Apple's newer secure video services. Live view at home and away, recording and two-way audio all run on the camera's own HEVC stream without transcoding, so a 4K camera doesn't cost a CPU core. This needs iOS 27 or tvOS 27 on the devices you watch on and on the home hub.\n\nAway from home, a camera with a second source in Low Resolution or Mid Resolution also offers a small picture from it, transcoded on the server unless that source is HEVC already. The Home app switches to it on slow connections such as LTE. Without such a source, live view over LTE shows no picture.\n\nCameras with an H.264 main stream stay on the classic HomeKit camera services, because Apple's new remote view only takes HEVC. Any other codec, MJPEG for example, uses the classic services too, with the stream transcoded to H.264. The plugin picks the path from the main stream's codec when the camera is published.\n\nIf your home stays on iOS 26 or older, turn on Force legacy path under Secure Video in the camera's settings. The camera then uses the classic services and its HEVC stream is transcoded to H.264."},{"page":"plugins/homekit","title":"HomeKit","heading":"What ends up where","anchor":"what-ends-up-where","text":"A camera is always its own accessory, with a motion sensor on it. That motion sensor is fed by camera.ui's detection, so it works whether or not a motion sensor is assigned.\n\nWhat a sensor turns into depends on its camera assignment:\n\nAssigned to a camera, the sensor becomes part of that camera's accessory instead of a separate tile:\n\n- Motion feeds the camera's motion sensor and gets no accessory of its own.\n- Doorbell adds the doorbell button to the camera.\n- Battery adds the charge level and the low-battery warning.\n- Spotlight and siren become controls on the camera, as long as they are the camera's own hardware.\n\nNot assigned to a camera, the sensor becomes its own accessory behind the single camera.ui Bridge: contact, occupancy, smoke, leak, temperature, humidity, lock, garage door, switch and security system always land here, and so do motion sensors, doorbells, lights and sirens that belong to no camera. A standalone light or siren stays on the bridge even if you assign it to a camera; only hardware built into the camera moves onto it.\n\nThe bridge runs as soon as the plugin does, with or without sensors. Pair it once and every sensor you expose later joins automatically. Its QR code and PIN sit in the plugin's settings.\n\nWhether a sensor comes across at all is the Expose sensor toggle on the Sensors page. Camera hardware has no toggle, it always follows its camera."},{"page":"plugins/homekit","title":"HomeKit","heading":"Fast streaming","anchor":"fast-streaming","text":"Opening a camera in Home takes a few milliseconds, with H.264 or H.265, as long as the source is kept ready: enable hot mode and preload on the cameras you bridge.\n\nHot mode on the main stream also decides whether HomeKit Secure Video recordings include the seconds before an event. Without it the camera is not kept connected, a recording opens the stream when the event starts and has nothing from before. Plugins switch hot mode off for battery cameras, so they can sleep.\n\nThe picture in Home follows the camera's snapshot setting. While an event runs or the doorbell rings, HomeKit fetches a fresh one."},{"page":"plugins/homekit","title":"HomeKit","heading":"Settings","anchor":"settings","text":"Each camera's HomeKit settings include:\n\n- QR code and PIN. The pairing details for that camera.\n- Force legacy path. Under Secure Video: keeps the camera on the classic HomeKit services, see Secure Video on iOS 27. Off by default.\n- Allow transcoding for mobile data. Under Secure Video as well: lets camera.ui transcode a low or mid resolution source that is not HEVC, so remote view works over LTE. An HEVC source always goes out as it is. On by default; when off, such a camera may show no picture over LTE. The log says on each remote start which source goes out and whether it is transcoded.\n- Use Hardware Acceleration. Uses the GPU, if one is available, when a stream has to be transcoded. On by default.\n- Use Hardware Acceleration for HKSV. The same for HomeKit Secure Video recordings. On by default; turn it off for a camera whose recordings break up.\n- Override Port and mDNS advertiser. Under Advanced: a fixed port instead of an automatic one (0), and the backend that announces the camera on the network.\n- Reset Pairing. Generate a new code and re-add the camera if pairing breaks.\n\nThe plugin's own settings hold the same set for the sensor bridge: its QR code, PIN, port, mDNS advertiser, and a Reset Bridge Pairing button."},{"page":"plugins/homekit","title":"HomeKit","heading":"Good to know","anchor":"good-to-know","text":"- Each camera is its own HomeKit accessory, so you add cameras one at a time. Standalone sensors share the one bridge and only need a single pairing.\n- Cameras that support two-way audio can be talked to from the Home app.\n- Cameras and the bridge listen on the Server addresses under Settings → Remote Access → Network, or for a plugin on a worker on the addresses set with Edit Worker (see Worker assignments). With none set they listen on all of them; if streams stutter or fail, pick the wired LAN address. An address that does not exist on the machine is skipped, and the log names it with the addresses it found.\n- The pairings are kept in the plugin's settings on the main server, so cameras and the bridge stay paired when the plugin moves to a worker or back.\n- A backup restored as a Copy (see Restoring) gives the bridge and the cameras new pairing codes, so they don't answer for the original server. Add them to Home again on the copy. A Move keeps the pairings."},{"page":"plugins/","title":"Plugins","heading":"Plugins","anchor":"","text":"Most of what camera.ui does beyond live view comes from plugins: the AI backends and motion engines behind detection, audio detection, smart-home bridges like HomeKit, and camera-brand integrations. Managing plugins is an admin task."},{"page":"plugins/","title":"Plugins","heading":"Two things, kept separate","anchor":"two-things-kept-separate","text":"- Installing a plugin adds it to your server, once. This page covers that.\n- Assigning a plugin to a camera decides which camera uses it, for example which motion engine a camera runs. That's done per camera, in Set up sensors."},{"page":"plugins/","title":"Plugins","heading":"Managing plugins","anchor":"managing-plugins","text":"Plugins lists everything installed, each card with a live status (ready, started, stopped, error). From a card you can:\n\n- Enable or disable it, and start, stop, or restart its process.\n- Update it: the update button starts right away, click it again while it runs for the live install output. For release notes or a specific version, use the card menu.\n- Open its settings, or view its logs.\n- Uninstall it, optionally removing its stored data.\n\nRecordings are the exception: if you delete a recording plugin's stored data, its footage is kept.\n\nUpdate all in the toolbar updates every outdated plugin. While an update run from the Updates page is going on, plugins are stopped and can't be managed here.\n\nThe gear button next to the search bar holds two settings, both off by default:\n\n- Beta versions. Shows pre-release versions in the version picker and offers beta releases as updates.\n- Allow plugin build scripts. Lets plugins run install scripts and native builds while installing or updating. Only turn this on for plugins you trust.\n\nThe button next to the plus switches to a compact table view with status, version and actions per row; your choice is remembered.\n\nInstalls, updates and uninstalls run through a server-side queue, two at a time. Every open browser sees the queue, whoever started it.\n\nEach plugin runs in its own process, so a crashing plugin doesn't take down the rest. A badge on the card shows its language (Node, Python or Go). A plugin built for a different plugin API doesn't start: its card says whether the plugin or camera.ui needs an update, and you get a notification."},{"page":"plugins/","title":"Plugins","heading":"Managing several at once","anchor":"managing-several-at-once","text":"The select button works in both views: update, enable, disable or uninstall several plugins in one step."},{"page":"plugins/","title":"Plugins","heading":"Installing a plugin","anchor":"installing-a-plugin","text":"Search plugins opens the store. The list comes from npm, so any published camera.ui plugin can appear; camera.ui curates on top of it:\n\n- Trust. Each plugin is marked Official (published by camera.ui), Verified (a community plugin the camera.ui team has reviewed), or Community (published by anyone, not reviewed). You can filter by trust level.\n- Safety. Plugins known to be malicious or compromised are blocked: they don't show up, can't be installed, and one you already have is disabled automatically if it's later blocked.\n- Compatibility. The store flags a plugin that needs a newer camera.ui, and installing a version that can't run on your server is refused with the reason.\n- Browsing. Filter by category, start from the featured plugins, and sort by name, weekly downloads, or last update.\n\nA specific version can be picked at install. Afterwards, enable the plugin and assign it where it's needed."},{"page":"plugins/","title":"Plugins","heading":"Private registry or mirror","anchor":"private-registry-or-mirror","text":"Plugins come from whichever npm registry your .npmrc points at, so a company mirror or a private registry works for the store, for installs and for a plugin's own dependencies. Put the file in the home directory of the user that runs camera.ui:\n\nregistry=https://npm.example.com/\n//npm.example.com/:_authToken=\n\n- One registry for everything with registry=, or a single scope with @example:registry=https://npm.example.com/.\n- Login with //npm.example.com/:_authToken=, or basic auth through //npm.example.com/:username= plus //npm.example.com/:_password=. Credentials are only sent to the host and path they are written for.\n- Token from the environment. CAMERAUI_NPM_TOKEN (or NPM_TOKEN) replaces the token line. It applies to the registry from registry=, so a scope with its own registry keeps its token in .npmrc.\n- Docker: mount the file to /root/.npmrc. The home directory is not part of the /data volume.\n- Linux service: the home directory of the service user. Desktop app: the home directory of the logged-in user.\n- Remote workers install plugins themselves, so each one needs the same file or the same variable.\n\nThe store lists packages named camera-ui-* or @scope/camera-ui-* that carry the keyword camera-ui-plugin. Registries without a search API, GitHub Packages for example, return no list: type the exact package name instead, and the store looks it up directly."},{"page":"plugins/","title":"Plugins","heading":"Settings","anchor":"settings","text":"A plugin's own settings live on its page, under Settings, and apply server-wide. Settings that affect a single camera are edited in that camera's settings instead. Recording-plugin settings are kept with the rest of recording."},{"page":"plugins/","title":"Plugins","heading":"Setup guides","anchor":"setup-guides","text":"- HomeKit — add your cameras to Apple Home.\n- Home Assistant — cameras, sensors and detection events as Home Assistant entities.\n- Google Home and Alexa — not available yet, both are on the roadmap."},{"page":"recording/browsing","title":"Browsing & filters","heading":"Browsing & filters","anchor":"","text":"The Recordings view lists past events across your cameras, ordered by when something last happened in them. An event that runs for hours shows while it runs, with its newest span on the card. Related events across cameras can appear as an episode card, and a recording that belongs to one shows Open episode. See Episodes.\n\nWhen the list says recordings are unavailable, the NVR plugin is not running. The list fills once the plugin answers again."},{"page":"recording/browsing","title":"Browsing & filters","heading":"Filtering","anchor":"filtering","text":"Show at the top of the sidebar picks what the list holds: All, only Events, or only Episodes. Episodes have no detections of their own, so in that view only camera, time range, Only with recording and Favorites only narrow them down.\n\nThe other filters:\n\n- Camera and time range.\n- Rooms. Selects the cameras placed in those rooms on the floor plan.\n- Labels. Person, vehicle, animal, or other.\n- Trigger. Motion, a doorbell, a sensor, or an audio alarm.\n- Attributes. Events that contain a face or a license plate.\n- Vehicles. Color and type of a vehicle, read from the picture. See Find an object.\n- Confidence. Hide low-confidence detections.\n- Grid search. With a single camera selected, draw a region on its snapshot to only show events detected inside that part of the frame."},{"page":"recording/browsing","title":"Browsing & filters","heading":"View options","anchor":"view-options","text":"Behind the gear, top right:\n\n- Ungroup. One card per moment instead of one card per event. Only events with several moments split, most have one. Episodes stay, pick Events under Show to hide them. Off by default. Your choice is saved to your account, so it follows you to other devices.\n- Only with recording. On by default. Turn it off to also see events where a trigger fired but nothing was recorded, and episodes from times without footage. Those can't be downloaded or previewed.\n- Favorites only. Admins only, see below."},{"page":"recording/browsing","title":"Browsing & filters","heading":"Reading a card","anchor":"reading-a-card","text":"Each card is one moment.\n\n- Time and camera. Selecting the card opens playback at that moment.\n- Faces and plates. When a moment also has a recognized face or a read plate, arrows step through its pictures in place, one per person or plate, with the name or plate shown. Someone the camera keeps losing and finding again still gets one picture. The small tiles at the bottom jump to one. With several people in one moment, the main picture lists every name and counts the unknown faces, like Azra +1.\n- Grouped cards. A grouped card shows the event's other moments as small previews at the bottom. Each opens its own moment.\n- Split cards. With Ungroup on, each card shows its place in the event, like 2/3. Hovering one highlights the other cards of the same event.\n- The dots menu. Holds what the card does not show itself: the detection trace (admins only), Ask the assistant, the download, Correct person (admins only) when a face picture is on screen, and Delete event and Delete recording (admins only, see below).\n- Hold to preview. On a phone or tablet, hold a card to play its short preview (lift to stop, tap to open). On a computer it plays on hover. The preview is zoomed to what the moment was about, with a clock for the frame's time. No preview means there is no playable footage, or it did not load in time. An episode card previews its cut: each camera in turn, with name and time."},{"page":"recording/browsing","title":"Browsing & filters","heading":"Favorites","anchor":"favorites","text":"The star on a recording or episode card keeps that moment: automatic cleanup skips it, so its footage stays playable however old it gets and however tight the disk. Favorites only in the view options lists them. Both are admin-only."},{"page":"recording/browsing","title":"Browsing & filters","heading":"Selecting and deleting","anchor":"selecting-and-deleting","text":"Admins can delete events here: Delete event in a card's dots menu removes that event, the dots button in the corner enters select mode for several or all loaded ones. Deleted events disappear everywhere at once: Home, the timeline, this list, and every open browser.[^delete-disk]\n\nDelete recording removes the event and its video right away. It works like deleting a range on the timeline: per minute, so other events in those minutes go too, and the most recent minutes can't be deleted yet."},{"page":"recording/browsing","title":"Browsing & filters","heading":"Search","anchor":"search","text":"The search in the filter sidebar has one field, and the buttons above it switch what it searches. A running search stays when you switch.\n\n- Search. Labels, names, plates and the text of AI descriptions, as you type.\n- AI Search. Moments by description, like \"a person carrying a package\". See Semantic search.\n- Ask the assistant. With the assistant set up. Type the search in your own words (\"cars in the driveway yesterday\", \"favorites with the doorbell\", \"episodes from the entrance in the last hour\") and it sets the filters below: cameras and rooms, time range, event types, vehicle color and type, sensors, favorites, and an AI Search when the words describe a scene the labels cannot. A note under the box says what the filters cannot express, an exact day for example.\n- Search by picture. Admins only. A picture of a person or a vehicle, see Find an object."},{"page":"recording/browsing","title":"Browsing & filters","heading":"Heatmap","anchor":"heatmap","text":"A heatmap shows where activity was concentrated over a period. See Object detection.\n\n[^delete-disk]: Deleting an event removes it from the list and its detections. The video file is left in place and removed later by the storage cleanup on its normal schedule."},{"page":"recording/export","title":"Export & download","heading":"Export a clip","anchor":"export-a-clip","text":"On the timeline, drag the trim handles to the start and end, then export an MP4. The clip is saved in the quality you are watching, so a lower quality gives a smaller file."},{"page":"recording/export","title":"Export & download","heading":"Export multiple recordings","anchor":"export-multiple-recordings","text":"The download button in the Recordings view opens Export recordings: one or more cameras, a time range across days, a quality (Best quality or Smallest files), and optionally a timelapse interval. You get one MP4 per camera per day (first and last day cut to the selected range, days in between in full) and a size estimate up front. Several files come as one ZIP, a single file as a plain MP4. The dialog's quality choice is independent of what you are watching.\n\nBoth exports convert the recordings of an MJPEG camera to H.264. That takes longer, and the files end up well below the dialog's size estimate."},{"page":"recording/export","title":"Export & download","heading":"Timelapse","anchor":"timelapse","text":"Both export flows can produce a timelapse instead, sampling frames at an interval to compress a long period into a short clip."},{"page":"recording/","title":"Recording (NVR)","heading":"Recording (NVR)","anchor":"","text":"Recording and everything in this section need an active camera.ui subscription.[^license]\n\nPreview phase\nSubscriptions aren't open yet. While camera.ui is in preview, recording is free for everyone with a camera.ui account, no card needed: create one at cloud.cameraui.com and follow the steps below. If the license does not activate, ask on our Discord."},{"page":"recording/","title":"Recording (NVR)","heading":"Turning on recording","anchor":"turning-on-recording","text":"Recording comes from the camera.ui NVR plugin, which is installed by default, and runs on a subscription tied to your camera.ui account. To turn it on:\n\n1. In Settings → Remote Access, choose Register server and sign in with your camera.ui account. This links the server to your account; you don't need to enable remote cloud access for it.\n2. Open Settings → Recordings and, in the License & Cloud card, click the cloud icon and sign in.\n3. Your license then activates automatically. If the status doesn't update, use the Refetch License button.\n\nIf signing in ends with Server limit reached for your plan, the license is still held by another server, an earlier test install for example. Remove that server in the cloud dashboard and sign in again.\n\nWith an active license, new cameras start recording on their own while a camera slot is free: continuously, in every stream quality. A battery camera that its plugin reports as one starts in Ad-hoc instead. Mode, qualities and on/off are in the camera's recording settings.\n\nRecording takes H.264, H.265 and MJPEG. An MJPEG camera records its pictures as it sends them, which needs several times the storage of H.264, and a picture wider or taller than 2040 pixels is cropped to 2040 around its center. A camera that sends another codec records nothing, and its timeline shows a recording error that says so. Live view shows how to transcode such a camera on the server."},{"page":"recording/","title":"Recording (NVR)","heading":"Camera slots","anchor":"camera-slots","text":"The License & Cloud card in Settings → Recordings shows how many recording slots your plan includes, how many cameras are active, and how many currently use a slot.\n\nSlots go to your enabled cameras in order. A camera past the limit keeps detecting, its events and its live view, but does not record and shows no warning, so check that card if footage is missing from one camera. Turning off recording on a camera frees its slot for another.\n\nIf your subscription lapses, recording stops and playback, export, face recognition and search ask for a valid license. Nothing on disk is deleted, it all comes back when the license is valid again.\n\nWithout internet the license keeps working. For the first 14 days without a connection to cameraui.com nothing changes. After that, playback, export, face recognition and search pause, while recording carries on until the paid period ends, plus 14 days for a subscription that renews. Then recording stops as well. Settings → Recordings shows the date, and admins get a notification three days before. One connection brings everything back. For a server that stays offline for good, the yearly plan fits best."},{"page":"recording/","title":"Recording (NVR)","heading":"Recording modes","anchor":"recording-modes","text":"Each camera records in one of three modes, set in its recording settings:\n\n- Continuous. Records around the clock. Uses the most storage.\n- Event. Records only when something happens (motion, an object, a sensor). A pre-buffer keeps a few seconds before each trigger.\n- Ad-hoc. Connects to the camera only when an event fires. For cameras that stop sending events while a stream is open, such as some battery cameras."},{"page":"recording/","title":"Recording (NVR)","heading":"Quality","anchor":"quality","text":"Record one or more stream qualities: high, mid, low. Also recording the low stream makes scrubbing the timeline fast."},{"page":"recording/","title":"Recording (NVR)","heading":"Next","anchor":"next","text":"- Storage & retention — where recordings live and how long they're kept.\n- Timeline & playback — review footage.\n- Export & download — save a clip.\n- Browsing & filters — find past events.\n\n[^license]: Recording (NVR) requires an active camera.ui subscription."},{"page":"recording/storage","title":"Storage & retention","heading":"Where recordings are stored","anchor":"where-recordings-are-stored","text":"By default, recordings live with the rest of camera.ui's data. For more than a couple of cameras, put them on a dedicated local disk, separate from the system disk. See System requirements.\n\nA local disk, or a symlink to one, is fine. A network share (NAS, SMB, NFS) is not: detection events, timeline markers and the search index sit next to the footage as databases, and those need file locking that network filesystems do not provide reliably. What breaks there is the event database, not the video. If your storage lives on a NAS, run camera.ui on the NAS and record locally there."},{"page":"recording/storage","title":"Storage & retention","heading":"Moving recordings to another disk","anchor":"moving-recordings-to-another-disk","text":"Changing the storage path does not move your recordings: camera.ui starts fresh at the new location and the old folder stays as it is. A new path that camera.ui can't create or write to is not taken, recording stays in the old folder and a notification says why. To move the data:\n\n1. Stop the server.\n2. Move the whole storage folder to the new disk, including the events and clip folders. They hold your events, timeline markers and search index for that footage.\n3. Start the server and set the new path in Settings → Recordings.\n\nNothing inside stores an absolute path, so everything works at the new location without a rebuild. Without events and clip the footage arrives bare, and they cannot be recreated from the video files. Enrolled faces live outside the recordings folder and are not affected."},{"page":"recording/storage","title":"Storage & retention","heading":"Rough sizing","anchor":"rough-sizing","text":"Continuous recording size follows the stream's bitrate:\n\nGB per day, per camera ≈ bitrate in Mbit/s × 10.8\n\n| Stream | Bitrate | Continuous, per day | Per camera, 7 days |\n|---|---|---|---|\n| 1080p H.264 | 4 Mbit/s | ~43 GB | ~300 GB |\n| 4K H.264 | 8 Mbit/s | ~86 GB | ~600 GB |\n| H.265 (HEVC) | about half the bitrate | roughly half the above | roughly half the above |\n| 720p MJPEG, 15 fps | ~4 Mbit/s | ~40 GB | ~280 GB |\n\nMJPEG has no fixed bitrate, it follows the camera's resolution, frame rate and JPEG quality.\n\nEvent recording uses a fraction of that, depending on how busy the scene is. When in doubt, set a retention window or storage cap and watch the per-camera rate (below) for a week."},{"page":"recording/storage","title":"Storage & retention","heading":"How long they're kept","anchor":"how-long-theyre-kept","text":"- Retention (Days). Keep footage for up to this many days (0 means no day limit).\n- Max Storage (GB). Cap the total space recordings may use (0 means no cap).\n\nWhen a limit is reached, the Retention Mode decides:\n\n- Overwrite (default). The oldest recordings are deleted to make room. Recording only pauses when other data fills the disk and no older recordings are left to delete.\n- Strict. Nothing is deleted beyond the retention window. If the disk fills, recording pauses until you free space.\n\nCleanup takes the oldest hours of all cameras side by side, so a quiet camera cannot lose its whole history while a busy one keeps everything, and it stops once enough space is free. Favorites are skipped and stay playable however old they are."},{"page":"recording/storage","title":"Storage & retention","heading":"Free disk space","anchor":"free-disk-space","text":"Independent of those limits, Min Free Space (GB) keeps part of the disk clear: cleanup works to keep at least that much available. Below half of it cleanup deletes faster, and recording pauses there with Strict, or with Overwrite once nothing is left to delete. At 0 the reserve is a share of the whole volume.\n\nSet your own value when the recordings share a disk with other data, otherwise the derived reserve on a large disk can delete recordings, or with Strict pause recording, long before your storage cap is reached. Values below 10 GB count as 10, or as a quarter of the disk if that is less."},{"page":"recording/storage","title":"Storage & retention","heading":"Footage from removed cameras","anchor":"footage-from-removed-cameras","text":"If you delete a camera or unassign the NVR from it, its recordings stay on disk. They still count towards Max Storage and are aged out and cleaned up like an active camera's.\n\nTo keep that footage, put an empty file named .cameraui-keep into the camera's folder in your recordings directory. camera.ui then leaves the folder alone: not counted, not aged out, not touched when space runs low.[^keepmarker]\n\n[^keepmarker]: The marker only applies to cameras camera.ui no longer knows. The folder of an active camera follows retention and the storage cap either way."},{"page":"recording/storage","title":"Storage & retention","heading":"Keeping an eye on usage","anchor":"keeping-an-eye-on-usage","text":"Metrics → Storage shows disk usage in the Storage Overview and, in the Camera Storage table, per camera: size on disk, days of footage held, recording rate per day, and mode.\n\nA warning that the storage volume is small means it has little room for recordings, so they rotate out quickly. Usually the larger disk you meant to use isn't mounted, so check the storage path. Separate warnings appear when free space drops below 8% and once recording has paused for lack of space.\n\nIf a custom storage path held recordings before and its disk is missing at startup, camera.ui records into its default folder instead of the empty mount point until the next start, and marks this on the timeline. If the disk drops out while camera.ui records, recording pauses, admins get one notification, and recording resumes by itself once the path is back."},{"page":"recording/storage","title":"Storage & retention","heading":"Uninstalling the NVR","anchor":"uninstalling-the-nvr","text":"Uninstalling the NVR plugin with Also delete stored data removes its settings, databases and caches but keeps your recordings folder, which is protected by a .cameraui-keep file inside it. To delete the recordings too, remove that file first, then uninstall."},{"page":"recording/timeline-playback","title":"Timeline & playback","heading":"Timeline & playback","anchor":"","text":"Each camera's timeline scrubs through its recorded footage, with marks where events happened."},{"page":"recording/timeline-playback","title":"Timeline & playback","heading":"Reviewing footage","anchor":"reviewing-footage","text":"- Scrub along the timeline. Scrubbing uses the low-resolution stream.\n- Play and pause, skip 30 seconds back or forward, and change the speed between 0.25x and 8x.\n- Find an object. Admins only. Open Find, tap a person or a vehicle in the picture, and get the moments that show it. See Find an object.\n- Event marks show a thumbnail. Click one to jump to that moment.\n- Rings and alarms. A doorbell press, siren, security system or contact event shows as a full event bar with its own icon and a snapshot, even when the camera saw nothing. A ring during a running motion event appears right away, not after the motion ends.\n- Filter by event type. The filter button picks which event types show on the timeline and in the detections list. The choice is remembered.\n- Filter by zone. In the filter, turn on Filter by zone and mark areas on the camera picture to keep only events detected inside them.\n- Zoom from an overview down to seconds. At the deepest level scrubbing is frame-accurate in the playback quality and the timeline shows a Fine Scrubbing badge. When a full picture takes too long to load, dragging shows the low-resolution stream and the picture turns sharp when you let go.\n- Jump to a day with the calendar. Days with footage are highlighted."},{"page":"recording/timeline-playback","title":"Timeline & playback","heading":"Playback quality","anchor":"playback-quality","text":"Each camera has a Playback source in its settings, under Interface: Auto, High, Mid or Low. Auto is the default and the one to keep.\n\nA recording can have a hole in one quality and not in another, for example when the high stream dropped out while the low one kept going. On Auto, playback takes the best quality that has footage at that spot, with a badge naming it while it is not the top one, and switches back once the high recording resumes. A pinned quality stays put, hole included."},{"page":"recording/timeline-playback","title":"Timeline & playback","heading":"When a camera was not recording","anchor":"when-a-camera-was-not-recording","text":"Bands under the timeline say what the camera was doing when there is no footage:\n\n- offline means the camera recorded nothing at all.\n- recording without high (or another quality) means only that stream stopped. There is still footage, just not in every quality, and Auto plays it.\n\nA band spans exactly from where the recording stopped to where it resumed. Blips shorter than 15 seconds paint no band.\n\nWhen an outage has phases (the high stream goes first, the rest follows, one comes back before the other), they sit side by side in one band, each in its own color, labeled with the total duration. Hover it to read the phases."},{"page":"recording/timeline-playback","title":"Timeline & playback","heading":"Timeline, detections and find","anchor":"timeline-detections-and-find","text":"The panel switches between its views: Timeline, Detections and, for admins with the NVR plugin, Find for the object search, with the number of matches next to it. Detections lists what was detected: day, time, duration, a preview image and icons for the detected types. Click an entry to jump to it.\n\nAbove the vertical timeline the views are a switch, in the strip below the picture icons in its top row. A narrow strip shows only the icon of the current view, tap it for the others. On a phone, dots at the bottom of the timeline show the view, tap one or swipe sideways."},{"page":"recording/timeline-playback","title":"Timeline & playback","heading":"Several cameras at once","anchor":"several-cameras-at-once","text":"In Camview, the timeline follows the camera you open and shows its recordings and events. Only that camera decodes, so scrubbing stays fast in a busy layout. The event filter can show the other cameras' events too, and clicking one jumps to it."},{"page":"recording/timeline-playback","title":"Timeline & playback","heading":"Trimming","anchor":"trimming","text":"Export on the timeline turns on trimming: drag the handles to set start and end, then export the selection. See Export & download."},{"page":"recording/timeline-playback","title":"Timeline & playback","heading":"Deleting a range","anchor":"deleting-a-range","text":"Admins can delete footage from the timeline: Delete recordings, drag the handles over the span, then Delete selection. The footage and every event inside it are removed for good, per minute, on every open device. The most recent minutes are still being written and can't be deleted yet."},{"page":"remote/certificate","title":"Certificate","heading":"Certificate","anchor":"","text":"camera.ui generates its own certificate on first start and serves it on every connection. Browsers do not know the issuer, so they warn. For your own domain you can upload a certificate you obtained yourself, no reverse proxy needed."},{"page":"remote/certificate","title":"Certificate","heading":"What the built-in certificate is for","anchor":"what-the-built-in-certificate-is-for","text":"The mobile apps, the Apple TV app and remote workers verify the server against camera.ui's own certificate authority. They reach the server by IP address, and no public issuer signs certificates for private addresses, so that part keeps running on the internal certificate no matter what you upload.\n\nYour certificate is served only when a client asks for a name it covers. Everything else, including every connection to an IP address, still gets the internal one.\n\nThe internal certificate covers 127.0.0.1, the machine's private network addresses, and whatever you picked under Settings → Remote Access → Network: the server addresses and the local address. A public address is only included when you select it there. Changing those reissues the certificate, so a browser exception stored for the old one no longer applies and the warning returns. Devices that installed the downloaded certificate keep trusting the server, because the certificate authority stays the same until a backup restore."},{"page":"remote/certificate","title":"Certificate","heading":"Uploading your own","anchor":"uploading-your-own","text":"Settings → System → Certificate → Upload. Uploading and removing is limited to the master account, and the desktop app has no such card.\n\n- Certificate. Your certificate including the intermediates above it. Let's Encrypt writes this as fullchain.pem. Commercial issuers often send the certificate and the chain as two files, then add the second one in the chain field.\n- Private key. privkey.pem, or the .key your issuer sent. It must not be protected by a passphrase.\n- Chain. Only needed when your issuer ships the intermediates separately.\n\nThe upload is checked before anything is stored: the files have to parse, the key has to belong to the certificate, and it has to be valid right now. Afterwards the card lists the names it covers, who issued it and how long it is valid.\n\nA certificate that holds no intermediates is accepted with a warning. From a public issuer that means the chain is missing: desktop browsers fetch it themselves and look fine, phones refuse the connection. From a local certificate authority that signs directly there is nothing to add.\n\ncamera.ui uses the new certificate immediately. The streaming engine keeps its own copy and offers a restart afterwards, which interrupts running streams for a moment."},{"page":"remote/certificate","title":"Certificate","heading":"Renewal","anchor":"renewal","text":"Uploaded files live in certs/custom/ inside the storage directory, as cert.pem, key.pem and chain.pem. camera.ui watches them, so a replaced file is picked up without a restart.\n\nFor automatic renewal, write to that directory:\n\n- Docker. Mount your certificate directory over certs/custom, read-only.\n- Bare metal. A certbot deploy hook that copies fullchain.pem and privkey.pem there under those names.\n\nAn upload through the interface has to be repeated at every renewal. Let's Encrypt certificates are valid for 90 days."},{"page":"remote/certificate","title":"Certificate","heading":"Removing it","anchor":"removing-it","text":"Settings → System → Certificate → Remove. Every name goes back to the internal certificate."},{"page":"remote/certificate","title":"Certificate","heading":"Next steps","anchor":"next-steps","text":"Reverse proxy — terminate TLS in front of camera.ui instead, useful when you already run one.\nCustom domain — reach your server under your own name from outside."},{"page":"remote/cloud","title":"camera.ui Cloud","heading":"camera.ui Cloud","anchor":"","text":"camera.ui Cloud lets you reach your server from anywhere without touching your router. Your server registers with the cloud, and you connect through your camera.ui account. It's free and separate from the recording subscription."},{"page":"remote/cloud","title":"camera.ui Cloud","heading":"Your server stays yours","anchor":"your-server-stays-yours","text":"camera.ui Cloud is a way to reach your own server, not a copy of it. Your cameras, recordings, settings, and the interface all stay on your server. The cloud's only job is to help your app and your server connect, and your session then runs directly to your own instance. Your video, credentials, and other data go straight to your server and are never visible to camera.ui.\n\nPush notifications are the one thing that does travel through the cloud, and they are end-to-end encrypted: your server seals every alert, only your phone can unlock it, and the cloud forwards the envelope without storing anything. See Notifications."},{"page":"remote/cloud","title":"camera.ui Cloud","heading":"Enabling it","anchor":"enabling-it","text":"In Settings → Remote Access, open the Cloud card and turn on Enabled. The server's name is how it appears in your cloud dashboard.\n\nThe status row shows whether the server is Registered, and a tunnel indicator shows the live connection. To connect a server, choose Register server and complete the pairing. If the status shows Re-authentication required, the server lost its cloud credentials: choose Reconnect and pair again. A registered server can be taken off your account with Unregister server. The same flow on a phone is covered in Pairing mobile apps."},{"page":"remote/cloud","title":"camera.ui Cloud","heading":"Cloud keeps direct access on","anchor":"cloud-keeps-direct-access-on","text":"While Cloud is on, your server also keeps a public address for the apps, so the Enabled switch in the Direct connection card is locked on and External URL shows the address in use.\n\nIf you haven't set up a direct address, or the one you configured can't be reached, camera.ui starts a temporary Cloudflare tunnel instead. To use your own address, set it up under Direct connection.\n\nThe fallback is not permanent. camera.ui keeps checking your own address and goes back to it once it answers, a custom domain every minute and a Cloudflare tunnel after ten minutes, then less often. No restart needed. While the fallback is up, External URL shows the temporary address, and a Cloudflare retry that fails again hands out a new one."},{"page":"remote/cloud","title":"camera.ui Cloud","heading":"The cloud dashboard","anchor":"the-cloud-dashboard","text":"The dashboard lists your registered servers by name, and you pick one to connect to. It is at cloud.cameraui.com in a browser and is the cloud server list in the mobile apps. You can share a server with another camera.ui account by invite, who then signs in with their own login.\n\nThe bin icon next to a server removes it from your account, also when you can no longer reach the server itself. That frees its place in your plan, so a new server that stopped at Server limit reached for your plan can connect afterwards. On a server shared with you, the bin only ends your own access."},{"page":"remote/cloud","title":"camera.ui Cloud","heading":"Turning it off","anchor":"turning-it-off","text":"Disabling cloud access ends any active remote sessions and stops share links from working. Local connections on your network keep working."},{"page":"remote/cloudflare","title":"Cloudflare tunnel","heading":"Cloudflare tunnel","anchor":"","text":"A Cloudflare tunnel gives your server a public address through Cloudflare's network, without opening ports on your router. In Settings → Remote Access, set the Direct connection method to Cloudflare, then pick a variant:\n\n- Quick Tunnel. A temporary address with no Cloudflare account needed. Good for a quick test. The address changes each time, and from about four people watching remotely at once a named tunnel holds up better.\n- Named Tunnel (Token). Use your own domain. Create a tunnel in your Cloudflare dashboard and give it a public hostname that points at your server, for example https://192.168.1.10:3443. The server uses a self-signed certificate, so turn on No TLS Verify in the tunnel's origin settings. Then paste Hostname and Tunnel Token here. Your domain must already be a zone in your Cloudflare account.\n- Named Tunnel (OAuth). Use your own domain, with camera.ui doing the setup. Enter a hostname, choose Connect Cloudflare, sign in to authorise your zone, and camera.ui creates the tunnel and DNS record for you.\n\nWith cloud access on, camera.ui keeps a public address up at all times. If your named tunnel stops connecting, it starts a Quick Tunnel so the server stays reachable and tries your tunnel again after ten minutes, then after twice the wait each time, up to every four hours. Saving the settings under Remote Access starts over at ten minutes. Each failed retry leaves you on a new Quick Tunnel address, and the apps need a moment to find the server again.\n\nEvery variant connects out to Cloudflare over TCP on port 7844. Behind a firewall that filters outgoing traffic, allow TCP on that port.\n\nTo use QUIC instead, add cloudflaredProtocol: quic to camera.ui.yaml in your storage directory and restart the server. In Docker, CAMERA_UI_CLOUDFLARED_PROTOCOL under environment: does the same. quic needs UDP on port 7844, auto tries QUIC and falls back to TCP. Over QUIC, a few stalled viewers, a locked phone with live cameras open for example, can freeze the tunnel for everyone."},{"page":"remote/custom-domain","title":"Custom domain","heading":"Custom domain","anchor":"","text":"In Settings → Remote Access, set the direct connection method to Custom domain and enter the URL where the server is reachable, for example https://nvr.example.com.\n\nThe server must already be reachable at that address through your own DNS and a reverse proxy or port forwarding: camera.ui only records the address, it does not change your network. Forwarding a port sends traffic to 3443, and the server answers it with its own self-signed certificate, so browsers and apps show a warning. A reverse proxy with a real certificate avoids that, and Certificate covers uploading one to camera.ui itself. Reverse proxy covers what a proxy has to forward.\n\nThat one port carries the interface, the API and MSE playback. WebRTC media travels separately, over port 2004 (UDP, with TCP on the same port as fallback), so forward that too for the low-latency path from outside. Without it the live view falls back to MSE.\n\nTest connection checks the saved address, so Save first. It requests the address from the server itself, which fails on routers without hairpin NAT or with split DNS even when the address works fine from outside. Remote access keeps running either way, camera.ui only gives up a custom domain when the name stops resolving publicly.\n\nIf you don't run your own domain or reverse proxy, camera.ui Cloud or a Cloudflare tunnel is simpler, since neither needs router changes."},{"page":"remote/","title":"Remote access","heading":"Remote access","anchor":"","text":"Without remote access, camera.ui is reachable on your local network only. Remote access is free and doesn't need the recording subscription.[^free]\n\nRemote access is an admin setting, under Settings → Remote Access. The page shows your current connection status (local, local network, remote, or cloud) and the address you're connected on. The info button next to it opens Connection details: the address this app is using, what the server sees from its side, and every address the app tried in its last attempt with the time it took and why it failed. Copy details copies all of it for a support message. The same page holds the Network card, where you pick which of the server's IP addresses apps may use and can give it a Local address on your own network. Its list holds VPN addresses such as WireGuard too, but leaves out container and VM networks.\n\nOn connecting, and whenever the path changes (you get home and the app leaves the internet path, say), a short notice at the bottom says Connected via local network, Connected via internet or Connected via cloud."},{"page":"remote/","title":"Remote access","heading":"Choosing how to connect","anchor":"choosing-how-to-connect","text":"- camera.ui Cloud. Register your server and reach it from anywhere, no router setup or port forwarding. Recommended for most people, and required for the mobile apps when away from home.\n- Direct connection. Expose your server with a public address of your own, either through a Cloudflare tunnel or your own domain.\n\nYou can also keep access local only by leaving remote access off.\n\n[^free]: Remote access and camera sharing are included at no cost. The subscription covers recording (NVR), not remote access."},{"page":"remote/pairing","title":"Pairing mobile apps","heading":"Pairing mobile apps","anchor":"","text":"The mobile apps connect to your server through camera.ui Cloud. You pair the app with your server once."},{"page":"remote/pairing","title":"Pairing mobile apps","heading":"Pairing","anchor":"pairing","text":"1. Make sure camera.ui Cloud is enabled on the server.\n2. On the server, open Settings → Remote Access and choose Register server. A QR code, a pair code, and a pair URL appear.\n3. Scan the QR code with your phone's camera, or open the pair URL in a browser, and sign in to your camera.ui account. The code is valid for 10 minutes.\n4. Press Connect. The server appears in your account."},{"page":"remote/pairing","title":"Pairing mobile apps","heading":"Connecting","anchor":"connecting","text":"Paired servers appear in the app's cloud server list. Pick one to connect, and switch between them in the app."},{"page":"remote/proxy-sign-in","title":"Sign-in through a proxy","heading":"Sign-in through a proxy","anchor":"","text":"Behind Authelia, Authentik, oauth2-proxy or another login proxy, camera.ui can take over the user the proxy signed in and skip its own login form. The proxy names the user in a header, and camera.ui signs in the account with exactly that username, capitals included. It creates no accounts: everyone the proxy lets through needs an account in Settings → Users."},{"page":"remote/proxy-sign-in","title":"Sign-in through a proxy","heading":"Setup","anchor":"setup","text":"Add a proxyAuth block to camera.ui.yaml in your storage directory and restart the server:\n\nproxyAuth:\n port: 8082\n trust:\n - 10.0.0.5\n\ncamera.ui opens a separate plain HTTP port for the proxy, bound to the same address as the main port. Point the proxy at http://10.0.0.27:8082 instead of the main port, with WebSockets and long timeouts as on Reverse proxy. Only this port reads the header. Sent to the main port, the plain HTTP port or through camera.ui Cloud, it does nothing.\n\n| Field | Environment variable | Meaning |\n|---|---|---|\n| port | CAMERA_UI_PROXY_AUTH_PORT | Port the proxy connects to. |\n| header | CAMERA_UI_PROXY_AUTH_HEADER | Header with the username, Remote-User by default. |\n| trust | CAMERA_UI_PROXY_AUTH_TRUST | Addresses of the proxy, as IP addresses or ranges like 10.0.0.0/24. In the variable, separated by commas. |\n| secret | CAMERA_UI_PROXY_AUTH_SECRET | At least 16 characters the proxy sends as X-Proxy-Secret on every request. |\n| logoutUrl | CAMERA_UI_PROXY_AUTH_LOGOUT_URL | Logout page of the proxy, see Signing out. |\n\nA variable takes precedence over the file. The port needs trust, secret or both, and with both set a request has to pass both. A setting camera.ui can't use keeps the port closed, and the log says why."},{"page":"remote/proxy-sign-in","title":"Sign-in through a proxy","heading":"Which header","anchor":"which-header","text":"| Proxy | Header | Logout page |\n|---|---|---|\n| Authelia | Remote-User | https://auth.example.com/logout |\n| Authentik | X-authentik-username | https://nvr.example.com/outpost.goauthentik.io/sign_out |\n| oauth2-proxy | X-Forwarded-Preferred-Username | https://nvr.example.com/oauth2/sign_out |\n\nWith forward auth, the web server has to copy the header from the auth response onto the request: copy_headers in Caddy, authResponseHeaders in Traefik, auth_request_set plus proxy_set_header in nginx."},{"page":"remote/proxy-sign-in","title":"Sign-in through a proxy","heading":"Who may sign in","anchor":"who-may-sign-in","text":"Whoever connects from a trusted address, or with the secret, can sign in as any user, master included. camera.ui's password and two-factor authentication don't apply on this port, the proxy's login does. So trust only the proxy:\n\n- Docker. camera.ui sees the proxy's address on the shared Docker network. Give the proxy a fixed address there (ipv4_address in Compose) or use the secret instead of trust. The log names every address it turned away.\n- Same machine. A proxy next to a bare-metal install connects over 127.0.0.1. Trusting that address lets every program on the machine sign in, plugins included, so set a secret as well.\n- Paths without a login. The proxy has to replace the header on every request it passes on. If it lets a path through without asking for a login, it must not forward a header of that name sent by the browser. Put camera.ui behind the login as a whole, or have the proxy remove the header."},{"page":"remote/proxy-sign-in","title":"Sign-in through a proxy","heading":"In the app","anchor":"in-the-app","text":"The browser opens camera.ui signed in. All browsers of one user share one session, listed as Reverse proxy under Logged in users. A request without the header gets the normal login form.\n\nIf the proxy names a user camera.ui doesn't have, the browser shows an error instead of the app and the log names the username. Since camera.ui finds the account by its username, the account settings show the username but don't let you change it while you are signed in through the proxy. If an admin renames the account, change the name on the proxy side too."},{"page":"remote/proxy-sign-in","title":"Sign-in through a proxy","heading":"Signing out","anchor":"signing-out","text":"Without logoutUrl, the menu has no Logout, since the proxy would sign you back in at once. With logoutUrl, Logout ends the camera.ui session and opens that page, which ends the session at the proxy."},{"page":"remote/proxy-sign-in","title":"Sign-in through a proxy","heading":"Next steps","anchor":"next-steps","text":"Reverse proxy — WebSockets, timeouts and certificates for the proxy.\nUsers & roles — create the accounts the proxy signs in."},{"page":"remote/reverse-proxy","title":"Reverse proxy","heading":"Reverse proxy","anchor":"","text":"Behind Apache, nginx, Traefik or any other reverse proxy, camera.ui needs more than plain HTTP: live video, events and the camera list travel over WebSockets. A proxy that forwards only ordinary requests gives you a UI that loads and stays empty."},{"page":"remote/reverse-proxy","title":"Reverse proxy","heading":"What the server expects","anchor":"what-the-server-expects","text":"The main port serves HTTPS. camera.ui listens on port 3443 by default, using a certificate it generates itself. A proxy pointed at that port has to connect with https:// and skip certificate verification, since there is no public issuer to check against. Or switch on a plain HTTP port.\n\nWebSocket upgrades have to reach the server. They use the same host and port as the rest of the app, so the simplest setup forwards everything under / with upgrades.\n\nConnections stay open for a long time. A live stream holds one connection for as long as you watch. Proxy timeouts in the low seconds cut the picture at regular intervals.\n\nOnly a trusted proxy passes on the client's address. camera.ui reads X-Forwarded-For from a proxy on the same machine, which connects over 127.0.0.1 or ::1, and from the addresses you trust. Behind any other proxy, the session lists show the proxy's address."},{"page":"remote/reverse-proxy","title":"Reverse proxy","heading":"Apache","anchor":"apache","text":"Enable proxy, proxy_http, proxy_wstunnel, ssl and headers, then use this virtual host:\n\n ServerName nvr.example.com\n\n ProxyPreserveHost On\n ProxyRequests Off\n\n SSLProxyEngine On\n SSLProxyVerify none\n SSLProxyCheckPeerCN off\n SSLProxyCheckPeerName off\n SSLProxyCheckPeerExpire off\n\n ProxyTimeout 300\n\n ProxyPass \"/\" \"https://10.0.0.27:3443/\" upgrade=websocket timeout=300\n ProxyPassReverse \"/\" \"https://10.0.0.27:3443/\"\n\n RequestHeader set X-Forwarded-Proto \"https\"\n RequestHeader set X-Forwarded-Port \"443\"\n\nThe upgrade=websocket option needs Apache 2.4.47 or newer (apachectl -v). On older versions, drop that option and route the upgrades with mod_rewrite instead:\n\n ProxyPass \"/\" \"https://10.0.0.27:3443/\"\n ProxyPassReverse \"/\" \"https://10.0.0.27:3443/\"\n\n RewriteEngine On\n RewriteCond %{HTTP:Upgrade} =websocket [NC]\n RewriteCond %{HTTP:Connection} upgrade [NC]\n RewriteRule /(.*) wss://10.0.0.27:3443/$1 [P,L]\n\nThe rewrite target must be wss://. With ws:// the handshake goes unencrypted to a TLS port, Apache logs error reading status line from remote server, and every WebSocket fails with a 502 while normal pages load fine."},{"page":"remote/reverse-proxy","title":"Reverse proxy","heading":"nginx","anchor":"nginx","text":"map $http_upgrade $connection_upgrade {\n default upgrade;\n '' close;\n}\n\nserver {\n listen 443 ssl;\n server_name nvr.example.com;\n\n client_max_body_size 100m;\n\n location / {\n proxy_pass https://10.0.0.27:3443;\n proxy_ssl_verify off;\n\n proxy_http_version 1.1;\n proxy_set_header Upgrade $http_upgrade;\n proxy_set_header Connection $connection_upgrade;\n\n proxy_set_header Host $host;\n proxy_set_header X-Forwarded-Proto $scheme;\n proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n\n proxy_buffering off;\n proxy_read_timeout 300s;\n }\n}\n\nclient_max_body_size matters if you restore a backup through the proxy, since nginx rejects uploads over 1 MB by default."},{"page":"remote/reverse-proxy","title":"Reverse proxy","heading":"Proxy on another machine","anchor":"proxy-on-another-machine","text":"For the session lists to show the client's address behind a proxy on another host, add the proxy to camera.ui.yaml in your storage directory and restart the server:\n\ntrustedProxies:\n - 10.0.0.5\n\nAn entry is an IP address or a range like 10.0.0.0/24. In Docker, CAMERA_UI_TRUSTED_PROXIES=10.0.0.5 does the same, with several entries separated by commas. camera.ui skips anything else and names it in the log. A listed address can pass on any client address it likes, so list the proxy and nothing else."},{"page":"remote/reverse-proxy","title":"Reverse proxy","heading":"Plain HTTP instead","anchor":"plain-http-instead","text":"camera.ui can open a second listener that speaks plain HTTP and forwards everything, WebSockets included, to the main port. Add a port to camera.ui.yaml in your storage directory:\n\ninsecurePort: 8081\n\nIn Docker you can set CAMERA_UI_INSECURE_PORT=8081 instead. Restart the server, then point the proxy at http://10.0.0.27:8081 and drop the SSLProxy* lines or proxy_ssl_verify.\n\nThis listener binds to the same address as the main port, so it is unencrypted on your whole network. Use it only where that traffic stays trusted, such as a proxy on the same machine or a network segment you control."},{"page":"remote/reverse-proxy","title":"Reverse proxy","heading":"When the interface loads but stays empty","anchor":"when-the-interface-loads-but-stays-empty","text":"Cameras show a placeholder, the event row never fills, and nothing updates. That is a WebSocket problem, not an authentication problem. In the browser's developer tools, filter the network tab by WS and reload. A failing entry with status 502 or 400 confirms it, and your proxy's error log names the reason."},{"page":"remote/reverse-proxy","title":"Reverse proxy","heading":"Behind a login proxy","anchor":"behind-a-login-proxy","text":"With Pangolin, Authelia or another proxy that asks for its own login, page loads always go through the proxy, so it can ask again once its session runs out. If a browser still stays on Cannot reach your home server, clear its site data for camera.ui once and open the page again. To skip the camera.ui login behind such a proxy, see Sign-in through a proxy."},{"page":"remote/reverse-proxy","title":"Reverse proxy","heading":"Telling camera.ui about the address","anchor":"telling-cameraui-about-the-address","text":"Once the proxy works, enter the public address under Settings → Remote Access as described in Custom domain.\n\nIf the proxy also answers on your own network, put its address into Local address in the Network card on the same page, for example https://camera.home.lan. Apps then talk to the proxy at home instead of the server's IP. Apps that connect through camera.ui Cloud do this only for a local name, one ending in .lan, .local or .home.arpa for example, or one without a dot. They treat any other name as an internet address. The proxy needs a certificate for that name and has to pass WebSocket connections through, the same as for outside access. It also has to forward OPTIONS requests to camera.ui and pass its Access-Control-* headers on unchanged, since the apps call it from another origin. Otherwise they take the internet path at home. Camera streams keep using the IP addresses in Server addresses above, so leave those alone.\n\nTest connection checks the address by requesting it from the server itself. If your router has no hairpin NAT, or internal DNS sends the name somewhere else, that request never comes back and the test fails even though the address works from outside. Remote access keeps running anyway: camera.ui gives up a custom domain only when the name stops resolving on the public internet. Pointing your internal DNS at the proxy makes the test pass too."},{"page":"remote/reverse-proxy","title":"Reverse proxy","heading":"Next steps","anchor":"next-steps","text":"Custom domain — enter the public address in camera.ui.\ncamera.ui Cloud — remote access without touching your network."},{"page":"sensors/controls","title":"Controls & states","heading":"Controls & states","anchor":"","text":"Once a camera has sensors set up, you can see their state and operate the ones that support it."},{"page":"sensors/controls","title":"Controls & states","heading":"Where sensors appear","anchor":"where-sensors-appear","text":"- The camera's Overview (in the settings drawer) lists every sensor assigned to the camera with its live state and, for controllable ones, the controls.\n- Shortcuts let you place a sensor on the video to read its state on hover and operate it on click.\n- The Sensors page lists every sensor in the system. Its edit dialog shows the same controls, including for sensors assigned to no camera."},{"page":"sensors/controls","title":"Controls & states","heading":"Read-only sensors","anchor":"read-only-sensors","text":"These show their current state but aren't operated:\n\n- Contact. Open or closed, for a door or window.\n- Temperature and humidity. The current reading.\n- Occupancy. Occupied or empty.\n- Smoke and leak. Clear or triggered.\n- Gas, carbon monoxide, heat, cold, vibration, tamper, problem, and power. Clear or triggered.\n- Illuminance and CO₂. The current reading.\n- Battery. Level and charging."},{"page":"sensors/controls","title":"Controls & states","heading":"Controllable accessories","anchor":"controllable-accessories","text":"These you can operate (admin only):\n\n- Light. On or off, and brightness where supported.\n- Switch. On or off.\n- Lock. Lock or unlock.\n- Siren. On or off, and volume where supported.\n- Garage. Open or close.\n- Doorbell. Trigger a ring.\n- Security system. Choose a mode: Off, Home, Away or Night. If the alarm goes off, the control shows an alarm banner and the modes stay locked while it's active.\n\nPTZ cameras have their own controls and autotrack."},{"page":"sensors/","title":"Sensors & accessories","heading":"Sensors & accessories","anchor":"","text":"A sensor is any non-video capability in camera.ui: a detection, an environmental reading, or a controllable accessory. Most sensors come from plugins; virtual sensors you create yourself.\n\nEvery sensor is its own entity. It keeps its name, settings, and history no matter which cameras it is attached to, and it can be assigned to one camera, to several, or to none at all."},{"page":"sensors/","title":"Sensors & accessories","heading":"Types of sensor","anchor":"types-of-sensor","text":"- Detection. Motion, object, audio, face, license plate, and semantic (CLIP). These are covered under Detection & AI.\n- Environmental & state. Contact, temperature, humidity, occupancy, smoke, leak, battery, gas, carbon monoxide, CO₂, heat, cold, vibration, tamper, problem, power, and illuminance. These are read-only. The binary ones (like gas or tamper) can trigger a camera's detection the same way a contact sensor does.\n- Accessories. Light, switch, lock, siren, garage, security system, and doorbell. These can be controlled.\n- PTZ. Pan, tilt, and zoom. See PTZ & autotrack."},{"page":"sensors/","title":"Sensors & accessories","heading":"Camera hardware and standalone sensors","anchor":"camera-hardware-and-standalone-sensors","text":"- Camera hardware. A camera's own plugin registers them on that camera: its doorbell button, spotlight, siren, or battery. The assignment is fixed; you can't move them to another camera.\n- Standalone. Everything else, from a smart plug brought in by an integration to a virtual sensor you created. You decide which cameras it belongs to, on the Sensors page. An assigned sensor shows up on those cameras and can trigger their detection.\n\nDeleting a camera takes its camera-hardware sensors with it. A standalone sensor that was assigned to it stays and only loses the assignment."},{"page":"sensors/","title":"Sensors & accessories","heading":"Setting up and using sensors","anchor":"setting-up-and-using-sensors","text":"Plugin sensors are enabled per camera, and every sensor is managed on the Sensors page, where integrations offer what they find under Discovered. See Set up sensors, and Controls & states to read state and operate accessories.\n\nVirtual sensors need no plugin, see Virtual sensors."},{"page":"sensors/","title":"Sensors & accessories","heading":"Export to your smart home","anchor":"export-to-your-smart-home","text":"Each sensor has an Expose sensor toggle. Exposed sensors show up in HomeKit, Home Assistant, and MQTT; turn the toggle off to keep a sensor local to camera.ui. Camera-fixed types such as PTZ and battery are always handled by their camera and have no toggle."},{"page":"sensors/","title":"Sensors & accessories","heading":"In automations","anchor":"in-automations","text":"Sensors can trigger automations, a contact opening for example, and automations can operate accessories such as a light."},{"page":"sensors/ptz","title":"PTZ & autotrack","heading":"PTZ & autotrack","anchor":"","text":"PTZ cameras (pan, tilt, zoom) can be moved from camera.ui, and with autotrack they follow detected objects on their own.\n\nPTZ is a sensor a plugin provides, the ONVIF plugin for example, enabled in the camera's settings under the Plugins tab. Only the moves the plugin reports (pan, tilt, zoom, presets and home) are shown."},{"page":"sensors/ptz","title":"PTZ & autotrack","heading":"Controlling a PTZ camera","anchor":"controlling-a-ptz-camera","text":"Hovering a PTZ camera's player shows a bar at the top right:\n\n- Go to home. Send the camera back to its home position. Shown when the camera has one.\n- Presets. Jump to a position saved on the camera. Shown when the camera reports at least one. camera.ui reads presets from the camera but cannot create or delete them.\n- PTZ. Show the on-screen controls: the zoom slider on the left, the pan and tilt joystick on the right.\n\nThe bar is for admins, needs a player at least 350 pixels wide, and hides while the timeline or a recording is open."},{"page":"sensors/ptz","title":"PTZ & autotrack","heading":"Autotrack","anchor":"autotrack","text":"Autotrack lets a PTZ camera follow detected objects automatically using pan and tilt. It needs object detection on the camera.\n\nIn the camera's settings, under Autotrack:\n\n- Enable autotrack. Turn tracking on.\n- Target labels. Which object types the camera should follow, such as people or vehicles.\n- Minimum confidence. Only track detections above this confidence score.\n- Dead zone. A central area where a target can move without the camera adjusting.\n- Tracking speed. How aggressively the camera moves to re-center the target. Higher is snappier, lower is smoother.\n- Motion prediction. Aims ahead of a moving target by this many milliseconds, covering the time the camera needs to move and settle. Lower it if the camera overshoots, raise it for fast targets. The prediction fades out as the target slows down. 1500 by default, up to 4000. Set it to 0 to turn it off.\n- Pan-rate calibration. Calibrates how far each move step travels on your camera. Lower it if the camera stops short of the target, raise it if it overshoots.\n- Return to home. Move the camera back to its home position when no target is seen for a while.\n- Wait before returning. How long to wait without a target before returning home.\n- Smallest object. Ignore objects that take up less than this share of the picture height. 0 turns the limit off.\n- Largest object. Stop following once an object fills more than this share of the picture height. The camera holds its position until the object backs off. 0 turns the limit off.\n- Only during these hours. Run autotrack between two times, for example at night. The times are read in the timezone of the browser you set them in, and a window that ends before it starts runs across midnight. Outside the window the camera stays where it is, and goes home if Return to home is on. Off means all day.\n\nThe size limits apply when autotrack picks a target. A target it is already following stays followed as it gets smaller, and a target that grows past the largest size keeps its place while the camera waits.\n\nWhen the target is hidden for a moment, the camera holds its position for up to three seconds and keeps following it once it shows up again. After that it follows the largest target in view.\n\nIf you move the camera yourself, with the joystick or your camera's own app, autotrack pauses for 45 seconds, counted from when you stop moving."},{"page":"sensors/setup","title":"Set up sensors","heading":"Set up sensors","anchor":"","text":"Most sensors come from plugins: you enable the providing plugin and the sensor in the camera's settings. Every sensor, including ones without a camera, is managed on the Sensors page. Both need admin."},{"page":"sensors/setup","title":"Set up sensors","heading":"Open the Plugins tab","anchor":"open-the-plugins-tab","text":"The Plugins tab in a camera's settings is grouped into categories:\n\n- Detection. The detectors: motion, object, audio, face, face recognition, person re-ID, segmentation, license plate, and CLIP.\n- Accessories. Controllable accessories and state sensors, such as lights, locks, switches, sirens, and contact sensors.\n- Core. Single-provider sensors such as PTZ and battery.\n- Hub. Smart-home and platform integrations, such as a HomeKit bridge.\n- More. Enable or disable whole plugins for this camera.\n\nA camera added through a plugin (such as ONVIF or Reolink) also has a category for that plugin, holding its settings."},{"page":"sensors/setup","title":"Set up sensors","heading":"Assign and enable sensors","anchor":"assign-and-enable-sensors","text":"Within a category, pick a sensor type, then choose the plugin that should provide it. Detection types take one provider at a time; accessories can come from several plugins. Each enabled sensor appears in the Sensors list, where you can rename it.\n\nA doorbell sensor assigned to a camera starts a camera event on every ring, without enabling it under Detection."},{"page":"sensors/setup","title":"Set up sensors","heading":"Configure settings","anchor":"configure-settings","text":"Plugin settings for this camera and per-sensor settings appear as a form in the same place. Changes take effect immediately.\n\nEnabled sensors show up in the camera's Overview and on the video. See Controls & states to use them, and PTZ & autotrack for PTZ cameras."},{"page":"sensors/setup","title":"Set up sensors","heading":"The Sensors page","anchor":"the-sensors-page","text":"The page has two lists. Adopted sensors holds everything camera.ui manages, plugin-provided and virtual. A camera's own sensors (motion, battery, PTZ) are listed only where their plugin is enabled for that category on that camera. The settings button next to the search field holds Show only enabled camera sensors (on by default) and Hide camera sensors; turn both off to see everything when cleaning up.\n\nEach row shows the connection state, name, ID, type, providing plugin and assigned cameras. The ID is the sensor's address in its source system (for Home Assistant, the entity id) and tells same-named sensors apart; search matches it too. It is a label, not the identity: rename the entity in Home Assistant and the sensor keeps its cameras, automations and history, only the ID changes. Sensors you created yourself have none.\n\nThe dot before the name shows one of four states: connected, not connected (the plugin is stopped), unavailable (the plugin runs but cannot reach the sensor, or the source reports it as unavailable), and removed in the source (the entity was deleted there). Nothing is deleted on its own: a removed sensor stays until you delete it, so nothing that referenced it breaks silently. A lock next to the camera marks camera hardware, a crossed-out eye marks a sensor hidden from your smart home, and the icon glows while the sensor is active. The floating buttons create a virtual sensor and start select mode for deleting several at once.\n\nDiscovered lists what plugins found, with name, ID, type, room and plugin. Nothing is adopted on its own: a plugin like Home Assistant offers everything it understands and leaves the choice to you. An adopted sensor (one row, or many via the select button) moves into the list above, where it can be assigned to cameras. Rescan asks the plugins again; the list also refreshes by itself while the page is open.\n\nClick a row to edit the sensor:\n\n- Name. How the sensor appears everywhere.\n- ID. The sensor's address in the system it comes from, read-only. Only shown when the sensor has one.\n- Assigned cameras. Pick any number of cameras. The sensor shows up on those cameras and can trigger their detection. Camera hardware is fixed to its camera and can't be re-assigned.\n- Expose sensor. Whether the sensor shows up in HomeKit, Home Assistant, and MQTT.\n\nControllable sensors can also be operated right in this dialog.\n\nRow actions:\n\n- History. The sensor's recorded state changes.\n- Delete sensor. Any adopted or virtual sens"},{"page":"sensors/setup","title":"Set up sensors","heading":"The Sensors page","anchor":"the-sensors-page","text":"or, connected or not. Only a camera's own hardware sensors cannot be deleted while their camera is connected, they would come back with it.[^delete]"},{"page":"sensors/setup","title":"Set up sensors","heading":"Virtual sensors","anchor":"virtual-sensors","text":"Sensors without a plugin behind them are created on the Sensors page, see Virtual sensors.\n\n[^delete]: Deleting removes the sensor and its assignments. A deleted adopted sensor shows up under Discovered again; adopting it once more creates a new sensor, and anything that referenced the old one (automations, smart-home exports) must be pointed at the new one."},{"page":"sensors/virtual","title":"Virtual sensors","heading":"Virtual sensors","anchor":"","text":"A virtual sensor is a sensor with no plugin behind it. You create it yourself, camera.ui holds its state, and you set that state from the UI or from an automation.\n\nTypical uses: a doorbell you trigger from an automation, a contact you flip when an MQTT message arrives, a switch for something no plugin reports. Otherwise it behaves like any other sensor: assign it to cameras, place it on the video as a shortcut, export it to your smart home, use it as an automation trigger.\n\nCreating, renaming, and deleting virtual sensors needs admin."},{"page":"sensors/virtual","title":"Virtual sensors","heading":"Create a virtual sensor","anchor":"create-a-virtual-sensor","text":"Create virtual sensor on the Sensors page asks for:\n\n- Type. What the sensor represents. Can't be changed later.\n- Name. How the sensor appears in camera.ui, for example \"Front Door\". Two virtual sensors of the same type can't share a name.\n\nThe sensor starts in its idle state: off, closed, locked, or disarmed, depending on the type. It belongs to no camera until you assign cameras from its row; it then shows up in their Overview.[^cameradelete]"},{"page":"sensors/virtual","title":"Virtual sensors","heading":"Available types","anchor":"available-types","text":"There are 23 types:\n\n- Doorbell. A ring you trigger. It resets itself two seconds later.\n- Switch. On or off.\n- Contact. Open or closed, for a door or window.\n- Occupancy. Occupied or empty.\n- Smoke. Clear or triggered.\n- Leak. Clear or triggered.\n- Light. On or off, with brightness.\n- Siren. On or off, with volume.\n- Lock. Locked or unlocked.\n- Garage. Open or closed.\n- Security System. Off, Home, Away, or Night.\n- Temperature. A temperature reading.\n- Humidity. A humidity reading.\n- Gas. Clear or triggered.\n- Carbon Monoxide. Clear or triggered.\n- Heat. Clear or triggered.\n- Cold. Clear or triggered.\n- Vibration. Clear or triggered.\n- Tamper. Clear or triggered.\n- Problem. Clear or triggered.\n- Power. Clear or triggered.\n- Illuminance. A light-level reading in lux.\n- CO₂. A carbon dioxide reading in ppm."},{"page":"sensors/virtual","title":"Virtual sensors","heading":"Control it","anchor":"control-it","text":"Doorbell, Switch, Light, Siren, Lock, Garage and Security System get the same controls as plugin-provided ones (see Controls & states): on an assigned camera's Overview tab, and in the sensor's edit dialog on the Sensors page, which also covers sensors assigned to no camera.\n\nContact, Occupancy, Smoke, Leak, Temperature, Humidity, Gas, Carbon Monoxide, Heat, Cold, Vibration, Tamper, Problem, Power, Illuminance, and CO₂ are read-only. Their value comes from an automation.\n\nLock, Garage and Security System switch immediately, with no \"locking\" or \"opening\" state in between.\n\nThe last state survives a restart."},{"page":"sensors/virtual","title":"Virtual sensors","heading":"Control it from an automation","anchor":"control-it-from-an-automation","text":"In the automation editor, a Control sensor action sets a virtual sensor, and a Sensor change trigger reacts when its state changes. For the read-only types this is the only way to set a value: an automation that receives an MQTT message, for example, can put that value on a virtual temperature sensor."},{"page":"sensors/virtual","title":"Virtual sensors","heading":"Rename or delete","anchor":"rename-or-delete","text":"On the Sensors page: click the row to rename, use the row's delete button to remove.[^delete]\n\nA new name applies everywhere (Overview, shortcuts, automations), and automations using the sensor keep working.\n\n[^cameradelete]: Deleting a camera only removes the assignment. The sensor itself stays and can be assigned to other cameras.\n\n[^delete]: Deleting a virtual sensor stops any automation that references it. Creating a new sensor with the same type and name does not restore the link, since it is a new sensor. Point the automation at it again."},{"page":"troubleshooting/","title":"Troubleshooting","heading":"Troubleshooting","anchor":"","text":"The most common issues and where to look. The Logs show what the server is actually doing."},{"page":"troubleshooting/","title":"Troubleshooting","heading":"A camera won't connect","anchor":"a-camera-wont-connect","text":"- Check the camera's sources. The Sources tab shows each stream's live status, detected codecs, and URL.\n- Make sure the stream URL and credentials are correct. Test the RTSP URL in a player like VLC to rule out the camera itself.\n- Confirm the camera is on the same network as the server, or otherwise reachable from it.\n- For brand or discovery-based cameras, check the relevant protocol or plugin."},{"page":"troubleshooting/","title":"Troubleshooting","heading":"Live view is black or won't play","anchor":"live-view-is-black-or-wont-play","text":"- If the player shows Stream unavailable, the line below it is the reason the streaming engine gave. Small players leave it out, the camera page shows it.\n- Check the source's codec in the Sources tab; very unusual codecs may need transcoding.\n- Try a different streaming mode in the camera's interface settings.\n- Enable hot mode and preload on the source for faster, more reliable opening."},{"page":"troubleshooting/","title":"Troubleshooting","heading":"Detection isn't firing","anchor":"detection-isnt-firing","text":"- Make sure a motion engine and, for objects, an AI backend are assigned to the camera. See Set up sensors.\n- Lower the confidence or motion sensitivity in Settings → Detection if events are missed.\n- Check your zones cover the area, and that the type you expect is listed on one of them.\n- Remember the cascade: object, face, and plate detection only run after motion or another trigger. See Detection."},{"page":"troubleshooting/","title":"Troubleshooting","heading":"OpenVINO can't find the GPU on bare metal","anchor":"openvino-cant-find-the-gpu-on-bare-metal","text":"The OpenVINO plugin logs libOpenCL.so.1: cannot open shared object file when the host is missing Intel's OpenCL runtime. Install it with sudo apt install ocl-icd-libopencl1 intel-opencl-icd, and make sure the service user is in the render and video groups. See Linux → Hardware acceleration."},{"page":"troubleshooting/","title":"Troubleshooting","heading":"No notifications","anchor":"no-notifications","text":"- Check the notification settings on the device: the master switch, quiet hours, and whether the device is registered.\n- Check the camera's notification settings: which objects, sounds, and sensors it alerts on.\n- For push to a phone, you need the native mobile app (a browser gets no push), camera.ui Cloud set up, and a tap on Sync this device. If push went quiet, the app usually shows a note with a Fix button that sets the phone up again. See Getting push on your phone."},{"page":"troubleshooting/","title":"Troubleshooting","heading":"Recording isn't working","anchor":"recording-isnt-working","text":"- Recording needs an active subscription. See Recording.\n- Confirm the camera's recording mode is set, either continuous or event.\n- Check storage: a full disk with Strict retention pauses recording until you free space. Overwrite only pauses once other data fills the disk and no older recordings are left to delete. Recording also pauses while the storage path can't be reached.\n- If Settings → Recordings warns that the storage volume is small, recordings rotate away quickly. Either the disk really is that small, or a storage mount you expected never mounted: check the path in storage."},{"page":"troubleshooting/","title":"Troubleshooting","heading":"Can't reach the server remotely","anchor":"cant-reach-the-server-remotely","text":"- Open Settings → Remote Access and select the info button next to the connection status. Connection details names the address in use, what the server sees, and every address that was tried with its timing and failure reason. Copy details copies the lot for a bug report.\n- Confirm cloud access is enabled and the server is registered, with the tunnel connected.\n- For a direct connection, use Test connection to check the address is reachable.\n- The mobile apps need camera.ui Cloud to connect from outside your network.\n- Behind a login proxy such as Pangolin or Authelia, a browser stuck on Cannot reach your home server needs its site data for camera.ui cleared once, see Reverse proxy.\n- If the app connects over the internet while you're at home, check the Network card on the same page: an IP address missing from Server addresses, or a Local address that doesn't answer, sends the app out over the internet instead."},{"page":"troubleshooting/","title":"Troubleshooting","heading":"I can't sign in","anchor":"i-cant-sign-in","text":"If you know the account but not the password, another administrator can set a new one in Settings → Users (user menu → Edit).\n\nThe master account is the exception: no other administrator can change it. To reset it, create an empty file named reset-password in camera.ui's volume folder and restart camera.ui:\n\n- Docker. /data/volume in the container, for example docker exec touch /data/volume/reset-password.\n- Proxmox. The same inside the LXC: pct exec -- docker exec cameraui touch /data/volume/reset-password.\n- Linux. ~/.camera.ui/volume of the service user.\n- Desktop app. The folder under Where your data lives.\n- Home Assistant app. /data/volume inside the app. The sidebar panel signs in without a password, only the direct address on port 3443 asks for it.\n\nThe master then signs in with its username and the password admin and has to set a new password right away. Its sessions are signed out and two-factor authentication is off. The log names the account, and the file removes itself."},{"page":"troubleshooting/","title":"Troubleshooting","heading":"Getting more detail","anchor":"getting-more-detail","text":"- Open the Logs and filter or search for the issue. You can download the log to share when asking for help.\n- For deeper inspection, the Terminal gives a shell on the server."},{"page":"troubleshooting/","title":"Troubleshooting","heading":"Still stuck?","anchor":"still-stuck","text":"Ask the community on Discord or Reddit, or open an issue on GitHub. Include your logs and the steps that led to the problem."}]
1
+ [{"page":"admin/assistant-setup","title":"Assistant setup","heading":"Assistant setup","anchor":"","text":"Everything the assistant needs sits under Settings → Assistant, admins only: the models it may use, which plugins may use them too, what bounds a run, and the endpoints other AI tools talk to."},{"page":"admin/assistant-setup","title":"Assistant setup","heading":"Choosing a model","anchor":"choosing-a-model","text":"The assistant uses models you bring; nothing is sent to camera.ui. Add model takes as many as you like, local and cloud side by side.\n\n- Ollama. Runs on your own hardware, nothing leaves your network. Install Ollama, pull a model that supports tool calling, and enter the Server URL if Ollama does not run on the camera.ui machine.\n- OpenAI-compatible server. Any server speaking the OpenAI chat completions API: LM Studio, vLLM, LiteLLM, llama.cpp. Enter its Server URL and, if it requires one, an API key.\n- OpenAI, Anthropic, Google Gemini, OpenRouter. Cloud providers. Paste the API key of your account, or reuse the key of a model you already added for the same provider. The key is stored encrypted on the server and never reaches the browser or a plugin. The provider bills you per token.\n- A plugin. Plugin models show up in the provider list, Apple LLM (plugin) for example, and answer on the machine the plugin runs on. The Apple LLM plugin needs a Mac with Apple Silicon and Apple Intelligence (macOS 26, pictures from macOS 27). It holds only a short conversation: fine for questions about cameras and the docs, weak on long multi-step tasks.\n\nPick the Model from the list the provider offers (the arrow loads it) or type its name. Saving tests the model with two short requests: whether it calls a tool, and whether it accepts a picture. The result shows as badges in the list (Tools, No tools, Pictures, No pictures, Test failed), and saving again repeats the test.\n\nThe default model answers wherever nothing else is picked: conversations without a profile, scheduled prompts, the recordings search and what the assistant remembers. The star next to a model makes it the default; with none picked, the first one in the list answers. Deleting a model moves the profiles and plugins that used it to the default.\n\nA model can be kept for admins: switch off Available to users in its dialog and the list marks it Admins only. What users run then falls back to the default model, or to the first one open to them. Without such a model the assistant counts as not set up for them.\n\nSend images to the model is a switch per model. With it on, a snapshot the assistant takes or an event picture from the recordings goes to the model so it can describe what it sees, which with a cloud provider means the picture leaves your machine. The pictures are always shown in the chat, whether the model receives them or not."},{"page":"admin/assistant-setup","title":"Assistant setup","heading":"Small models and the context window","anchor":"small-models-and-the-context-window","text":"Context window in the model dialog says how much this model holds. A plugin model brings the number itself, for everything else it is empty and the context budget from the general settings applies. Set it for a local or small model, 8192 for an 8k model for example, and camera.ui plans the request around it: the procedures move behind a load_skill call, the feature list gives way to a hint at the documentation, and tools the question did not start from are listed by name and fetched when the model wants them. The same happens on a large model in a big installation, where many plugins and connected servers push the tool list past what the window can carry.\n\nPick the tools first in the same dialog helps models that get lost in a long tool list: one extra call per question asks the model which tools it needs, and only those are offered, with a short set of rules. A plugin model switches it on by itself when it needs it; leave it off for large models."},{"page":"admin/assistant-setup","title":"Assistant setup","heading":"Models without tools or pictures","anchor":"models-without-tools-or-pictures","text":"Nothing is switched off for the user: every model can be picked, the assistant adapts and says what is missing.\n\n- No tools. The model can only chat; questions about cameras, recordings or settings stay unanswered.\n- No pictures. Snapshots and event pictures still appear in the chat, the model gets the text of the tool results instead.\n\nSome models pass both tests and still call tools unreliably, with invented ids for example. When answers come back empty or wrong, try a larger model."},{"page":"admin/assistant-setup","title":"Assistant setup","heading":"Plugin access","anchor":"plugin-access","text":"Plugins can use your models too, the NVR plugin for event descriptions and to double-check text alerts. Under Plugin access pick a model per plugin or No access. A text alert can check its pictures with another model, the NVR plugin still needs access for that. The plugin sends its request to camera.ui, which runs it on that model, so the key stays on the server and a plugin on a worker needs no internet access of its own. Its tokens show up under Usage with the plugin's name. The list is a decision, not a protection: plugins run with the rights of camera.ui."},{"page":"admin/assistant-setup","title":"Assistant setup","heading":"Thinking, cost and limits","anchor":"thinking-cost-and-limits","text":"Thinking sets how long the model reasons before it answers. *Model default* sends nothing and leaves it to the provider, *Off* asks for a direct answer, *Brief* and *Thorough* request short or long reasoning. The setting is mapped to what each provider offers, and a model without reasoning ignores it. When the model shares its reasoning, the answer shows a collapsible Thinking block.\n\nUnder every answer a small line shows the tokens sent and received, the time it took, the output speed and, for a model with a known list price, an estimated cost. Usage adds it up per user or plugin, model and month, chats and scheduled prompts together. The estimate uses public list prices and the tokens the provider reported; the invoice of your provider is what counts.\n\nThe Behavior card bounds a run:\n\n- Model turns per question. How often the model may call tools and think again before it has to answer. Default 8.\n- Tool calls per question. A hard stop against a model stuck in a loop. Default 25.\n- Context budget. How much of a long conversation the model sees. Ollama models get a context window of this size plus headroom for the instructions and the answer, so lower it on small hardware. Without it Ollama would use its 4k default and cut off the instructions, and the assistant would then claim it cannot look at your cameras.\n- Conversations kept per user and Pictures kept per conversation. What the history takes on disk. Older ones go first. Defaults 50 and 24.\n- Additional instructions. Appended to the built-in instructions, for house rules and camera hints, for example which camera points at the street.\n- Remember facts about users. Lets the assistant keep facts like names across conversations. On by default, and every user sees and clears their own list in the chat. A model with a window under 16k tokens only remembers what you ask it to remember, nothing it picks up on its own.\n\nReset to defaults puts these values back, the additional instructions stay."},{"page":"admin/assistant-setup","title":"Assistant setup","heading":"Shell commands","anchor":"shell-commands","text":"Off by default. Shell commands lets admins ask the assistant for host diagnostics the other tools cannot answer: disk usage, whether a camera answers on the network, processes, container logs. The assistant runs one command at a time on the server that hosts camera.ui, and every command is shown with the exact command line to allow, edit or reject before it runs. Output is capped, a command is killed after 30 seconds by default, and the tool never appears in the MCP server, in scheduled prompts or in automations. What you allow runs with the rights of the camera.ui process. While the switch is off and a question would need it, the answer carries the switch, so an admin can turn it on in the chat."},{"page":"admin/assistant-setup","title":"Assistant setup","heading":"MCP: use the instance from other AI tools","anchor":"mcp-use-the-instance-from-other-ai-tools","text":"The assistant's tools are also available over the Model Context Protocol, so Claude Code, Claude Desktop, Cursor and similar tools can ask your instance directly, with their own model. Turn on Enable MCP endpoint, create an API token under Account, and paste the setup snippet the page shows for your client. The endpoint is /api/assistant/mcp, authenticated with the token as bearer.\n\nBy default only reading tools are offered: cameras, snapshots, system status, recordings, the read side of the API. Allow changes adds the tools that change something. camera.ui cannot ask for confirmation over MCP, so the client is responsible for that; Claude Code and Claude Desktop ask before every tool call. The tools follow the role of the token owner, a user account never sees admin tools. The endpoint is independent of the chat model, so it works even when no model is configured."},{"page":"admin/assistant-setup","title":"Assistant setup","heading":"External MCP servers","anchor":"external-mcp-servers","text":"Other systems that offer an MCP server can lend the assistant their tools. Add one under External MCP servers with a name, its URL and an access token; the token is stored encrypted. The state next to the name shows whether the server answered and how many tools it offers. Accept a self-signed certificate is there for servers in your own network. Before a tool of such a server runs, the user confirms it, unless the server marks the tool as read-only. Many servers mark nothing, Home Assistant for example. Under Tools, the tab of the server has the field Run without asking: the tools picked there run without confirmation, in scheduled prompts and automations too, so pick only tools that read. In the chat these tools form their own group that can be switched off per conversation.\n\nFor Home Assistant enable the Model Context Protocol Server integration, create a long-lived access token in your Home Assistant profile and enter http://:8123/api/mcp as the URL. The assistant can then read states and switch lights, scenes and climate through Home Assistant's own tools, and it can wire them into automations."},{"page":"admin/assistant-setup","title":"Assistant setup","heading":"Next steps","anchor":"next-steps","text":"Assistant — what it can do and how it is used day to day."},{"page":"admin/backup","title":"Backup & restore","heading":"Backup & restore","anchor":"","text":"Backups live in Settings → Backup (admins only) and restore on any platform."},{"page":"admin/backup","title":"Backup & restore","heading":"Creating a backup","anchor":"creating-a-backup","text":"Choose Download backup archive to save a single encrypted archive (.cuibackup) of your camera.ui storage directory: configuration, database, user accounts, and plugin data. Recordings are not included.[^ignore] The server packs the archive first and needs about as much free space as the data in it.[^packing] If that does not fit, the backup stops and names the space it needs. The card shows when the last backup was made, and so does Settings → System."},{"page":"admin/backup","title":"Backup & restore","heading":"Backup key","anchor":"backup-key","text":"Every backup is encrypted with the backup key of its server, shown in the Backup key card. The same server opens its archives by itself, another server asks for the key. Keep it outside the server, in a password manager or as the file from Save key file: without it nobody can open the backups. Until you confirm with I saved the key, camera.ui reminds you after each download.\n\nA restore brings the backup key of the backup along, so the restored server opens the archives of the old one and encrypts new ones with the same key. Unencrypted .tar.gz archives restore without a key."},{"page":"admin/backup","title":"Backup & restore","heading":"Restoring","anchor":"restoring","text":"Pick an archive and choose Restore backup archive, or use the restore button of an archive under Available backups. A dropped connection does not start the upload over: it continues where it stopped, also when you pick the same file again in the same browser tab.\n\nAn archive from another server asks for its backup key: enter it or open the key file. camera.ui then checks the archive and shows what it holds: when it was created, the camera.ui version and platform, the number of cameras and plugins, and the accounts to sign in with afterwards. A restore outside Home Assistant leaves out the homeassistant account of the Home Assistant app, which signs in only there. If no other master account remains, an account named admin from the backup becomes the master and keeps its password. Without such an account, sign in as admin with the password admin afterwards. Ports and addresses that belong to the old machine, such as port 3543 of the desktop app or an IP this server does not have, are reset to this server's defaults, and the summary lists each one. Nothing changes until you confirm with Restore. A backup that waits for confirmation also shows under Last restore and is discarded after 24 hours.\n\nEach plugin installs in the version from the backup if that version runs on this camera.ui, otherwise in the newest version that does. The summary names a different version, a plugin that cannot be installed, one that does not run on this system (its cameras keep their settings), and plugins that ran on a worker. It also names a plugin that is installed on this server but missing from the backup: it stays installed and starts without its settings.\n\nAfter the confirmation the server restarts, applies the backup and installs its plugins, with the progress per plugin in the window. At the end camera.ui logs you out; sign in with an account from the backup. A plugin that fails to install is retried at every start, and its cameras keep their settings until then. Under Last restore you can retry it right away or discard it.\n\nFolders a backup leaves out, such as recordings, stay as they are on this server. The summary lists the ones the old server had, with their size, so you can copy them over yourself. For a backup of this same server the list is left out, the folders are already here.\n\n- Undo. The setup from before the restore is kept for 7 days.[^previous] Undo restore under Last restore puts it back and restarts the server. Changes made since the restore are lost.\n- Auto"},{"page":"admin/backup","title":"Backup & restore","heading":"Restoring","anchor":"restoring","text":"matic rollback. If camera.ui fails to start three times with the restored setup, it puts the previous one back by itself. The login page and Last restore say so.\n- Interrupted restore. If the server stops while it applies the backup, it finishes the restore at the next start.\n- Rejected archives. An archive with a damaged database is rejected before anything changes, and so is a backup from a newer camera.ui than the one on this server. Update camera.ui first in that case.\n\nBefore you confirm, choose how this server takes over the backup. camera.ui suggests Move for a backup of this server and for a server without cameras, otherwise Copy.\n\n- Move. This server replaces the old one and keeps its identity: certificate authority, remote access, cloud registration, sessions and paired workers. Apps, the Apple TV and workers keep trusting it. Turn the old server off. If it still answers on this network, the summary names its address. If camera.ui could not look for it, the summary says so, on a Mac usually because camera.ui has no access under System Settings → Privacy & Security → Local Network. If the address changed, set the new one as master address on the server. Workers in the same network find the new server by themselves, others need the new address too. A backup without a certificate authority keeps this server's own, so devices that trusted the old server need the certificate again.\n- Copy. Both servers keep running and the copy gets its own identity. Sessions, API tokens and workers of the old server are left behind, cameras and plugins run on this server, remote access is off and the copy is not registered in the cloud.\n\nAfter every restore, an undo included, sign the server in to cameraui.com again under Settings → Remote Access and connect a licensed NVR again in its settings. Until then, after a move or an undo, the NVR runs on its saved license for at most 14 days from the backup, and its notification names the date. After a copy the license is inactive until you connect it."},{"page":"admin/backup","title":"Backup & restore","heading":"Moving to another server","anchor":"moving-to-another-server","text":"The new server can fetch everything from the old one directly, without downloading an archive. Both servers have to reach each other on the network.\n\n1. On the old server, choose Create move code under Move this server. The code is valid for 30 minutes and is shown once. Treat it like a password, it gives access to the whole backup.\n2. On the new server, enter the code on the welcome screen under From Another Server or in the Restore backup card, then choose Fetch backup. The code only works on another server, the old one cannot fetch a backup while it offers a code. If the code does not find the old server, for example in another network, enter its address as host:port under Address of the old server.\n3. The old server restarts, stops its cameras, plugins and workers, packs its backup and sends it. The summary then shows as for an archive, with Move suggested. Confirm as described under Restoring.\n\nUntil the new server has fetched the backup, Cancel move under Move this server stops the move and the old server runs as before. After the transfer the old server stays stopped, and its login page says it moved. Run here again in the same place starts its cameras and plugins again. It does so by itself if you cancel on the new server, discard the backup there before confirming, or if the new server does not fetch the packed backup within 30 minutes. Restoring a backup on the old server lets it run again as well."},{"page":"admin/backup","title":"Backup & restore","heading":"Scheduled backups","anchor":"scheduled-backups","text":"The Backup scheduler card creates backups automatically and keeps only the newest archives.\n\nTurn it on and set:\n\n- Frequency. Daily, Weekly, or Monthly.\n- Time. The time of day the backup runs, as HH:mm (default 03:00).\n- Weekday. Shown only when Frequency is Weekly.\n- Day of month. Shown only when Frequency is Monthly, 1-28.\n- Keep backups. How many of the newest archives to retain, 1-60 (default 7). Older archives are deleted automatically after each run.\n- Destination folder. An absolute path on the server, for example a NAS mount. Leave it empty to use the default folder inside your storage directory. A relative path is rejected.\n\nRun now starts a backup immediately. Last run shows the time and result of the most recent scheduled run. A run missed while the server was off follows two minutes after the next start. A failed run sends the system notification Scheduled backup failed to admins.\n\nScheduled archives are encrypted like a download and written to the destination folder on the server. In a folder of your own, each server writes into a subfolder named after its instance ID, so several servers can share one NAS folder. Saving the destination places a .backupignore file in it. A run that finds the folder missing, or empty without that file (a share that is not mounted), writes nothing and fails. The destination folder is never part of a backup and stays in place when one is restored. Available backups lists the archives for download, restore or removal and marks those encrypted with the key of another server."},{"page":"admin/backup","title":"Backup & restore","heading":"Command line","anchor":"command-line","text":"On the machine the server runs on, the cameraui command creates and restores backups without the browser. In Docker run it inside the container (docker exec -it cameraui cameraui backup) and write to a mounted folder.\n\ncameraui backup -o /mnt/nas/camera.ui\ncameraui restore camera.ui-backup-1760000000000.cuibackup --key-file key.txt --move\n\n- backup. Saves an encrypted archive to the current folder, or to the file or folder given with -o. It signs in with an admin API token from --token or CAMERA_UI_API_TOKEN, otherwise with an admin account.\n- restore. Uploads the archive, prints the summary and asks before it changes anything. --move or --copy sets the mode, otherwise it asks with the suggested one as default. --key-file gives the backup key of another server, -y restores without asking. A restore needs an admin account, API tokens are not accepted.\n- Sign-in. --username names the account, the password comes from CAMERA_UI_PASSWORD or a prompt, --code gives the two-factor code.\n\nThe exit code is 0 on success and 1 otherwise.\n\n[^previous]: In the restore/previous folder of the camera.ui home directory.\n\n[^packing]: In the restore/packing folder of the camera.ui home directory.\n\n[^ignore]: To exclude a folder from the backup (for example a large media folder), place an empty file named .backupignore in it. A restore leaves such a folder untouched."},{"page":"admin/instances","title":"Instances","heading":"Instances","anchor":"","text":"An instance is another camera.ui server saved here, so you can switch to it without logging in again. Manage them in Instances (in the menu). Instances are separate, full servers; to make a second machine share the load of *this* server instead, see Workers.\n\n- Add an instance with a name, its URL, and a username and password for it.\n- Two-factor accounts. An account with 2FA needs the 6-digit code from your authenticator app when you add the instance or switch to it, see Security.[^backupcodes] Until then the card shows a 2FA tag and Enter code in its menu.\n- Each instance card shows its status: version, uptime, cameras, whether it's online and recording, and resource use.\n\n[^backupcodes]: The prompt takes the 6-digit code from the app only, not a backup code.\n\nWhen you have more than one, an instance switcher appears in the top bar.\n\nSwitching to an instance over plain HTTP passes your session in the address, so camera.ui warns you first. Use HTTPS where you can."},{"page":"admin/logs","title":"Logs & console","heading":"Logs & console","anchor":"","text":"The Logs view (in the menu, admins only) shows the server log."},{"page":"admin/logs","title":"Logs & console","heading":"Reading the logs","anchor":"reading-the-logs","text":"Logs stream live. Filter by source (All, a system component such as Server, go2rtc, nats, tunnel, a camera or a plugin), search the visible lines, or download, copy all or delete the log."},{"page":"admin/logs","title":"Logs & console","heading":"Console","anchor":"console","text":"The Console (in the menu, available to every user) shows the log of the app itself, not the server: the place to look when a view stays empty, a stream won't play or a button does nothing.\n\nIt only collects entries while Record logs is on, in Settings → Appearance. The same section has Verbose logging, which adds debug-level entries.\n\nThe Levels menu filters by Debug, Log, Info, Warning and Error. Export / share saves the output as a text file.\n\nFor problems with the app, the Console export is the useful thing to attach. For problems with the server, the Logs download."},{"page":"admin/logs","title":"Logs & console","heading":"Terminal","anchor":"terminal","text":"There's also a Terminal in the menu, an interactive shell on the server for advanced troubleshooting. It's available on server installations, not in the desktop app."},{"page":"admin/mqtt","title":"MQTT","heading":"MQTT","anchor":"","text":"The Settings → MQTT page (admins only) connects camera.ui to an MQTT broker. Once connected, camera.ui publishes server events, camera states, detections, snapshots and sensor values as MQTT topics, and accepts commands for your sensors. It's off by default."},{"page":"admin/mqtt","title":"MQTT","heading":"Turning it on","anchor":"turning-it-on","text":"Switch Enabled on and fill in the connection. Every save restarts the connection, so a wrong value shows up right away in the status card at the top."},{"page":"admin/mqtt","title":"MQTT","heading":"Mode","anchor":"mode","text":"Mode decides where the broker lives.\n\n- External broker. camera.ui connects to a broker you already run (Mosquitto, the Home Assistant Mosquitto add-on, and so on).\n- Built-in broker. camera.ui runs its own broker and connects to it locally."},{"page":"admin/mqtt","title":"MQTT","heading":"Built-in broker","anchor":"built-in-broker","text":"camera.ui starts the broker on Port (default 1883) and shows the Broker address as a read-only field with a copy button, in the form mqtt://:. Devices log in with:\n\n- Username. Defaults to cameraui.\n- Password. Generated randomly on first start, replaceable with your own.[^anon]"},{"page":"admin/mqtt","title":"MQTT","heading":"External broker","anchor":"external-broker","text":"- Host. The broker's hostname or IP, for example 192.168.1.10.\n- Port. Defaults to 1883.\n- Protocol. mqtt:// (TCP) or mqtts:// (TLS). Default is mqtt://.\n- Username and Password. Leave both empty for an anonymous broker. An empty password field after saving keeps the stored password."},{"page":"admin/mqtt","title":"MQTT","heading":"Client ID and topic prefix","anchor":"client-id-and-topic-prefix","text":"Both apply to either mode.\n\n- Client ID. The name camera.ui registers with at the broker. Defaults to cameraui. Change it if something else on your network already uses that ID.\n- Topic prefix. Everything camera.ui publishes lives below this prefix. Defaults to cameraui. Letters, numbers, _, - and / separators only."},{"page":"admin/mqtt","title":"MQTT","heading":"TLS","anchor":"tls","text":"The TLS fields appear for an external broker on mqtts://.\n\n- Verify server certificate. On by default. Turn it off only for a broker with a self-signed certificate.\n- CA certificate (PEM). The certificate authority that signed the broker's certificate.\n- Client certificate (PEM) and Client key (PEM). For brokers that require client certificates."},{"page":"admin/mqtt","title":"MQTT","heading":"Testing and status","anchor":"testing-and-status","text":"Test connection opens a short-lived, separate connection with the values currently in the form, without saving. For the built-in broker, save first: the test connects to the running broker.[^test]\n\nThe status card at the top shows the live state of the connection:\n\n| State | Meaning |\n|---|---|\n| Disabled | MQTT is off, or an external broker has no host set |\n| Connecting… | The connection is being established |\n| Connected | Connected to the broker |\n| Reconnecting… | The connection dropped, camera.ui is retrying |\n| Error | The last attempt failed · the card shows the broker's message |\n\nWhen the built-in broker is running, the card says so along with its port."},{"page":"admin/mqtt","title":"MQTT","heading":"Topic reference","anchor":"topic-reference","text":"below is your topic prefix, the camera's ID, a sensor's ID. In topic segments, #, +, / and whitespace are replaced with -.\n\nState topics are retained, so a subscriber that connects later still gets the current value. Events are not retained.\n\nSensors publish under their own tree, not under a camera: a sensor is its own entity and can belong to several cameras or none. Only exposed sensors are published; turning the expose toggle off clears the sensor's retained topics."},{"page":"admin/mqtt","title":"MQTT","heading":"What camera.ui publishes","anchor":"what-cameraui-publishes","text":"| Topic | Payload | Retained |\n|---|---|---|\n| /status | online · offline | yes |\n| /server/state | JSON with the server version and a timestamp | yes |\n| /server/event | JSON, started or shutdown with a timestamp | no |\n| /server/notification | JSON, system notifications | no |\n| /plugin//status | started · stopped · error · crashed | yes |\n| /camera//status | online · offline | yes |\n| /camera//frameworker | online · offline | yes |\n| /camera//meta | JSON: id, name, room, type, disabled, camera info | yes |\n| /camera//event | JSON detection event, start to end | no |\n| /camera//motion | ON · OFF | yes |\n| /camera//detection/ | ON · OFF, one topic per detected label | yes |\n| /camera//snapshot | JPEG of the last event thumbnail | yes |\n| /sensor//meta | JSON: ID, type, name, display name, plugin, assigned cameras | yes |\n| /sensor// | the property's value as JSON | yes |\n\n/status is also the last will: if camera.ui goes away without saying goodbye, the broker publishes offline for it.\n\nThe motion topic goes ON when an event includes motion, a label topic when that label is first detected. Both go back to OFF when the event ends. The event payload carries the detection details without any image data. The image goes to the snapshot topic."},{"page":"admin/mqtt","title":"MQTT","heading":"What camera.ui subscribes to","anchor":"what-cameraui-subscribes-to","text":"| Topic | Purpose |\n|---|---|\n| /sensor///set | Set a sensor property |\n\nPublish to a /set topic to control a sensor: ON and OFF become true and false, anything else is read as JSON and falls back to plain text. Detection sensors ignore commands, their values come from the camera.\n\nAutomations with an MQTT trigger subscribe to their own topics on top of this."},{"page":"admin/mqtt","title":"MQTT","heading":"Home Assistant discovery","anchor":"home-assistant-discovery","text":"Home Assistant discovery makes Home Assistant create the devices and entities for you, no YAML. It's off by default.\n\nPick one path, not both\nMQTT discovery is one of two ways to get camera.ui entities into Home Assistant. The other is the Home Assistant integration, which needs no broker. The two do not merge: run both and every device shows up twice. Use MQTT discovery if you already run a broker, otherwise use the integration.\nit on and check the Discovery prefix. It has to match the discovery prefix configured in Home Assistant, which is homeassistant unless you changed it.\n\nHome Assistant then builds one device per camera, using the camera's name, plus its model and firmware version when camera.ui knows them. Each device gets:\n\n- Status. A connectivity sensor, listed under diagnostics.\n- Motion. A motion sensor.\n- Snapshot. A camera entity fed by the snapshot topic.\n- Person, Vehicle, Animal, Package. Detection sensors, only for cameras that have object detection.\n\nEvery exposed sensor becomes a device of its own when its type maps to a Home Assistant one: contact, occupancy, smoke and leak sensors, doorbells, temperature, humidity and battery readings, switches, lights (with brightness where the light supports it), sirens, locks, garage doors and security systems. A sensor assigned to exactly one camera is linked below that camera's device. Switches, lights, sirens, locks, garage doors and security systems are controllable from Home Assistant.[^detsensors]\n\nEntities stay available only while both camera.ui and the camera are online. Status is the exception: it follows camera.ui alone, so it can still report the camera as offline. Turning discovery back off, turning MQTT off, or changing the discovery prefix or topic prefix removes the entities camera.ui created."},{"page":"admin/mqtt","title":"MQTT","heading":"Next steps","anchor":"next-steps","text":"- Automations — react to an incoming MQTT message, or publish one as an action.\n- Sensors — the sensors that show up on the sensor topics and in Home Assistant, and the expose toggle that controls it.\n\n[^detsensors]: Detection sensors (motion, object, audio, face, license plate and the like) get no entity of their own. They feed the Motion and detection entities above.\n[^anon]: Clearing the username or the password lets the built-in broker accept any client without credentials.\n[^test]: Testing the built-in broker before it's ever been saved reports that the broker isn't running."},{"page":"admin/security","title":"Security & 2FA","heading":"Security & 2FA","anchor":"","text":"Each account has its own security settings, in Settings → Account. They apply to your own login, so every user can set them up."},{"page":"admin/security","title":"Security & 2FA","heading":"Two-factor authentication","anchor":"two-factor-authentication","text":"Two-factor authentication (2FA) adds a code from an authenticator app, such as Google Authenticator, Authy or Apple's Passwords app, on top of your password. Password managers that keep the code offer it at sign-in.\n\nSet up 2FA shows a QR code for your app (or a code to enter by hand); on the device that keeps your codes, tap the QR code instead of scanning it. Confirm with the 6-digit code. The backup codes shown afterwards are the way back in if you lose your phone.\n\nOnce enabled, you can regenerate backup codes or disable 2FA, both of which ask for a current code."},{"page":"admin/security","title":"Security & 2FA","heading":"Remote instances","anchor":"remote-instances","text":"If the account on a remote instance has 2FA enabled, camera.ui asks for the 6-digit code when you add that instance, change its credentials, or switch to it. Only the code from your authenticator app works here, backup codes aren't accepted.\n\nIf you cancel while adding or editing, the instance is still saved, but its card shows Two-factor confirmation pending. Pick Enter code in the card's menu to finish. Cancelling during a switch only stops the switch."},{"page":"admin/security","title":"Security & 2FA","heading":"Biometric login (mobile app)","anchor":"biometric-login-mobile-app","text":"On a phone or tablet with Face ID or Touch ID, the app offers to save your credentials in the device's secure storage the first time you sign in successfully. If you pick Not now, the app won't ask again for that server.\n\nOnce enabled, the login screen offers Sign in with Face ID. The Use Face ID / Touch ID switch under Settings → Account only turns biometric login off: it removes the saved credentials, and the app offers to save them again the next time you sign in."},{"page":"admin/security","title":"Security & 2FA","heading":"Active sessions","anchor":"active-sessions","text":"The Active sessions list shows every device signed in to your account, with the current one marked. You can sign out a single device, or sign out all the others at once. A device you sign out also loses its live view right away."},{"page":"admin/security","title":"Security & 2FA","heading":"API tokens","anchor":"api-tokens","text":"Integrations that talk to camera.ui on their own, the Home Assistant integration and the Apple TV app for example, sign in with a token instead of your password. Create one under Settings → Account → API tokens and copy it right away, it is shown only once.\n\nA token has full access to your account, so treat it like a password. The list shows when each was last used, and Revoke kills one immediately."},{"page":"admin/security","title":"Security & 2FA","heading":"Password","anchor":"password","text":"Change your password under Account. Changing your password or username signs you out everywhere at once. Your API tokens keep working."},{"page":"admin/settings","title":"Settings overview","heading":"Settings overview","anchor":"","text":"Most configuration lives under Settings in the menu. Some settings are personal to your account; others are server-wide and need an admin."},{"page":"admin/settings","title":"Settings overview","heading":"Personal settings","anchor":"personal-settings","text":"Available to everyone signed in:\n\n- Account. Your profile, password, two-factor authentication, and active sessions. See Security.\n- Appearance. Theme, language, and interface options, including whether the settings pages form their own sidebar group and which groups can collapse.\n- Notifications. How and when this device alerts you. See Notifications."},{"page":"admin/settings","title":"Settings overview","heading":"Administrator settings","anchor":"administrator-settings","text":"Available to admins:\n\n- Users. Accounts, roles, and active sessions. See Users.\n- Recordings. Recording and storage options. See Recording.\n- Remote access. Reaching the server from outside, plus the addresses apps use to reach it on your own network. See Remote access.\n- MQTT. Connect camera.ui to an MQTT broker.\n- Backup. Back up and restore your setup, and schedule recurring backups. See Backup.\n- System. Version, updates, and server actions. See System."},{"page":"admin/settings","title":"Settings overview","heading":"Admin tools","anchor":"admin-tools","text":"The menu has more admin tools:\n\n- Metrics for how the server and each camera are doing, in three tabs: Overview with the system and process tables, Cameras with what detection costs per camera, and Storage with the disk breakdown. See What detection costs.\n- Updates for the server, the plugins and the workers in one list.\n- Automations for connecting events to actions.\n- Logs and a terminal for troubleshooting.\n- Instances for switching between several servers, and Workers for sharing the load of this one.\n- A raw configuration editor for advanced changes."},{"page":"admin/system","title":"System & network","heading":"System & network","anchor":"","text":"The Settings → System page (admins only) handles the server's version and a few power actions."},{"page":"admin/system","title":"System & network","heading":"Version and updates","anchor":"version-and-updates","text":"It shows the server version, and the app version in the desktop and mobile apps. Installing updates happens on the Updates page; what stays here is Manage, which lists every published version to install a specific one, and Beta updates, the toggle at the top of the page that decides whether pre-releases are offered at all. When an installed version needs a restart to take effect, the version line shows both and offers Restart. See Updating for how updates work across platforms."},{"page":"admin/system","title":"System & network","heading":"Server actions","anchor":"server-actions","text":"- Restart server. Restarts camera.ui.\n- Reset server. Wipes the configuration back to defaults, removing all settings and data. It can't be undone and is limited to the master account."},{"page":"admin/system","title":"System & network","heading":"Certificates","anchor":"certificates","text":"Certificate downloads the server's own certificate, for adding it to a device's trust store. Own certificate takes a certificate you obtained for your domain and serves it for the names it covers. See Certificate.\n\nLooking for the server name or network addresses? Those are under Remote access. Theme and language are under Appearance."},{"page":"admin/users","title":"Users & roles","heading":"Users & roles","anchor":"","text":"Every account has a role that decides what it can do. Accounts are managed in Settings → Users (admins only)."},{"page":"admin/users","title":"Users & roles","heading":"Roles","anchor":"roles","text":"- User. Day-to-day access: the camera list, live view, recordings, Camview, and their own account settings. The Cameras page opens as a plain list to tap through to any camera; discovery, adding cameras, and camera settings stay with admins. A user account sees a camera's source addresses only as protocol, host and port, without logins, paths or tokens.\n- Admin. Everything a user can do, plus managing cameras, plugins, automations, users, and all server settings.\n- Master. The main account. The same as an admin, plus it can reset the server. It can't be deleted, and its role can't be changed, not even by itself. Only the master can edit its own username, password and avatar; other admins get an error if they try.\n\nAccess is decided by role, not per camera; there are no per-camera permissions."},{"page":"admin/users","title":"Users & roles","heading":"Managing users","anchor":"managing-users","text":"The Registered users list creates, edits and removes accounts. A new account gets the role user or admin. Accounts named ha-… come from the Home Assistant app, one for each Home Assistant user without admin rights who opened its panel. Deleting an account or taking its admin rights away applies at once, also on devices where it is signed in."},{"page":"admin/users","title":"Users & roles","heading":"Logged in users","anchor":"logged-in-users","text":"Below the registered users, the Logged in users list shows every active session across all accounts, with the device, its address, and type. Sign out on a row ends that session."},{"page":"admin/users","title":"Users & roles","heading":"Connection limit","anchor":"connection-limit","text":"Each sign-in, meaning a browser or app you logged in with or an API token, can have up to 64 live streams and about 32 camera.ui tabs or windows open at the same time. Past that, new connections wait and retry until others close. Connections of devices that stopped answering are closed to make room."},{"page":"admin/users","title":"Users & roles","heading":"The first account","anchor":"the-first-account","text":"camera.ui starts with a single master account (username admin, password admin). You're prompted to change the password on first login."},{"page":"admin/worker-assignments","title":"Worker assignments","heading":"Worker assignments","anchor":"","text":"Cameras and plugins run on the machine you assign them to under Workers. The status dot at the start of each row shows where the camera or plugin runs right now, for example while it installs on a worker, waits for one that restarts, or runs on the main server during a failover. Its tooltip names the state and the reason. Changes in both tables take effect when you click Save below the table. The camera's settings show the same under Frame Worker, a plugin's status dot on its card."},{"page":"admin/worker-assignments","title":"Worker assignments","heading":"Cameras","anchor":"cameras","text":"Under Camera Assignments, choose which worker decodes each camera; cameras left as Local stay on the main server.\n\nDecoding runs where the camera is assigned, detection runs where the detection plugin runs. When a camera and a detection plugin that analyses its frames run on different machines, every analysed frame crosses the network: per active camera about 2.3 Mbit/s for motion and 12 to 74 Mbit/s for object detection at a 320 px model input, four times that at 640 px. A warning icon next to the camera's name shows when its frames take this path. Keep a camera and its detection plugins on the same machine."},{"page":"admin/worker-assignments","title":"Worker assignments","heading":"Plugins","anchor":"plugins","text":"Under Plugin Assignments, an entire plugin can run on a worker instead of the main server. Only workers whose capabilities allow the job are listed, and a worker whose platform does not fit the plugin can't be chosen. Moving a plugin asks first: files it keeps on its machine stay there, such as downloaded models, and it starts without them on the new one. The worker installs and runs the plugin; it's still configured normally in the UI. A plugin version built for a plugin API the worker doesn't support is refused before the install, and the message says whether to update the worker or pick another plugin version.\n\nA plugin update installs the new version on the worker next to the running one and then switches with one restart. If that install fails, the worker keeps the previous version. Uninstalling the plugin removes it from the worker too, its data included when you choose to remove it.\n\nA plugin on a worker uses the worker's own network addresses, not the Server addresses of the main server. Choose them with Edit Worker in the workers list; left empty, the plugin listens on all of them. Changing them restarts the plugins on that worker.\n\nSome plugins serve the camera video on their own machine, Reolink for example. On a worker, the worker sends that video to the master's HTTPS port, nothing has to be opened for it.\n\nThe NVR plugin stores its recordings on the machine it runs on, on a worker that is the worker's disk. Recordings from a time it ran elsewhere, during a failover for example, stay on that machine; the timeline marks the time with a band that names it."},{"page":"admin/worker-assignments","title":"Worker assignments","heading":"Failover","anchor":"failover","text":"Workers check in with the master every 5 seconds. A worker the master has not heard from for 15 seconds shows as not reachable, but its cameras and plugins stay where they are. After 25 seconds without an answer they fall back to the main server. A plugin the main server's platform can't run, one for macOS only for example, waits for the worker instead, and its status says so. A worker that loses the master stops its cameras and plugins after 20 seconds, so nothing runs on two machines at once. A shorter drop changes nothing: cameras and plugins keep running and exchange the sensor states that changed in between once the link is back. Once the worker has answered for a minute, its cameras and plugins move back to it one after the other; a worker that keeps dropping out waits longer, up to 10 minutes.\n\nTurn off Fallback next to an assignment to keep a camera or plugin on its worker. It then waits for the worker as well, also after a failure there, and starts on it as soon as the worker is back. That suits a plugin that keeps files on the worker, or a main server that could not carry the load. While a camera waits, camera.ui does not analyse its video; live view and recording keep running.\n\nRestarting or updating the main server leaves the workers running: their cameras and plugins carry on without a restart once the main server is back. Older versions of Go plugins like Reolink restart once instead. A worker that restarts or updates tells the master first, and its cameras and plugins wait for it (up to 30 seconds for a restart, 2 minutes for an update) before they fall back."},{"page":"admin/worker-setup","title":"Worker setup","heading":"Worker setup","anchor":"","text":"How the snippet from Pair a worker gets onto the worker depends on its installation."},{"page":"admin/worker-setup","title":"Worker setup","heading":"Docker","anchor":"docker","text":"A worker runs from the same image as the server. Save this as docker-compose.worker.yml on the second machine, with your master's address and the pairing code:\n\nname: cameraui-worker\n\nservices:\n cameraui-worker:\n image: ghcr.io/cameraui/camera.ui:latest\n container_name: cameraui-worker\n restart: unless-stopped\n stop_grace_period: 30s\n network_mode: host\n environment:\n - TZ=Europe/Berlin\n - CAMERA_UI_WORKER=true\n - CAMERA_UI_WORKER_MASTER=192.168.1.10\n - CAMERA_UI_WORKER_PAIRING_CODE=XXXX-XXXX\n - CAMERA_UI_WORKER_NAME=worker-1\n volumes:\n - cameraui-worker-data:/data\n\nvolumes:\n cameraui-worker-data:\n\nThen start it:\n\ndocker compose -f docker-compose.worker.yml up -d\n\nThe master address takes a hostname or IP, without a scheme. CAMERA_UI_WORKER_API_PORT is only needed if the master's HTTPS port is not 3443, a desktop app as master listens on 3543. To give the worker a GPU, see Hardware acceleration below."},{"page":"admin/worker-setup","title":"Worker setup","heading":"Desktop app","anchor":"desktop-app","text":"A machine with the desktop app joins as a worker when you choose Worker in the mode picker on first start (or later via Switch mode… in the tray menu) and enter the master's address and the pairing code, or without the picker from the command line. The app then shows only a status window. If the address is wrong or the code has expired, the picker reopens with the error.\n\nThree tray options: Open At Login starts the app with the system, minimized to the tray, Close to tray keeps the worker running in the background when the window is closed,[^trayopts] and Keep Computer Awake (on by default) keeps the computer from sleeping while the worker runs cameras or plugins; the display can still turn off. If the computer sleeps anyway, the worker stops its work first and the master takes over until it is back. The address takes a hostname or IP, with :port only when the master's HTTPS port differs from the default.\n\n[^trayopts]: Open At Login is offered on Windows and macOS. Close to tray is offered on Windows and Linux; on macOS, closing the window leaves the app running anyway."},{"page":"admin/worker-setup","title":"Worker setup","heading":"Linux (bare-metal)","anchor":"linux-bare-metal","text":"Paste the snippet into camera.ui.yaml in the worker machine's storage directory, then start it with cameraui --worker run. To keep the machine a worker across reboots, install it as a service instead: cameraui --worker install (the flag is stored with the service, see Linux (bare-metal))."},{"page":"admin/worker-setup","title":"Worker setup","heading":"Hardware acceleration","anchor":"hardware-acceleration","text":"In Docker, the override files from the main deployment do not fit: they patch the service cameraui, not cameraui-worker, so layering one on top starts a second, unrelated container. Put the image flavor that matches the hardware and the device into the worker file itself:\n\nservices:\n cameraui-worker:\n image: ghcr.io/cameraui/camera.ui:intel # or :nvidia, :amd\n devices:\n - /dev/dri:/dev/dri\n\nFor NVIDIA, take the environment and deploy blocks from the NVIDIA override as well, and install the NVIDIA Container Toolkit on the worker machine. On bare-metal and in the desktop app there is nothing to pass through, the host driver is all it takes.\n\nWhich decoder a camera actually uses on a worker is set per camera, under Frame Worker in its settings. The second selection there applies while the camera runs on a worker, so master and worker can sit on different hardware. Hardware acceleration covers host drivers and how to check what the container really got."},{"page":"admin/worker-setup","title":"Worker setup","heading":"Docker Swarm","anchor":"docker-swarm","text":"Swarm services can't pass devices: devices: is ignored, and a device mounted as a volume shows up in the container but can't be opened. The container logs it as blocked, and detection falls back to the CPU. Run each worker as a plain container on its machine with the compose file above. Swarm scheduling is not needed, the master assigns the work.\n\nTo keep a worker in the stack anyway, start it through a launcher service such as swarm-launcher. It runs the worker file as a normal compose project on the node, devices: included, and removes it when the service stops:\n\nconfigs:\n worker-3:\n file: ./docker-compose.worker.yml\n\nservices:\n worker-3:\n image: registry.gitlab.com/egos-tech/swarm-launcher:latest\n configs:\n - source: worker-3\n target: /docker-compose.yml\n environment:\n LAUNCH_PROJECT_NAME: cameraui-worker-3\n volumes:\n - /var/run/docker.sock:/var/run/docker.sock\n stop_grace_period: 40s\n deploy:\n placement:\n constraints:\n - node.hostname == swarm3\n\nKeep LAUNCH_PROJECT_NAME fixed, the worker's data volume belongs to that project. Bind mounts in the worker file need absolute host paths."},{"page":"admin/worker-setup","title":"Worker setup","heading":"Environment variables","anchor":"environment-variables","text":"The snippet values can also be set as environment variables. CAMERA_UI_WORKER=true starts worker mode without the --worker option, and CAMERA_UI_WORKER_MASTER, CAMERA_UI_WORKER_API_PORT, CAMERA_UI_WORKER_PAIRING_CODE, CAMERA_UI_WORKER_NAME and CAMERA_UI_WORKER_CAPABILITIES (comma-separated) cover the rest of the snippet. No camera.ui.yaml is needed then, and if you set both, the environment variable wins.\n\nWithout capabilities the worker offers everything; the master still only assigns what you give it. Set them to restrict a worker to one job, for example only hosting plugins."},{"page":"admin/workers","title":"Workers","heading":"Workers","anchor":"","text":"A worker is a second machine that takes on part of a server's processing. The main server (the master) keeps the UI and the settings; workers contribute compute. Manage them in Workers (in the menu).\n\nA worker decodes the cameras assigned to it, or runs an entire plugin, for example a detection backend that needs a GPU the master doesn't have, or a plugin the master's platform can't load."},{"page":"admin/workers","title":"Workers","heading":"How workers work","anchor":"how-workers-work","text":"A worker is a regular camera.ui installation started in worker mode. It has no UI of its own and holds no cameras; it connects to the master and waits for work.\n\n- The worker connects outward. Pairing goes to the master's HTTPS port, the running connection to the master's worker port (default 7422). Only the master has to be reachable; nothing needs to be opened on the worker machine.\n- Paired once, then on its own. A one-time pairing code is exchanged for the worker's own credentials. Nothing is shared between workers, each one can be revoked independently, and after the first start the worker reconnects by itself.\n- Assignments follow the worker. Cameras and plugins you assign run on the worker, fall back to the master while it's offline, and move back when it returns (see Failover).\n\nMaster and worker can mix installation types: a Docker server with a desktop app worker, a desktop-app server with a Docker worker, and so on. The camera.ui version should match across machines; the workers list warns when it doesn't."},{"page":"admin/workers","title":"Workers","heading":"Enable workers","anchor":"enable-workers","text":"Turn on Enable Workers on the master and set a Master Address, the LAN IP or hostname other machines use to reach this server. Workers can't be enabled without an address. A Worker Connection Port (default 7422) is also required; changing it later means already-paired workers must be re-paired.\n\nA new master address needs no new pairing. Set it as the master address on each worker and restart the worker: it checks that the master behind the address is the one it was paired with and connects there. This works for the master's private IPs and the Master Address set here. In the same local network a worker finds the master by itself, for example after a move: when the master has not answered for a minute, the worker looks for it on the network and restarts to connect there.[^discovery]"},{"page":"admin/workers","title":"Workers","heading":"Pair a worker","anchor":"pair-a-worker","text":"Generate Pairing Code creates a one-time code (valid for 15 minutes) and a configuration snippet:\n\nworker:\n master: \n apiPort: \n pairingCode: \n name: my-worker\n capabilities:\n - frameDecoding\n - pluginHost\n\nname is how the worker shows up on the master. Without it, the worker uses its host name.\n\nBelow the code the master shows its fingerprint. The worker logs the same fingerprint when it pairs; a different one means it paired with another machine.\n\nOnly the first start needs the code, and it may stay in the configuration afterwards: a used code is never sent again. A worker that starts before the master is reachable keeps trying, at least once a minute. A worker without a code, or with one the master refused, keeps running and waits for a new one. It reads camera.ui.yaml every 30 seconds, a new code in an environment variable takes effect when the container restarts.\n\nHow the snippet reaches the worker depends on the installation, see Worker setup."},{"page":"admin/workers","title":"Workers","heading":"The workers list","anchor":"the-workers-list","text":"Once paired, a worker appears in the Workers list with its status, the number of its cameras and plugins that run, platform (OS/architecture), process ID, version, and live CPU and memory use. The status is Connecting until a new or restarted worker checks in, then Online, Not reachable while its answers stay away, Restarting while it restarts or updates, Offline, Outdated for a release this master cannot work with, and Disabled once workers are turned off. Hover it for the reason and the time from which its cameras and plugins run elsewhere, and the count for what each one is doing. An orange dot means the worker's version differs from the master's. When a worker is behind, a dot appears on the Updates entry in the navigation.\n\nA worker that fails to link up at startup, or loses the link later, logs the reason in its log instead of looking online. Cameras and plugins running on a worker show up in Metrics with the worker's name and its load.\n\nThe Updates page lists every lagging online worker next to the server and the plugins, and a lagging worker shows an Update button right in the list too: the worker installs the version the master is heading for and restarts itself. The update counts as done once the worker runs that version; one that failed, or that the worker rolled back, shows why. A worker can't be restarted while it updates. Normally that is the version the master runs; when a server update is waiting, it is that one, so the worker does not have to be updated twice. An update that needs a newer Node.js on the master does not count until Node.js is updated. Desktop-app workers update the same way. See Updating.\n\nA worker on a release this master cannot work with shows Outdated. It runs nothing, its cameras and plugins run elsewhere until it is updated, and its Update button and the Updates page install the new version on it. If the worker refuses, the message says why: a desktop app that cannot install updates remotely, and a worker on a release too old to take updates from the master, are updated on their own machine, see Updating."},{"page":"admin/workers","title":"Workers","heading":"Remove or replace a worker","anchor":"remove-or-replace-a-worker","text":"Removing a worker revokes its credentials and drops the connection; its cameras and plugins stop there and fall back to the main server. A worker that was online keeps running and waits for a new pairing code; the old code in its configuration does not pair it again. One that was offline logs the rejection when it returns and keeps trying until it gets a new code. A new code in camera.ui.yaml pairs it within 30 seconds, one in an environment variable once the container restarts.\n\nTwo machines that run with one worker's identity, from a copied worker folder or a restored backup, get no cameras or plugins: the master stops the work on both and logs it. Remove the worker and pair one of them again. A second machine needs its own, empty storage directory to pair as another worker: while one identity is online, the master refuses to pair it a second time. To hand the identity to new hardware while the old machine still runs, pick the worker under Replaces before you generate the code; the machine that pairs with it takes over and the old one loses its access.\n\n[^discovery]: Through mDNS. A worker in a Docker container needs host networking for it."},{"page":"assistant/","title":"Assistant","heading":"Assistant","anchor":"","text":"The Assistant answers questions about your instance in plain language: what happened at home today, whether every camera is online, which sensors are active, what a camera sees right now. The answers come from a language model you bring, and the model reaches your instance only through a fixed set of tools. Anything that changes something is shown to you first and runs after you confirm it.\n\nOnce an admin has set it up, every user sees the Assistant in the navigation, and the sparkle button in the top bar opens it on any page: as a side panel, or as a small window in the corner that leaves the page usable while you chat."},{"page":"assistant/","title":"Assistant","heading":"Asking","anchor":"asking","text":"The assistant answers in your interface language unless Answer language is set to a fixed one. Every tool it used appears as a card under the answer with its arguments and result.\n\nIt also works the app for you: \"show me the garden camera\", \"open the recording of the gate from 7:12\" or \"go to the automations\" opens the matching page, and on a camera page it moves and zooms the timeline. It does that only when you ask to see or do something, a plain question is answered in the chat. The recordings page has its own box for the assistant, see Browsing recordings.\n\nWhen a request is ambiguous the assistant asks back with a small form: pick a camera, choose a time range, or type an answer. Skip the question and it continues with an assumption it names. A recap of a day or a system check comes back as a card with one row per camera or component, and the rows open the moment they describe.\n\nNext to the text box sits a microphone: the browser turns speech into text, camera.ui never receives audio. It shows up in browsers that offer speech recognition, on a secure connection, and in the iOS and Android apps, which ask for microphone and speech recognition access the first time.\n\nThe paper clip attaches your own files, pasting a picture works too: pictures up to 12 MB, audio up to 25 MB, video up to 60 MB, eight files per message. The assistant hands them to the detection plugins. Audio and video never go to the language model, only to the plugins. Attached pictures stay with the conversation, audio and video are analyzed once and not kept.\n\nA recording card and a camera's menu both offer Ask the assistant, which opens the chat with that event or camera already named. During playback the camera's menu asks about the moment on screen instead of what the camera sees now."},{"page":"assistant/","title":"Assistant","heading":"Working with a conversation","anchor":"working-with-a-conversation","text":"Hover a message for its actions. A question can be edited and sent again, which discards everything after it. An answer can be regenerated, copied, or deleted together with its question. Branch from here copies the conversation up to that point into a new one. A stopped answer offers Continue. You can type the next message while an answer is still running; it waits below the text box and goes out when the answer is done.\n\nThe sliders button in the text box opens the settings of this conversation. Instructions apply to every answer in it, \"answer in one sentence\" for example, and keep working in long conversations where early messages drop out of what the model sees. Tools lists the tool groups (camera.ui, the documentation, one per plugin); a removed group is hidden from the model in this conversation. With more than one model the panel also picks the Model, and the conversation keeps it instead of the default. A combination of model, tool groups and instructions can be saved as a profile; profiles belong to your account.\n\nConversations are stored per user on the server, together with the pictures and the open-this buttons of the last tool calls. An answer that is still streaming survives a bad connection, a reload or a second tab: the browser picks the stream up where it broke off instead of asking the model again, and a confirmation that was waiting is shown again after a restart. Stop ends the run on the server too, closing the tab alone does not."},{"page":"assistant/","title":"Assistant","heading":"What the assistant remembers","anchor":"what-the-assistant-remembers","text":"Facts you mention or ask it to remember (\"remember that our dog is called Bruno\", \"the Gate camera watches the street\") are kept across conversations and given to the model every time. After each exchange a short model call picks such facts out on its own; events and anything temporary are never stored. A model with a context window below 16,000 tokens skips that step and keeps only what you ask it to remember. The brain button in the text box shows the list under What the assistant remembers about you, and each entry can be forgotten. Admins can turn the feature off."},{"page":"assistant/","title":"Assistant","heading":"What the assistant can do","anchor":"what-the-assistant-can-do","text":"The tools decide what the assistant can see and do. The Tools card at the bottom of Settings → Assistant lists them, one tab per provider: camera.ui itself and every plugin that contributes tools.\n\nThe camera.ui set is generic: snapshots, the sensors with their current state and switching one, push notifications to your own devices, the live load of the instance, the house layout, a health overview, and three generic API tools. Those three give the assistant the whole camera.ui REST API: it searches the endpoint that fits a question, reads it with your permissions, and changes something only after you confirm. That covers plugins, workers, sessions, automations, users and zones. Two instance tools cover what the API does not: discovering and adopting cameras and sensors, the notification history, logs, update checks, MQTT and process states. Credentials in the answers, stream passwords for example, are removed before the model sees them, and endpoints for login, configuration files, backups and downloads are never offered.\n\nThe assistant can also run a detection plugin (object, face, license plate or CLIP) on a fresh snapshot, a short sequence of snapshots or the picture of a recorded event.\n\nWith the NVR plugin it reaches your recordings: events and episodes in a time range, a summary of a day or a whole week, events searched by description, event pictures, known faces and when someone was last seen. Ask for the clip of a moment and it builds one with a download button, or sends it to your phone as a video notification. Faces the recognition could not name can be named, ignored or corrected in the chat. It also lists the license plates read in a period, tells when a camera is busiest and where in the picture the activity was, and shows the stretches a camera did not record.[^license]\n\nThe assistant also knows the user documentation: it looks up how a feature works and where it lives in the app, and points there when it cannot do something from the chat. It never asks for passwords in the chat.\n\nTools that change something (switching a sensor, running an automation, sending a notification, marking a favorite, adopting a device) are marked asks first. The chat shows what the assistant wants to do with which arguments, Edit lets you correct the values, and it runs after Allow. Tools marked admin are not offered to users with the user role."},{"page":"assistant/","title":"Assistant","heading":"Text alerts and watching a camera","anchor":"text-alerts-and-watching-a-camera","text":"With the NVR plugin the assistant can watch your cameras for a description: \"let me know when a delivery person comes\", \"alert me when a dog is in the garden\". It saves a text alert, shows you past events that would have fired so you can judge the wording, and from then on every new event whose picture looks like the description sends a push with the picture. The picture match calibrates itself on your recordings, and when the NVR plugin may use a model that model looks at the pictures of every hit and only lets a push through when it agrees, with its reason as the push text. Alerts can be limited to cameras, can get a fixed match threshold instead of the automatic one (lower fires more often) and have a cooldown between two pushes. To change one, ask the assistant. Settings → Assistant lists them under Text alerts. The menu of each one pauses or deletes it, or picks another model for the picture check than the one the NVR plugin has under Plugin access. An alert can also drive an automation: \"when the parcel alert fires, switch on the porch light\" becomes a flow with a System event trigger on the plugin's notifications.\n\n\"Keep an eye on the driveway for the next two hours\" is the same thing with an end: the alert deletes itself at that time. Every hit appears in the conversation with the picture and a button to the recording, and as a push; when the watch ends the assistant posts a short summary. Nothing is streamed to the model in between, it only looks at pictures when the cameras record something.\n\nBoth need a CLIP plugin, the same one the description search uses."},{"page":"assistant/","title":"Assistant","heading":"Automations and scheduled prompts","anchor":"automations-and-scheduled-prompts","text":"Automations can ask the assistant: the Ask the assistant action takes a question with the variables of the flow (\"Look at the picture from {{camera.name}}: is this a delivery?\"), optionally the picture of a snapshot node, and delivers the answer to one user as push, as a conversation, or only as a variable for the next nodes. A Profile of that user sets the model, tools and instructions. When nothing is worth telling, the assistant stays silent and nothing is sent.\n\nIt also builds automations from a description: \"when someone is on the terrace after 22:00, send me a push with a picture\". The assistant assembles the flow from the same node types the editor uses, shows you the nodes and connections, and creates it once you allow it. The result opens in the automation editor like any other flow. Changing or running an existing automation works the same way. This needs the admin role.\n\nA scheduled prompt is a question the assistant answers on a schedule, a summary of the day every evening for example. Ask for it in the chat or add one under Scheduled prompts with a title, the prompt, the time and the delivery: a push notification, a saved conversation, or both. An optional Profile runs it with that profile's model, tools and instructions. Model picks the model for this schedule alone, later through Change model in the schedule's menu; left empty, the profile's model or the default answers. Run now tries a schedule right away. Scheduled runs skip every tool that would ask for confirmation, and the answer comes in the language you used when you created the schedule. Up to 20 schedules per user.\n\nA push holds two short sentences that fit the lock screen. With push and conversation together, the push carries the opening sentence and the conversation the whole answer, in scheduled prompts and in Ask the assistant alike.\n\nThe answers come from a language model and can be wrong. Where it matters, check the recording."},{"page":"assistant/","title":"Assistant","heading":"Next steps","anchor":"next-steps","text":"Assistant setup — models, what plugins may use them, limits and the MCP endpoint.\n\n[^license]: The recordings tools build on recording (NVR), which requires an active camera.ui subscription."},{"page":"automations/blueprints","title":"Blueprints & store","heading":"Blueprints & store","anchor":"","text":"A blueprint is an automation saved as a file, so you can back it up, move it between servers, or share it with someone else."},{"page":"automations/blueprints","title":"Blueprints & store","heading":"Exporting","anchor":"exporting","text":"Open an automation and choose Export blueprint. camera.ui downloads a .blueprint.json file containing the flow's nodes and connections, but not its secrets, such as webhook keys."},{"page":"automations/blueprints","title":"Blueprints & store","heading":"Importing","anchor":"importing","text":"On the Automations list, choose Import blueprint and pick a .blueprint.json file. It is added as a new automation, disabled. Imported flows get fresh internal IDs, so importing the same blueprint twice won't clash."},{"page":"automations/blueprints","title":"Blueprints & store","heading":"Sharing","anchor":"sharing","text":"Export the automation and send the file; the other person imports it on their server."},{"page":"automations/blueprints","title":"Blueprints & store","heading":"Community blueprints","anchor":"community-blueprints","text":"Browse community automations on the Automations list opens the store of blueprints shared by the camera.ui community.\n\nA blueprint's page lists what it needs: required plugins (with a link to the plugin store if one is missing) and required inputs such as a camera, notification recipients or a sensor. Import then asks for your own cameras, sensors, plugins and recipients, plus plain values such as an MQTT topic, some prefilled with a default. The page doesn't list the plain values, so expect a few more fields during import. A blueprint with no references to bind imports directly. Either way it lands disabled.\n\nThe store is a public repository, cameraui/automations, that camera.ui reads at runtime. There is no upload from inside the app: to add one, open a pull request against that repository with your exported blueprint."},{"page":"automations/","title":"Building automations","heading":"Building automations","anchor":"","text":"Automations connect what happens at your cameras to actions: drag nodes onto a canvas and wire them into a flow. Automations are an admin feature."},{"page":"automations/","title":"Building automations","heading":"How a flow works","anchor":"how-a-flow-works","text":"A flow runs from left to right: a trigger starts it, optional conditions decide whether it continues, and actions do something. Utilities help pass data between steps.\n\nA new flow (+ on the Automations page) starts empty and disabled."},{"page":"automations/","title":"Building automations","heading":"Triggers","anchor":"triggers","text":"A flow can have more than one trigger.\n\n- Detection event. Something detected at a camera: motion, a person, vehicle, or animal, an audio event like a doorbell or glass breaking, a recognized face, or a license plate. Filter by object, confidence, and event phase (start, update, end).\n- Sensor change. A sensor's state changes, like a contact sensor opening. This also covers your cameras' detection sensors: motion, objects, faces, license plates, and classifiers. Face, plate, and classifier sensors carry what was recognized, so a flow can react to a specific person, car, plate, or result, for example a bird classifier that reports the species.[^detvssensor]\n- Schedule. A recurring time, set with a cron expression.\n- System event. Something in camera.ui itself, such as a camera connecting or disconnecting, a plugin starting or stopping, or a plugin sending a notification. The last one turns every plugin push into a trigger, a text alert of the NVR plugin for example: its title and data fields become variables, so an If/Else condition can pick the one you mean.\n- Webhook. An external service calls a URL to start the flow.\n- MQTT message. A message arrives on an MQTT topic. Set the topic (+ and # wildcards work) and choose how to match: any message, an exact payload, or a value at a JSON path like params.switch:0.output. Needs the MQTT connection set up in Settings.\n- Geofence. A user enters or leaves a location you define.\n- Manual. Started with the Run button, for testing.\n\nVirtual sensors are set with a Control sensor action and trigger through Sensor change."},{"page":"automations/","title":"Building automations","heading":"Conditions","anchor":"conditions","text":"- If / else. Branch on a comparison.\n- Switch. Branch on several possible values.\n- Sensor state. Check one or more sensors, combined with AND or OR. Each value can be a fixed value or a variable from an earlier step. For a detection sensor you can check a specific value, like a recognized name, a plate, or a classifier label. When a sensor holds several values at once, the check passes if any one matches, and matching ignores upper and lower case.\n- Time. Only continue within a time-of-day range and on chosen days."},{"page":"automations/","title":"Building automations","heading":"Actions","anchor":"actions","text":"- Send notification. Push an alert with a title, message, and severity.\n- Notifications on/off. Mute or unmute push. Under Applies to pick Everyone, One camera, or One user. Muting one camera leaves the others notifying. Muted alerts still land in the in-app bell and the events still show up on the timeline. Critical alerts always come through.\n- Capture snapshot. Take a fresh image from a camera.\n- Control sensor. Set an accessory, such as turning on a light or locking a door. Values can be fixed or taken from a variable.\n- Camera control. Change one or more of a camera's settings in one step: snooze detections, disable the camera, recording (on/off, mode, pre-buffer, and which streams it records), detection thresholds and timeouts, PTZ autotrack (on/off, which object types it tracks, return to home, minimum confidence), and snapshot refresh.\n- HTTP request. Call an external service.\n- MQTT publish. Send a message to an MQTT topic. Set the topic and the payload, and turn on Retain if the broker should keep the message for anyone subscribing later. Wildcards aren't allowed in the topic. Needs the MQTT connection set up in Settings.\n- Plugin call. Run a detection or analysis plugin on an image.\n- Ask the assistant. Hand the situation to the assistant and use its answer: pick the user it answers for, write the question with the flow's variables, optionally pass a picture, and deliver it as push, as a conversation, or only as a variable for the next steps.\n- Set variable and delay. Hold a value, or wait before the next step."},{"page":"automations/","title":"Building automations","heading":"Utilities","anchor":"utilities","text":"- Image input. Load an image (from a URL, a variable, or an upload) for an action to use.\n- Output. Collect results to view after the flow runs."},{"page":"automations/","title":"Building automations","heading":"Passing values between steps","anchor":"passing-values-between-steps","text":"Steps hand values on through variables. A node that produces something offers Add output variable in its settings: name it, and every step wired after it can use the value. Variables on a step lists what is available; if it is empty, the source node isn't connected yet.\n\nWrite a variable as {{name}} in any text field. Fields that only take one value, like a sensor value or a condition, let you pick the variable from a list instead of typing it. What each trigger and action provides, and which values it can take, is listed under Variables.\n\nSet variable stores a value for steps further down. Alias on a node prefixes its output variables, so result from two different plugins becomes cam1.result and cam2.result instead of clashing.\n\nA misspelled name is caught on save, with Unknown variable naming the one it could not resolve."},{"page":"automations/","title":"Building automations","heading":"Options and running","anchor":"options-and-running","text":"The toolbar holds the name, Enabled, and two behaviors:\n\n- Ignore repeated events, so a burst of triggers runs the flow once.\n- Wait for completion, so a new trigger waits until the current run finishes.\n\ncamera.ui disables an automation automatically if it points at a camera or sensor you've removed, with a notice to fix it. The list shows each automation's last run and lets you enable, disable or delete several at once.\n\nTo share automations, see Blueprints & store.\n\n[^detvssensor]: A Detection event fires on each raw event as it happens. A Sensor change reacts to the sensor's own state, which keeps the recognized names, plates, or labels for as long as the subject stays in view."},{"page":"automations/variables","title":"Automation variables","heading":"Automation variables","anchor":"","text":"Every trigger fills a set of variables that later steps use as {{event.label}}, in a notification, the body of an HTTP Request, or a condition. Every value is text: numbers come as digits, lists as comma-separated text, objects as JSON. How to wire steps and name your own variables is on Building automations.\n\nIf / Else and Switch suggest the values a variable can take for the trigger or sensor you picked."},{"page":"automations/variables","title":"Automation variables","heading":"Detection event","anchor":"detection-event","text":"| Variable | Value |\n|---|---|\n| event.id | ID of the event |\n| event.cameraId | ID of the camera |\n| event.state | active, or ended on Event End |\n| event.types | Everything the event contained so far, comma-separated[^types] |\n| event.type | First entry of event.types, usually what started the event: motion or audio |\n| event.label | The detected object (person, vehicle, animal), otherwise the audio event, otherwise event.type |\n| event.confidence | Best score from 0 to 1, 0 when nothing reported one (plain motion) |\n| event.faces | Recognized names, comma-separated |\n| event.plates | Recognized license plates, comma-separated |\n\nFaces, plates and the detected object travel with the segments, the stretches in which an object is tracked. Event Start, Event Update and Event End don't carry them: event.faces and event.plates stay empty and event.label falls back to the audio event or event.type. To send who or what was seen, set Trigger on to Segment Start, Segment Update or Segment End.\n\nAudio events are doorbell, glass_break, siren, speaking, gunshot, dog_bark, baby_cry, alarm, scream, cat, car_alarm and smoke_alarm."},{"page":"automations/variables","title":"Automation variables","heading":"Sensor change","anchor":"sensor-change","text":"| Variable | Value |\n|---|---|\n| sensor.property | The property that changed, one of the sensor's Properties |\n| sensor.value | The new value: true or false for on/off and detected states, a number for levels and readings, text for names and labels |\n| sensor.previousValue | The value before the change |\n| sensor.sensorType | The kind of sensor, such as contact, motion, object, face, licensePlate or temperature |\n| sensor.sensorId | ID of the sensor |\n| sensor.assignedCameraIds | IDs of the cameras the sensor belongs to, comma-separated |\n\nA value with several parts also appears field by field: a PTZ position as sensor.value.pan, a list of recognized names as sensor.value.0, sensor.value.1 and sensor.value.length."},{"page":"automations/variables","title":"Automation variables","heading":"System event","anchor":"system-event","text":"system.eventType holds the event picked in the trigger: system:started, system:shutdown, camera:added, camera:removed, camera:connected, camera:disconnected, camera:frameworker:started, camera:frameworker:stopped, camera:property:changed, sensor:added, sensor:deleted, plugin:started, plugin:stopped, plugin:error or plugin:notification.\n\n| Variable | Set by | Value |\n|---|---|---|\n| system.cameraId, system.cameraName | Camera events | The camera |\n| system.property | Property Changed | The settings group that changed: name, sources, detectionSettings, ptzAutotrack, recordingSettings, notificationSettings, zones, frameWorkerSettings or interfaceSettings |\n| system.pluginId, system.pluginName | Plugin events | The plugin |\n| system.status | Plugin Started, Plugin Stopped, Plugin Error | started, stopped or error |\n| system.sensorId, system.sensorType, system.sensorName | Sensor Added, Sensor Deleted | The sensor |\n\nPlugin sends a notification adds system.title, system.subtitle, system.body, system.tag, system.severity (info, warn, error or critical) and every data field the plugin attaches. The NVR plugin sets system.type to detection, episode, text-alert, text-alert-end, camera_online, camera_offline, storage_paused, storage_resumed, storage_path_rejected or cloud_reauth, and depending on the type system.cameraId, system.cameraName, system.eventId, system.episodeId, system.alertId and system.alertTitle."},{"page":"automations/variables","title":"Automation variables","heading":"Webhook","anchor":"webhook","text":"A webhook is called with POST and a JSON object as body.\n\n| Variable | Value |\n|---|---|\n| webhook.body | The whole body as JSON text |\n| webhook. | Each top-level text, number or true/false field: {\"room\": \"hall\"} gives webhook.room with hall |\n| webhook.headers | The request headers as JSON |\n| webhook.method | POST |"},{"page":"automations/variables","title":"Automation variables","heading":"MQTT message","anchor":"mqtt-message","text":"| Variable | Value |\n|---|---|\n| mqtt.topic | The topic the message arrived on, useful with wildcards |\n| mqtt.payload | The payload as text |\n| mqtt. | Each top-level text, number or true/false field of a JSON object payload |"},{"page":"automations/variables","title":"Automation variables","heading":"Geofence","anchor":"geofence","text":"| Variable | Value |\n|---|---|\n| geo.event | enter or leave |\n| geo.user | The user who moved |\n| geo.zone | Name of the zone |\n| geo.lat, geo.lon | The reported position |\n| geo.distance | Distance to the zone's center in meters, rounded |\n\nSchedule and Manual start a flow without variables."},{"page":"automations/variables","title":"Automation variables","heading":"Results of actions","anchor":"results-of-actions","text":"| Variable | Step | Value |\n|---|---|---|\n| snapshot | Capture Snapshot | The JPEG as base64 |\n| snapshot.base64 | Capture Snapshot | The JPEG as a data URL, data:image/jpeg;base64,… |\n| http.status | HTTP Request | Status code, such as 200 |\n| http.body | HTTP Request | The response as text, base64 for images and other binary data |\n| http.base64 | HTTP Request | A binary response as a data URL |\n| http.json, http. | HTTP Request | A JSON response as text, and each of its top-level fields |\n| plugin.detected | Plugin Call | true or false |\n| plugin.labels | Plugin Call | Found labels, comma-separated |\n| plugin.detections, plugin.result | Plugin Call | The detections and the full result as JSON |\n| plugin.durationMs | Plugin Call | Run time in milliseconds |\n| assistant.answer | Ask the assistant | The answer as text |\n| assistant.replied | Ask the assistant | false when the assistant found nothing worth telling |\n| camera.id, camera.name | Camera Control | The camera it changed |\n| previous.success | Most actions | true or false for the last step that ran |\n| previous.result | Most actions | snapshot_captured, snapshot_failed, detected, not_detected, no_response, assistant_replied or assistant_silent |"},{"page":"automations/variables","title":"Automation variables","heading":"Time and arithmetic","anchor":"time-and-arithmetic","text":"{{time.now}} is the current time in milliseconds since 1970, in any flow.\n\n{{= … }} calculates with numbers: {{= event.confidence * 100 }} gives the confidence in percent. It knows +, -, *, /, % and parentheses, rounds results with decimals to two places, and counts a variable that isn't a number as 0.\n\n[^types]: motion and audio, the objects person, vehicle and animal, face and license_plate, a classifier result such as bird, and the event types a camera reports itself: contact, doorbell, switch, light, siren, security_system, line-crossing, occupancy, smoke, leak, gas, carbonMonoxide, heat, cold, vibration, tamper, problem, power, lock and garage."},{"page":"cameras/add-camera","title":"Adding a camera","heading":"Adding a camera","anchor":"","text":"There are two ways to add a camera: let camera.ui discover it, or add it manually with its stream URL. Both start from the Cameras page."},{"page":"cameras/add-camera","title":"Adding a camera","heading":"Before you start","anchor":"before-you-start","text":"- The camera is on the same network as your server.\n- For network discovery, enable ONVIF in the camera's own settings.\n- Have the camera's username and password ready. For a manual add, also have its RTSP URL (from the camera's manual or app)."},{"page":"cameras/add-camera","title":"Adding a camera","heading":"Discover a camera","anchor":"discover-a-camera","text":"The Cameras page lists devices that have been discovered, ready for you to add. A camera shows up here when camera.ui's built-in discovery finds it on your network (ONVIF and other protocols), or when a plugin that manages cameras reports one. Cameras you have already added appear under Adopted cameras. Discovery and adding are for admins; other users open the same Cameras page as a plain list and tap through to any camera.\n\n1. Open Cameras. Discovery runs automatically. If a camera doesn't appear, select Rescan.\n2. Select a camera under Discovered. If asked, enter username and password (a few cameras use a PIN) and select Connect.\n3. Confirm camera shows the Name, Room and Sources camera.ui read from the camera. Adjust them if needed.\n4. Select Add camera. The camera moves to Adopted cameras.\n\nA plugin also tells camera.ui whether a camera runs on battery and whether it is a doorbell. A battery camera is added so it can sleep: hot mode off, new pictures Only on request, live view in Standby and Ad-hoc recording. A doorbell is added as a doorbell.\n\nThe eye icon hides a device; Show hidden devices brings it back."},{"page":"cameras/add-camera","title":"Adding a camera","heading":"Add a camera manually","anchor":"add-a-camera-manually","text":"For cameras that aren't discovered, or to set up streams yourself, select Add camera on the Cameras page.\n\n1. Give the camera a Name, and optionally assign it to a Room.\n2. Under Sources, set up at least one stream:\n - Enter a Source name.\n - Pick a Source role: High Resolution, Mid Resolution, or Low Resolution for streaming, or Snapshot for stills. At least one streaming role is required.\n - Paste the full stream URL including the protocol, for example rtsp://user:pass@192.168.1.50:554/stream. camera.ui detects the protocol from the URL and shows Detected protocol under the field. Use the (i) button next to the field for protocol-specific help, or Test camera source to preview a frame.\n3. Select Add source to add another URL to the same source. To add a second source with another role, for example a low-resolution stream for fast scrubbing, use the plus button below the Sources card.\n\nThe NVR plugin, if installed and enabled, is activated for every new camera automatically, whether you added it manually or adopted it from Discovered. Recording then starts on its own as long as your subscription has a free camera slot. You can change the mode or turn recording off per camera, see Recording."},{"page":"cameras/add-camera","title":"Adding a camera","heading":"Sources and roles","anchor":"sources-and-roles","text":"A camera can have several sources, each with a role:\n\n- High / Mid / Low Resolution are streaming tiers. camera.ui uses the one that fits the situation, such as a low-resolution stream in a multi-camera grid and a high-resolution one at full screen.\n- Snapshot provides still images.\n\nOne source is enough; a lower-resolution stream makes grids and playback lighter. Each source needs its own name within the camera; a name already in use is rejected with a note under the field. Once a source is saved its name is fixed."},{"page":"cameras/add-camera","title":"Adding a camera","heading":"Source options","anchor":"source-options","text":"- Use for snapshot. The camera's snapshots come from this source: the cards, Home Assistant, HomeKit, automations and the API. A source with the Snapshot role always comes first. Without either, snapshots come from the lowest resolution stream, so turn this on for the main stream if you want sharper ones.\n- Hot mode. Keeps the connection to the camera always active, so there's no reconnect delay when a stream is opened.\n- Preload. Preloads the stream so the camera view opens with minimal delay.\n- Mute audio. Removes the audio track from this source. Off by default.\n- Disable two-way audio. Removes the talk channel from this source, camera.ui no longer requests it from the camera. Off by default.\n- Stream timeout. Seconds without video before the stream is reconnected. Left empty it is 5 seconds for a camera you entered yourself and 60 for a source a plugin provides, since the plugin already watches its camera.\n- Connection timeout. Seconds the camera gets for each step of connecting. Raise it for cameras that wake up slowly, a battery model for example. Empty means 5.\n\nWith hot mode and preload on, opening a stream is near-instant, whether the camera uses H.264 or H.265."},{"page":"cameras/add-camera","title":"Adding a camera","heading":"Connection protocols","anchor":"connection-protocols","text":"RTSP works with almost any IP camera and is the usual choice, while ONVIF powers discovery. The protocol list also covers specific brands and systems, some of which connect directly and others through a plugin. See Connection protocols for the full list.[^cloud]\n\n[^cloud]: Cameras that depend on a vendor cloud (such as Ring or Nest) can be connected, either directly or through a plugin, but they are not recommended and not covered by support. Use local RTSP / ONVIF cameras for a reliable experience."},{"page":"cameras/camview","title":"Camview","heading":"Camview","anchor":"","text":"Camview shows several cameras at once in a grid."},{"page":"cameras/camview","title":"Camview","heading":"Create a layout","anchor":"create-a-layout","text":"1. Open Camview and select New View.\n2. Give the view a name and pick a grid size, from a single camera up to 26.\n3. Select Next and fill the slots: drag cameras in from the list on the right, or on a narrow screen tap the plus in a slot."},{"page":"cameras/camview","title":"Camview","heading":"Arrange the grid","anchor":"arrange-the-grid","text":"Turn on Rearrange to drag tiles around. On a computer you can also resize a tile by its bottom-right corner, with two helpers:\n\n- Auto-arrange packs the tiles together with no gaps.\n- Fit aspect ratios sizes each tile to its camera's configured aspect ratio.\n\nOn phones the cameras sit in one column, so Rearrange only changes their order, and resizing and the rearrange helpers aren't available there. A view itself is saved to your account. The arrangement is kept in the browser you made it in, and phones and computers keep their own."},{"page":"cameras/camview","title":"Camview","heading":"Fit the picture to the card","anchor":"fit-the-picture-to-the-card","text":"In Rearrange, the Picture in the card button offers four choices:\n\n- Keep aspect ratio (default) and Fit inside show the whole picture, with black bars where the shapes differ.\n- Crop to fit fills the card and cuts off the edges.\n- Stretch to fit fills the card and distorts the picture.\n\nThe choice is saved with the view, so it follows your account to every device, and it applies to live and to playback. Expanding a tile, fullscreen and zoom always show the whole picture again. The button is on computers only, but a view keeps the fit you picked there when you open it on a phone."},{"page":"cameras/camview","title":"Camview","heading":"Control all cameras at once","anchor":"control-all-cameras-at-once","text":"A floating control in the corner acts on every camera in the view together:\n\n- Fullscreen the whole grid.\n- Mute or unmute all.\n- Two-way talk to all cameras at once.\n- Switch the streaming mode for all.\n- Set one activity mode for all (Always On, Standby, Activity), or Per Camera to let each camera keep its own activity mode. Per Camera is the default.\n- Change the resolution for all.\n- Turn the detection markers on or off."},{"page":"cameras/camview","title":"Camview","heading":"What a camera is seeing","anchor":"what-a-camera-is-seeing","text":"While a camera detects something its tile glows, and small icons name what was found: motion, person, vehicle, face, plate and the rest. Detections in the floating control switches both off for the whole view. The choice is saved with your account."},{"page":"cameras/camview","title":"Camview","heading":"Focus one camera","anchor":"focus-one-camera","text":"Click a tile to expand it to the full view, click it again to go back to the grid. A sleeping tile (moon icon) only wakes on that click, the next one expands or collapses it. A view with a single tile stays as it is.\n\nThe fullscreen button in a tile's control bar shows that camera alone. There the dots in the control bar also offer Shortcuts and a Timeline for this camera. If you play back recordings there, the other cameras pick up the same point in time when you leave fullscreen."},{"page":"cameras/camview","title":"Camview","heading":"Playback across the grid","anchor":"playback-across-the-grid","text":"Open the timeline to scrub through recordings for the whole view at the same time, with playback kept in sync across every camera. See Timeline & playback."},{"page":"cameras/camview","title":"Camview","heading":"Find objects across the grid","anchor":"find-objects-across-the-grid","text":"Switch the grid's timeline to Find or press F. Every tile can be picked then, each one freezes on its own while the others keep playing, and the matches of all picks form one list. The tile of a camera without object detection says No object detection. A scrub moves every frozen tile to the new moment. A tile in fullscreen has Find in its own timeline, a match of another camera opens that camera. See Find an object."},{"page":"cameras/camview","title":"Camview","heading":"Performance","anchor":"performance","text":"Camview streams each camera at its low resolution so a full grid stays smooth. A single view holds up to 26 cameras. Give your cameras a low-resolution source."},{"page":"cameras/floor-plan","title":"Floor plan","heading":"Floor plan","anchor":"","text":"The floor plan is a drawing of your home with the cameras and sensors on it, under Floor Plan in the menu. Everyone can look at it, drawing needs admin.\n\nIt is live: a camera lights up while it sees something, sensors show their state, and admins can operate them right on the plan. The drawing is also read by camera.ui itself. It knows which rooms connect, how long it takes to walk from one camera to the next, and which sensor sits at which door."},{"page":"cameras/floor-plan","title":"Floor plan","heading":"Drawing","anchor":"drawing","text":"Start with a level, one per floor. They live behind the button in the top left.\n\nThen drag pieces from the palette onto the canvas. On a phone the plus button opens the same palette.\n\n- Rooms. Drag New room in, then move and resize the rectangle. A room around a corner, an L-shaped yard for example, is drawn as several rectangles: draw the second one and set its Belongs to to the first, and the two count as one room.\n- Kind. A room is Inside, Outside, on the property, or Outside world. The last one is the street, the pavement, a neighbour's land: someone arriving from there was not seen anywhere before, so camera.ui does not look for an earlier sighting. Draw the gate or the front path as the place where they get in.\n- Connections. Rooms that only share a wall are not connected. Put a Door, an Open passage or Stairs on the shared wall, slide it along the wall and drag its dots to set the width. Between two levels the connection is always stairs.\n- Cameras. Drag a camera into the room it watches, then set Direction, Field of view and Range on the right. The cone on the plan follows, and the room you drop it in becomes its room in the camera settings. A camera without a room adds nothing to the spatial context.\n- Sensors. Same idea. A contact or a lock that reports on one specific door gets Belongs to passage, so its state is read as that door opening rather than as something happening in the room.\n- North. Drag the compass until it matches reality.\n- Note. Rooms, cameras, sensors and connections each take a free-text note for what the drawing cannot show. The AI reads it when it describes an event.\n\nCentimetre accuracy buys nothing. Rooms roughly the right size in roughly the right place, with the cameras pointing the right way, is enough.\n\nChanges are kept only when you select Save. The dots button holds undo, redo, Clear this level and Discard changes. The lock button freezes the plan, so panning around cannot move anything by accident."},{"page":"cameras/floor-plan","title":"Floor plan","heading":"Reading it","anchor":"reading-it","text":"- A camera's cone lights up while the camera sees something, with icons for what was detected.\n- Point at a camera for its live picture and Open camera.\n- Point at a sensor for its state. Admins also get an accessory's control there: a light toggles, an alarm panel offers its modes.\n- Blind spots hatches every part of a room that no camera covers."},{"page":"cameras/floor-plan","title":"Floor plan","heading":"What the plan is used for","anchor":"what-the-plan-is-used-for","text":"- Episodes use it to tell activity apart. Two things happening at the same time in different corners of the property stay separate stories, a quick walk from the hall to the drive stays one, and the title names the places. Two cameras on the same room count as two angles of one scene: the one that had the subject closest plays, and the player offers the other.\n- AI descriptions get the layout, the walking times and what nearby sensors read at the time as context.\n- Recordings gains a Rooms filter, which selects the cameras placed in those rooms."},{"page":"cameras/floor-plan","title":"Floor plan","heading":"Next steps","anchor":"next-steps","text":"Camera settings — the room a camera belongs to is set here too, and groups the home view.\nSensors — what a sensor is and how it gets to a camera."},{"page":"cameras/home","title":"Home","heading":"Home","anchor":"","text":"Home shows every camera you have access to as a card with a still image.\n\nAbove the cards, Recent Events is a row of recent moments you can scroll sideways. A visit with several phases shows one card per moment, like the timeline, and selecting a card opens it at that moment. Cards with a recognized face or a read license plate get arrows to browse the pictures, and resting your finger on a card (or hovering on a computer) plays a short preview. Episodes appear here too, as a mosaic card showing how many cameras took part. See Events & detections.\n\nThe filter button in the corner decides which cameras show which kinds of detection there: person, vehicle, animal, face and license plate, each with its own list of cameras. An event only disappears when everything detected in it is hidden, so someone walking by while a car passes still shows. Hidden events are still detected, recorded and found in Recordings. The choice belongs to your account.\n\nWith no cameras yet, Get started takes admins to add one."},{"page":"cameras/home","title":"Home","heading":"Camera cards","anchor":"camera-cards","text":"- The picture. A snapshot, not a live stream.\n- Status icons. A red camera icon next to the name means the camera is disabled. An amber moon means detections are snoozed for it.\n- Disabled cameras. The image is replaced by a dark tile with a crossed-out camera icon.\n\nSelecting a card opens the camera page with the live view, recordings, and the timeline.\n\nThree buttons sit in the bottom right of a card:\n\n- Refresh snapshot. Pulls a new picture from the camera right away.\n- Log. Opens that camera's log in a dialog, for admins. You can download it from there.\n- Settings. Opens the camera settings drawer, for admins.\n\nA disabled camera only offers Settings."},{"page":"cameras/home","title":"Home","heading":"Arranging the cards","anchor":"arranging-the-cards","text":"A floating control sits in the corner:\n\n- Group by room (with more than one camera) switches from one flat grid to sections per room, using the Room you set in the camera settings. Cameras without a room land in Default, which comes first, and the remaining rooms are sorted by name. Show all cameras switches back.\n- Edit layout / Lock layout unlocks the cards. Unlocked, you can drag them to a new place, and the eye button on a card hides it from Home or shows it again. Hidden cameras stay on the page, dimmed, until you lock the layout. The order is kept; in the grouped view you reorder within a room.\n\nHiding only changes Home. The camera keeps streaming, detecting and recording, its events still show in Recent Events, and selection mode leaves it out. Hidden cameras belong to your account.\n\nCards you never moved are sorted by name."},{"page":"cameras/home","title":"Home","heading":"Snapshots and how they refresh","anchor":"snapshots-and-how-they-refresh","text":"The card picture is a stored still image, reused across the app. The source it comes from is set with Use for snapshot. When a camera takes a new one is set under New picture in the Snapshot section of the camera settings:\n\n- Regularly. A timer takes a picture every 10 seconds to 1 hour (60 seconds by default). Looking at the camera never wakes it.\n- When viewed. A new picture only when someone looks at the camera and the stored one is older than the age you set.\n- Only on request. The camera is left alone until an automation, Refresh snapshot or the API asks. Made for battery cameras.\n\nA picture taken by an automation or the refresh button shows up on the cards right away, in every mode. Disabled cameras are never asked for a new one.\n\nIf a camera is offline or slow to answer, the card keeps the last picture it had rather than going blank."},{"page":"cameras/home","title":"Home","heading":"The snapshot age badge","anchor":"the-snapshot-age-badge","text":"The badge in the top right corner of a card shows how old its picture is, counting up every second.\n\nIt reads now for the first second, then seconds (14s), minutes (3min), hours (2h), days (5d), months (2m), and years (1y).\n\nThe badge resets with every new picture. On Regularly it stays around the interval, and a badge climbing well past it means the camera is not delivering, usually because it is unreachable. On When viewed and Only on request a high value is normal until the next picture is taken.[^badge]"},{"page":"cameras/home","title":"Home","heading":"Selection mode","anchor":"selection-mode","text":"Admins can act on several cameras at once. Select the arrow button in the floating control to enter selection mode.\n\nIn selection mode a card ticks instead of opening the camera, and dragging and the per-card buttons are off.\n\nThe floating control now holds the bulk actions:\n\n- Disable selected cameras / Enable selected cameras. Disabling stops a camera's live stream and its detection, and its card goes dark. The camera stays configured.\n- Snooze detections for selected cameras / Resume detections for selected cameras. Snoozing keeps the camera streaming but pauses its detection.\n- Enable recording for selected cameras / Disable recording for selected cameras. Only appears when at least one camera on the page is set up for recording, and it only acts on those cameras. See Recording.\n- Remove selected cameras. Deletes the cameras.\n\nBulk actions run through the cameras one after another, so with many cameras picked it takes a moment."},{"page":"cameras/home","title":"Home","heading":"Next steps","anchor":"next-steps","text":"- Live view — watch a camera, with the player controls.\n- Camview — several cameras at once in a grid.\n- Camera settings — sources, room, detection, and everything per camera.\n\n[^badge]: The badge is only shown on the cards on Home, not in the player or in Camview."},{"page":"cameras/live-view","title":"Live view","heading":"Live view","anchor":"","text":"Open any camera to watch it live."},{"page":"cameras/live-view","title":"Live view","heading":"Player controls","anchor":"player-controls","text":"- Play / pause the live stream.\n- Mute / unmute for cameras with audio.\n- Two-way talk to speak through the camera (see below).\n- Fullscreen and Picture-in-Picture to pop the video into a floating window. In fullscreen the dots in the control bar switch Shortcuts, the Timeline over the video and the PiP Source overlay. On a phone held upright, the timeline sits at the bottom of the screen, below the picture.\n- Digital zoom by scrolling or pinching, up to 5×, with a minimap. Drag to pan when zoomed in. The crop holds while you scrub the timeline, until you double-click the picture or zoom back out.\n- Quality switches the source resolution (High, Mid, or Low Resolution).\n- Open camera opens the full camera page with recordings and the timeline.\n\nWhen you review recordings, the player also offers rewind, fast-forward, and playback speed. See Timeline & playback."},{"page":"cameras/live-view","title":"Live view","heading":"Detection boxes","anchor":"detection-boxes","text":"With Detections on, the player draws a box around everything the camera currently sees, with the type, the confidence and the track number. A camera that reports a detection without a position gets no box.\n\nAn object that has settled, a car parked in the driveway or a bin left at the kerb, keeps its box but gets a faded label and a timer counting how long it has been standing there. A settled object triggers no new events and no notifications, see stationary objects. It goes back to a normal box as soon as it moves again. On a small card the labels are left out and only the box remains."},{"page":"cameras/live-view","title":"Live view","heading":"Two-way talk","anchor":"two-way-talk","text":"The Intercom button sends your microphone to the camera's speaker, on cameras that support two-way audio."},{"page":"cameras/live-view","title":"Live view","heading":"Second stream overlay","anchor":"second-stream-overlay","text":"Give a source a PiP Source in the camera settings (Sources tab) and the player gets a Toggle PiP button. It shows that second stream as a small overlay on the video. Click the overlay to swap the two streams.\n\nThe PiP Source can also be a stream of another camera, the list groups them by camera. That fits a second lens of the same device, such as the tele lens of a dual-lens camera added as a camera of its own. Clicking that overlay opens the other camera with its timeline and recordings, at the same moment while you review footage, and the camera you came from becomes its overlay."},{"page":"cameras/live-view","title":"Live view","heading":"Streaming modes","anchor":"streaming-modes","text":"You set the streaming mode per camera in its settings, where a new camera starts on Auto. The player also has a button to switch between WebRTC and MSE while you watch.\n\n- Auto starts WebRTC and MSE together and keeps whichever connects first, so a network that blocks WebRTC still gets a picture. When the camera's codec cannot travel over WebRTC it goes straight to MSE.\n- WebRTC gives the lowest latency.\n- MSE is a broadly compatible fallback.\n- webrtc/tcp sends WebRTC over TCP only. Pick it in the camera settings if some networks block the ports WebRTC normally uses. It is not available from the player button.\n\nThe setting applies to the camera view. The small players elsewhere, the zone editor, shortcut previews, the floor plan hover and the event dialog, always run on Auto.\n\nAn MJPEG camera plays as MJPEG in every mode, in the browser, the mobile apps and the Apple TV app. The picture arrives as single JPEG frames, the sound, if the camera has any, over MSE. Recording takes MJPEG as it is."},{"page":"cameras/live-view","title":"Live view","heading":"Activity mode: Always On and Standby","anchor":"activity-mode-always-on-and-standby","text":"Each camera has an Activity mode that controls when its stream runs:\n\n- Always On keeps the stream live while the player is in view.\n- Standby keeps the stream off until you click the video. It stops again a few seconds after your last click or after the picture appears, whichever is later. It stays on while you use the intercom, the PTZ controls, fullscreen, Picture-in-Picture or a menu of the player.\n- Activity keeps the stream running while there is activity and lets it rest otherwise.\n\nThe setting applies to the camera view and to the Home Assistant camera card in live mode. Camview and the Home Assistant view card follow it too, unless they are set to one mode for the whole view. The small players elsewhere always stay on: the zone editor, shortcut previews, the floor plan hover and the event dialog.\n\nWhatever the mode, a player scrolled out of view stops after about 10 seconds and shows its last picture until you scroll back, then starts again. The intercom, the PTZ controls, fullscreen, Picture-in-Picture and an open player menu keep it running, as in Standby."},{"page":"cameras/live-view","title":"Live view","heading":"Shortcuts","anchor":"shortcuts","text":"Shortcuts are controls you place on the video to jump to another camera or operate a sensor. Turn on Edit Shortcuts on the player to add them. See Shortcuts."},{"page":"cameras/live-view","title":"Live view","heading":"More options","anchor":"more-options","text":"The options menu (the dots) toggles Zones, Heatmap and Edit Shortcuts, plus Detections on cameras that run motion or object detection. AI descriptions appears when the NVR plugin is installed. Export appears once the timeline is open, and saves the range you trim there. See Detection & AI.\n\nCameras with pan, tilt and zoom get their own bar at the top right of the player, with the home position, the presets saved on the camera, and the PTZ button for the on-screen controls. See PTZ & autotrack."},{"page":"cameras/live-view","title":"Live view","heading":"The camera page","anchor":"the-camera-page","text":"Open camera (or selecting a camera from the list) opens its full page: the live player, a Recordings tab, a list to switch between Cameras, Shares for admins, and a Timeline panel for jumping through footage. Drag the handle between the player and the tabs to give either more height; the browser keeps that split, a double-click on the handle resets it. The Settings cog (admins) opens the camera settings drawer, where a camera's sources, zones, PTZ, detection, and more live.\n\nTo watch several cameras at once, use Camview."},{"page":"cameras/protocols","title":"Connection protocols","heading":"Connection protocols","anchor":"","text":"When you add a camera manually, each stream URL begins with a protocol. camera.ui reads it from the URL and shows it under the field. The (i) button next to the field opens help for that protocol, with URL formats and examples.\n\nA URL with an unsupported protocol won't save. The (i) help only appears once camera.ui recognizes the protocol, which makes it a quick check for a URL."},{"page":"cameras/protocols","title":"Connection protocols","heading":"RTSP (recommended)","anchor":"rtsp-recommended","text":"RTSP is the universal choice and what most cameras provide. A URL looks like this:\n\nrtsp://username:password@192.168.1.123:554/path\n\nThe path after the address is camera-specific; the camera's manual or app has it. Many cameras offer a main (high-resolution) and a sub (low-resolution) stream; you can add each as a separate source."},{"page":"cameras/protocols","title":"Connection protocols","heading":"Other common protocols","anchor":"other-common-protocols","text":"| Protocol | Use it for |\n|---|---|\n| ONVIF | A standard most cameras support. Connects without knowing the RTSP path, and powers discovery. |\n| HTTP / HTTPS | Snapshot (JPEG), MJPEG, HTTP-FLV, or MPEG-TS streams. |\n| RTMP | RTMP streams. |\n| FFmpeg | Wrap any source FFmpeg can read. |"},{"page":"cameras/protocols","title":"Connection protocols","heading":"NVR and DVR systems","anchor":"nvr-and-dvr-systems","text":"| Protocol | Use it for |\n|---|---|\n| ISAPI | Hikvision two-way audio (add it alongside an RTSP source). |\n| DVRIP | Dahua and compatible recorders (also known as NetSurveillance / Sofia / XMeye). |"},{"page":"cameras/protocols","title":"Connection protocols","heading":"Brand and smart-home integrations","anchor":"brand-and-smart-home-integrations","text":"camera.ui also recognizes brand- and app-specific URLs, such as Tapo, Kasa, Doorbird, GoPro, Home Assistant, HomeKit, Ring, Nest, Wyze, and Tuya. Many of these depend on a vendor cloud.[^cloud] Paste the URL and use the (i) help for the exact setup of each."},{"page":"cameras/protocols","title":"Connection protocols","heading":"Tips","anchor":"tips","text":"- You can give one source multiple URLs. camera.ui combines them, which helps when streams carry different codecs (for example video on one and backchannel audio on another).\n- If you don't know your camera's URLs, try ONVIF or auto-discovery first.\n\n[^cloud]: Cameras that depend on a vendor cloud (such as Ring or Nest) can be connected, either directly or through a plugin, but they are not recommended and not covered by support. Use local RTSP / ONVIF cameras for a reliable experience."},{"page":"cameras/settings","title":"Camera settings","heading":"Camera settings","anchor":"","text":"Admins open a camera's settings drawer with the Settings (cog) button on the camera."},{"page":"cameras/settings","title":"Camera settings","heading":"Overview","anchor":"overview","text":"The camera's status and device info, plus a live view of its sensors and accessories. Read-only sensors (contact, temperature, motion, and so on) show their current state, and you can operate the controllable ones right here, such as turning a light on, locking a door, or arming a security system."},{"page":"cameras/settings","title":"Camera settings","heading":"Sources","anchor":"sources","text":"Manage the camera's stream sources as in adding a camera: each source's name, role (high, mid, low resolution, or snapshot), stream URLs, and options such as hot mode, preload, and mute audio (drops the audio track from that source). Each source also shows its live status, its stream URL and the codecs the camera itself sends, with profile, level and sample rate where the camera reports them, for example \"H.264 · High · 5.1\" or \"AAC · 16 kHz\"."},{"page":"cameras/settings","title":"Camera settings","heading":"Settings","anchor":"settings","text":"The main configuration form, grouped into sections:\n\n- General. Name, room, camera type, and whether the camera is disabled (also where you remove a camera). The room groups the home view and is the room the camera sits in on the floor plan. Pick one of your rooms or create one here. The delete button next to it removes the picked room: its cameras move to Default and the room leaves the floor plan. Moving a camera to another room takes it off the floor plan until you drag it into the new room there.\n- Branding. Manufacturer, model, and other device details.\n- Interface. Everything about how this camera is shown and played: the streaming mode and source, the playback source for recordings, the activity mode, and the display aspect ratio. For the ratio, pick a preset (16:9, 4:3, 1:1, and more) or type a custom width:height like 21:9.\n- Detection. Per-camera tuning for motion, object, audio, face, license plate and sensor detection: sensitivity, confidence, timeouts, triggers and stationary-object handling, plus a snooze switch. Every section has a reset button next to its heading that puts that section back to its defaults. The confidence values the AI backends use come from here, so a change applies right away. Which object types the camera reports is set in Zones. See Detection & AI.\n- Recording. Whether and how this camera is recorded: the mode, the pre-buffer, and which streams go to disk. An installed NVR plugin does the recording. See Recording.\n- Notifications. What this camera pushes to your phone: the camera's own on/off switch, video in push, which sounds and sensors notify, the cooldown and the notification speed. Which detections notify you is set in Zones, on the alert zones. See Notifications.\n- Autotrack. Let a PTZ camera follow detected objects automatically. See PTZ & autotrack.\n- Snapshot. When the camera takes a new still image: regularly, when viewed, or only on request. See Snapshots and how they refresh.\n- Zones. Draw motion, object, alert and privacy zones, and crossing lines. See Zones & privacy.\n- Frame Worker. Picks the decoder hardware (CUDA, VAAPI, Quick Sync, and more) and device for this camera. Auto chooses the best available. A second decoder selection applies while the camera decodes on an assigned worker, so host and worker can use different GPUs; on Same as server the worker uses the first selection. With a worker assigned, the Decoder line shows where the camera decodes right now. Main stream without GPU forces detection "},{"page":"cameras/settings","title":"Camera settings","heading":"Settings","anchor":"settings","text":"onto the high-resolution stream on CPU-only systems, where it costs real processing power; with a hardware decoder the high-resolution stream is already analysed when something is detected, so leave it off."},{"page":"cameras/settings","title":"Camera settings","heading":"Plugins","anchor":"plugins","text":"Enable plugins for this camera and choose which provides each of its sensors and accessories. This is also where you turn individual sensors on or off and adjust the settings a plugin offers per camera. See Set up sensors for the full walkthrough."},{"page":"cameras/shares","title":"Shares","heading":"Shares","anchor":"","text":"A share lets someone watch a single camera's live stream without a camera.ui account, using a link and an access code. Creating and managing shares is for admins."},{"page":"cameras/shares","title":"Shares","heading":"Creating a share","anchor":"creating-a-share","text":"From a camera, select Share Camera in the player toolbar, then set:\n\n- Source. Which stream quality to share (high, mid, or low resolution).\n- Expires after. How long the link stays valid.\n- Max Viewers. How many people can watch at the same time. Unlimited stops at 50.\n- Label. An optional name to recognize the share later.\n\nSelect Create. camera.ui gives you a Share Link and an Access Code. The access code is shown only once, so copy it before closing.\n\nThe viewer page starts with the sound off. The recipient can turn it on, and on cameras with two-way audio, use the microphone button to talk back.\n\nIn the background the viewer page stops the stream and starts it again on return, unless it plays in picture-in-picture. After a dropped connection it reconnects by itself. Someone over the Max Viewers limit sees Maximum number of viewers reached, and the page keeps trying until a seat is free.\n\nRequires camera.ui Cloud\nSharing needs your server connected to camera.ui Cloud (Settings → Remote) and online. The cloud hosts the link and helps the viewer find your server, but the video travels over a direct, end-to-end-encrypted connection between the viewer and your server. It never passes through camera.ui's servers, and the access code is checked only on your own server."},{"page":"cameras/shares","title":"Shares","heading":"Managing shares","anchor":"managing-shares","text":"A camera's Shares tab lists its active share links: the label, the shared source, when each was created, when it expires, and how many people are watching against the viewer limit. The link can be copied again, the access code can't be retrieved. Select Revoke to end a share immediately: everyone watching is cut off and sees This share link has expired. A share that runs out ends the same way."},{"page":"cameras/shortcuts","title":"Shortcuts","heading":"Shortcuts","anchor":"","text":"Shortcuts are controls you place directly on a camera's video. A shortcut points at another camera or a sensor, and what it does depends on whether you hover or click. Shortcuts are saved per camera and per user."},{"page":"cameras/shortcuts","title":"Shortcuts","heading":"Camera shortcuts","anchor":"camera-shortcuts","text":"A camera shortcut puts a neighbouring camera one move away:\n\n- Hover to preview that camera inline, without leaving the one you're watching. While reviewing a recording, the preview shows that camera at the same moment and follows along as you scrub.\n- Click to switch to that camera. When you're reviewing the past, it opens at the same timestamp."},{"page":"cameras/shortcuts","title":"Shortcuts","heading":"Sensor shortcuts","anchor":"sensor-shortcuts","text":"A sensor shortcut puts an accessory on the video:\n\n- Hover to read its current state, such as a contact sensor's open or closed, a temperature, or a lock's status.\n- Click to control it, for accessories that can be controlled: toggle a light or switch, lock or unlock, open a garage, sound a siren, ring a doorbell, or pick an arm state for a security system. Read-only sensors only show their state.\n\nControlling accessories requires admin. See Sensors & accessories for what each type does."},{"page":"cameras/shortcuts","title":"Shortcuts","heading":"Adding and editing shortcuts","anchor":"adding-and-editing-shortcuts","text":"1. On the camera, click the Shortcuts button in the toolbar (in fullscreen, in the menu of the control bar) to show your shortcuts, then click the pencil (Edit Shortcuts) that appears on the video. The toolbar button on its own only shows or hides the shortcuts you already have.\n2. Click anywhere on the video to drop a shortcut, then choose a Camera or a Sensor as its target.\n3. Drag a shortcut to reposition it.\n4. Long-press a shortcut to remove it."},{"page":"cameras/zones-and-masks","title":"Zones & privacy","heading":"Zones & privacy","anchor":"","text":"Zones mark parts of the camera picture and give that area a job. There are five kinds, each with one job and its own tab in the editor: Motion, Objects, Alerts, Privacy and Lines.\n\nOne rule runs through all of them: a kind only narrows things down once it exists. No motion zone means motion counts everywhere, no object zone means every object type counts everywhere, no alert zone means everything that is detected may alert you.\n\nCameras that detect on their own\nReolink AI, Eufy, ONVIF events and plugins that report detections tell camera.ui what they saw, usually without saying where. Without a box there is nothing to place, so no zone can judge such a report and only the type lists still apply:\n\n- Motion zones don't limit them. Their motion counts everywhere on that camera.\n- Object zones still drop the types you left off, but the area has no effect.\n- Alert zones shrink to a type selection. \"Vehicles in the driveway\" is not possible for such a camera, \"vehicles\" is.\n- Lines never apply, a crossing needs tracking and there is nothing to track.\n\nTwo ways out. Let camera.ui detect for the camera: pick an AI backend as its object sensor instead of the camera's own detection, if your hardware carries the extra stream. Or keep the camera as the sensor and add an object assist plugin, which finds the reported object in the frame. Either way the detection has a box from then on and every zone works as described here. Cameras that do send coordinates are not affected."},{"page":"cameras/zones-and-masks","title":"Zones & privacy","heading":"Opening the editor","anchor":"opening-the-editor","text":"Open a camera's settings and go to the Settings tab. Under Zones you see what the camera already has, each with its color, name and type (Motion zone, Object zone, Alert zone, Privacy zone or Line crossing).\n\nBelow the list, Edit zones opens the editor over the camera image.\n\nMotion, object and alert zones start out covering the whole image, so you drag the corners inward instead of drawing from scratch. A privacy zone is the exception: you click its points yourself."},{"page":"cameras/zones-and-masks","title":"Zones & privacy","heading":"Motion zones","anchor":"motion-zones","text":"Motion zones say where motion counts. Only motion inside a zone counts, in any of them if there are several. Motion is always judged by touch: as soon as movement reaches the zone, it counts."},{"page":"cameras/zones-and-masks","title":"Zones & privacy","heading":"Object zones","anchor":"object-zones","text":"Object zones say which object types count, and where. A zone means: only what is inside here counts, for the types listed on it.\n\nThe Labels list has two groups. Under Base sit the object types: person, vehicle, animal. Under Identification sit Recognize faces and Read plates, which decide whether what counts here also gets a name.\n\nA new object zone starts with nothing selected, which means every type counts in it and faces and plates are recognized as usual."},{"page":"cameras/zones-and-masks","title":"Zones & privacy","heading":"Combining zones","anchor":"combining-zones","text":"A zone only governs the types listed on it. Draw two zones, person on one and vehicle on the other, and a person counts in the first area while a vehicle counts in the second.\n\nGive a type more than one zone and it counts in any of them. A zone with no types selected governs every type, so as soon as one exists, everything has to be inside some zone.\n\nThere is no way to cut a hole out of a zone. To keep an area out of detection, draw the zone around it, or put a privacy zone over it with Detections inside set to Are dropped."},{"page":"cameras/zones-and-masks","title":"Zones & privacy","heading":"Which types survive","anchor":"which-types-survive","text":"Once every object zone carries a type list, those lists together are what the camera detects: a type that appears on none of them is dropped everywhere on that camera.\n\nRecognize faces and Read plates count as types here. If every object zone has a list and none of them carries Recognize faces, the camera stops recognizing faces entirely, not just in one corner."},{"page":"cameras/zones-and-masks","title":"Zones & privacy","heading":"Counting without recognizing","anchor":"counting-without-recognizing","text":"Drop both Recognize faces and Read plates and the zone still detects the person and the car, but no face lands in your face list and no name or plate reaches the event or the push.\n\nA zone that lists person and nothing else no longer recognizes faces. Tick Person and Recognize faces together to get the person counted and named; Recognize faces alone finds nothing.\n\nIt does not lower CPU use, it only holds the result back."},{"page":"cameras/zones-and-masks","title":"Zones & privacy","heading":"Match mode","anchor":"match-mode","text":"Counts as inside when decides what makes a detection count for the zone:\n\n- The object touches the zone. Any overlap is enough. This is the default.\n- The object is fully inside. The whole box has to sit in the zone. A car that just clips the edge no longer triggers it."},{"page":"cameras/zones-and-masks","title":"Zones & privacy","heading":"Alert zones","anchor":"alert-zones","text":"An alert zone decides what gets you notified and changes nothing else: detection, events and recordings are untouched by it.\n\nOnce a camera has an alert zone, the zones decide its alerts:\n\n- A type listed on a zone alerts while it is inside that zone.\n- The same type outside that zone stays quiet.\n- A type that no alert zone lists stays quiet everywhere on the camera.\n\nWith one alert zone over the driveway listing person and vehicle, someone on the road still lands on the timeline and in the recording, but only the driveway alerts. Animal is then on no zone, so animals stop alerting on that camera. Give a type a zone covering the whole image if it should keep alerting everywhere.\n\nAn alert zone with no types selected alerts on every type from inside it.\n\nIf you put a type on an alert zone that your object zones filter out, the editor says so."},{"page":"cameras/zones-and-masks","title":"Zones & privacy","heading":"Who exactly, and which plate","anchor":"who-exactly-and-which-plate","text":"A zone can also name the people it pushes for. The Labels list has a second group, Who or what exactly, with Faces and Plates. Ticking one adds a field below it and pulls the matching type into the zone: Faces brings person along, Plates brings vehicle.\n\nFaces is a list of who may push from inside the zone. It offers Unknown faces for everyone the recognition could not name, Any known face for anyone you have enrolled, and then each enrolled person by name. Pick nothing and every face pushes.\n\nPlates works the same way, except you type the plates yourself rather than pick them. Pick nothing and every plate pushes.\n\nBoth lists only ever hold back people and vehicles.\n\nSomeone the camera could not identify counts as an unknown face, whether the recognition failed or no face was ever captured. A person on the ignore list stays silent either way. And if the object zone over that area has Recognize faces switched off, nobody there is ever identified, so everyone counts as unknown."},{"page":"cameras/zones-and-masks","title":"Zones & privacy","heading":"Where it counts as inside","anchor":"where-it-counts-as-inside","text":"Alert zones add a third option, The object stands in the zone: only the bottom center of the box has to be inside. Good for a driveway seen at an angle, where a tall person leans well past the edge. The default here is The object is fully inside.\n\nOnce a type has been inside during a stretch of activity, that whole stretch alerts. Someone who crosses the driveway and then waits on the road still gets you the alert for that visit."},{"page":"cameras/zones-and-masks","title":"Zones & privacy","heading":"What still applies","anchor":"what-still-applies","text":"The master switch, the cooldown and quiet hours are separate from the zones and apply on top.\n\nA doorbell press, a contact sensor, a siren or an audio alert is not an object detection, so an alert zone never holds it back."},{"page":"cameras/zones-and-masks","title":"Zones & privacy","heading":"Privacy zones","anchor":"privacy-zones","text":"A privacy zone covers an area in black, for example a neighbour's window or a public pavement.\n\nDetections inside decides what happens to detections there:\n\n- Are dropped. Detections that sit completely inside the area are thrown away. Something that straddles the edge, like a person half in and half out, still counts. This is the default.\n- Still count. The area is hidden but still watched, so a person walking behind it is detected without being shown."},{"page":"cameras/zones-and-masks","title":"Zones & privacy","heading":"What is hidden, and what is not","anchor":"what-is-hidden-and-what-is-not","text":"The black area is not burned into the video.\n\nHidden:\n\n- Event and recording thumbnails, moment cards, face and plate crops, and episode mosaics.\n- Pictures attached to push notifications.\n- Snapshots: camera tiles, the camera list, the overview, the snapshot API, MQTT, HomeKit stills and the Home Assistant camera image.\n- Live view and playback in the camera.ui web interface, and in the mobile apps, which run that same interface. The area is drawn black on top of the video.\n- Live view, playback and the episode player in the Apple TV app, and the clip that loops on its Home screen, where the app draws the area black on top of the video.\n- The preview that plays while you hover an event or a recording, and a picture you take with the camera button in the player.\n\nNot hidden:\n\n- The recording on disk, and anything you export or download from it.\n- Video clips attached to push notifications.\n- RTSP, and everything that reads it: HomeKit live and HomeKit recordings, the Home Assistant stream, and other apps.\n- Picture-in-picture, which pops the bare video out of the page and leaves the black area behind.\n\nIf an area must never be filmed at all, point the camera elsewhere, or use the camera's own privacy mask if it has one, which blanks the area before the picture reaches camera.ui."},{"page":"cameras/zones-and-masks","title":"Zones & privacy","heading":"When a picture cannot be covered","anchor":"when-a-picture-cannot-be-covered","text":"Sometimes camera.ui cannot paint the black area, for example on a hardware frame it could not read back, or a pixel format it does not know. When a picture cannot be covered, at the bottom of the Privacy tab and valid for the whole camera, decides what happens then:\n\n- Send it anyway. The picture goes out uncovered. This is the default.\n- Send no picture. No picture is produced at all."},{"page":"cameras/zones-and-masks","title":"Zones & privacy","heading":"Lines (line-crossing)","anchor":"lines-line-crossing","text":"A line detects when something crosses it. Crossing direction picks when it fires:\n\n- A ↔ B both ways triggers in either direction.\n- A → B or B → A triggers on a crossing in one direction.\n\nA line can be limited to person, vehicle and animal; a new line starts with all three selected. A line never filters detection."},{"page":"detection/ai-backends","title":"Object detection & AI backends","heading":"Object detection & AI backends","anchor":"","text":"Object detection finds people, vehicles, and animals in the frame, and is the basis for faces, license plates, and semantic search. It runs on an AI backend you choose to match your hardware."},{"page":"detection/ai-backends","title":"Object detection & AI backends","heading":"Choosing a backend","anchor":"choosing-a-backend","text":"camera.ui ships six AI backends as plugins. Pick the one for your hardware and enable it per camera in Settings → Plugins (see Set up sensors):\n\n- CoreML. For Apple Silicon Macs; uses the GPU and Neural Engine.\n- OpenVINO. For Intel CPUs and GPUs.\n- ONNX. Cross-platform, on CPU or a supported GPU.\n- NCNN. A lightweight backend for supported hardware. (No semantic search.)\n- Coral. For a Coral Edge TPU, with a CPU fallback. Linux only.\n- Hailo. For a Hailo-8 / Hailo-8L accelerator. Linux only.\n\nCoreML, OpenVINO, ONNX, and NCNN provide object, face, and license-plate detection, and person re-identification and segmentation for finding an object. CoreML, OpenVINO, and ONNX also provide CLIP for semantic search. Coral and Hailo are edge-AI accelerator backends that provide object detection only (no faces, license plates, or CLIP)."},{"page":"detection/ai-backends","title":"Object detection & AI backends","heading":"Models and confidence","anchor":"models-and-confidence","text":"In a backend's plugin settings you choose a model for each task (object, face, license plate, segmentation, CLIP). Larger models are more accurate but heavier. camera.ui downloads the models it needs automatically.\n\nConfidence is not set here but per camera in Settings → Detection, for object, face and plate detection alike, and a change takes effect right away. Objects have one value per type, each between 0.3 and 1: Person Confidence, Vehicle Confidence and Animal Confidence.\n\nThe backend reports everything from 0.25 up. Below your value a detection never starts an event, a notification or a sensor state, it only helps camera.ui keep following an object it already tracks.\n\nWhich object types a camera reports is set in its object zones. Motion and audio are not affected.\n\nNext to the confidence values is Object timeout, in seconds (minimum 10, default 15). It only matters for cameras that report objects on their own, like many Reolink models: it caps how long such a detection stays active when the camera never sends an end signal. Frame-based backends ignore it.\n\nBelow it is Ignore stationary objects, on by default, see Stationary objects."},{"page":"detection/ai-backends","title":"Object detection & AI backends","heading":"Where detection looks","anchor":"where-detection-looks","text":"A detection model works on a small square, often 640 pixels. Squeezed into it, a 4K frame leaves a person at the end of the driveway a few pixels tall. camera.ui instead cuts the moving areas out of the full-resolution frame, several per frame if the movement is spread out, runs the model on those and maps the boxes back onto the picture, so small and distant objects get found. Nothing to set up.\n\nWhen nothing is moving, the whole frame goes through. The Zoom column in the metrics below shows how often a camera worked this way."},{"page":"detection/ai-backends","title":"Object detection & AI backends","heading":"What detection costs","anchor":"what-detection-costs","text":"Metrics → Cameras shows two tables. The first is the process table, one row per camera, with CPU and memory. The second, Detection, shows where the time goes:\n\n- Decode, the time for one frame: analysis stream first, then the high-resolution stream.\n- Processing, scaling to the model input and turning the results back into boxes. On a camera without an object detector it shows the scaling for motion.\n- Transport, the time the frame and the result spend on the way to the plugin and back.\n- Network, the bit rate of frames sent over the network to detection plugins on another machine, with those plugins behind it. See Worker assignments.\n- Analysed, frames per second analysed, again analysis stream then high-resolution stream.\n- Active, the share of time the camera analysed at all. A camera with a frame loop sits at 100%, a camera that reports motion itself only counts during events.\n- Detections, objects found per analysed frame.\n- Zoom, the share of object detections that looked at zoomed motion windows instead of the whole frame, with the number of windows per zoomed frame behind it.\n\nShow inference switches the same table to the time inside each detector: Motion, Object, Face, Recognition, Re-ID, Plate, Classifier and CLIP. Point at a cell to see the plugin, the model and the device it runs on, and a camera running on a worker names that worker. Values are averages since the camera started; use Reset after changing a model. Copy puts both views for every camera on the clipboard as text."},{"page":"detection/ai-backends","title":"Object detection & AI backends","heading":"Benchmark","anchor":"benchmark","text":"Benchmark loads the object detector of the cameras you pick with test frames and reports how many detections per second the machine manages, in total and per camera, with each camera's plugin, model and device. In the plugin at once is how many of the calls In flight the plugin held at the same time on average: at 1 it takes one detection after the other. Copy puts the result on the clipboard.\n\nDetection pauses on every camera while the run is going, including the ones you did not pick, and continues when it is done. Only admins can run it."},{"page":"detection/ai-backends","title":"Object detection & AI backends","heading":"Object assist","anchor":"object-assist","text":"Some cameras detect objects on their own but report only that they saw something, not where in the frame it is. Many Reolink models work this way. That leaves face and license-plate recognition nothing to zoom in on, so they rarely find anything.\n\nUnder the camera's Plugins tab, pick an Object Assist plugin below Object. It is a second detector that locates the person or vehicle in the frame, so face and plate recognition get a proper crop to work on. See Set up sensors."},{"page":"detection/ai-backends","title":"Object detection & AI backends","heading":"Camera detection as a second opinion","anchor":"camera-detection-as-a-second-opinion","text":"Detection needs more than one sighting, so a person seen only for a moment (behind a fence or a clothesline) is dropped as a flicker. If the camera also detects objects on its own (a camera plugin that reports people or vehicles, like the Reolink plugin or ONVIF analytics) while a backend here does the detection, a camera report of the same kind of object at the same moment confirms that glimpse, and the card shows its picture. The camera's report never creates a detection by itself. Nothing to set up."},{"page":"detection/ai-backends","title":"Object detection & AI backends","heading":"What you get","anchor":"what-you-get","text":"Detected objects appear on the live view as boxes, drive notifications, and become events you can browse and filter by type. They also feed face recognition, license plates, and semantic search. A heatmap overlay on the player shows where objects were detected most often over a time window."},{"page":"detection/audio","title":"Audio detection","heading":"Audio detection","anchor":"","text":"Audio detection listens to a camera's audio for specific sounds, such as glass breaking, an alarm, or a dog barking. It runs alongside video detection and can trigger events and notifications on its own. The camera needs to provide an audio stream."},{"page":"detection/audio","title":"Audio detection","heading":"Setup","anchor":"setup","text":"Enable the audio plugin for a camera in Settings → Plugins (see Set up sensors). The plugin has no settings of its own: it listens for the standard sound list and takes its confidence from the camera.\n\nThe sounds it recognizes are glass breaking, screaming, gunshots, alarms (smoke, car, siren), a doorbell, knocking, a dog bark, a cat, a baby crying, and speech."},{"page":"detection/audio","title":"Audio detection","heading":"Settings on the camera","anchor":"settings-on-the-camera","text":"Audio detection is tuned in Settings → Detection, per camera:\n\n- Min. volume (dBFS). Audio frames below this level are skipped. Ranges from -100 (silence) to 0 (maximum), default -40.\n- Audio confidence. Sounds the detector is less sure about than this are dropped (0 to 1, default 0.7).\n- Audio timeout. How long an audio event stays active after the last trigger (minimum 10 seconds)."},{"page":"detection/episodes","title":"Episodes","heading":"Episodes","anchor":"","text":"When several cameras see the same activity around the same time, camera.ui can bundle those sightings into one episode: a single title and a short story that follows the subject across cameras, in order. A person walking from the driveway to the front door becomes one episode instead of three separate events.\n\nThe story comes from AI descriptions. Without them an episode gets a title with the places and the recognized names instead, and forms only when something ties the sightings together without pictures: a face or plate recognized on both cameras, or a walk on the floor plan or one seen before between the cameras that fits the time in between with at most a short stop."},{"page":"detection/episodes","title":"Episodes","heading":"Turning it on","anchor":"turning-it-on","text":"In Settings → Recordings, open the Episodes tab and turn on Enabled. Every camera takes part unless its own Episodes toggle in the camera's settings is off; an episode forms once at least two different cameras saw related activity. With a drawn floor plan, camera.ui also checks whether the sightings fit together in space: unrelated things happening at the same time in different corners of the property become separate episodes instead of one mixed story, and the title names the places involved. The walk and the time in between then decide which sightings belong together, and the AI only leaves out a sighting that clearly shows someone else. Without a floor plan camera.ui learns the walks between cameras on its own, from faces and plates recognized on both and from the episodes it told, and uses those of the last 30 days the same way.\n\n- Join Window. A new event joins the open episode when it starts within this long after the last activity. Default 90 seconds.\n- Quiet Time. The episode closes and gets its story once it has been this quiet. Default 60 seconds.\n- Wrap Up After. Once an episode reaches this length it stops taking in new activity and closes at the next natural break: whatever is still happening finishes first, and later activity starts the next episode. Default 10 minutes.\n- Custom Instructions. Free-text guidance added to the built-in prompt for the story, for example what to watch for or how to phrase it.\n- Video in Push. Whether an episode push plays the episode clip, which switches cameras like the episode player. Off never attaches it, Always always does, and Camera setting attaches it when at least one camera in the episode has Video in Push turned on. Default Camera setting."},{"page":"detection/episodes","title":"Episodes","heading":"Where episodes show up","anchor":"where-episodes-show-up","text":"- Home. Episodes appear in the Recent Events row as a mosaic card, showing how many cameras took part.\n- Recordings. The same mosaic card appears in the list. A recording that belongs to an episode shows an Open episode button that jumps to it. Episode cards follow the camera, time range, Only with recording and Favorites only filters; any other filter (labels, triggers, attributes, vehicles, search) hides them.\n- Apple TV. Episodes are available in the Apple TV app too.\n- Push. An episode pushes once its story is ready, also from cameras with Send notifications turned off. With that switch off on every camera, you get pushes only for activity across several cameras. Alert zones and their face and plate filters still count: an episode whose sightings all lie outside them stays in Recordings without a push."},{"page":"detection/episodes","title":"Episodes","heading":"The episode player","anchor":"the-episode-player","text":"An episode plays as one sequence, switching cameras as the story moves and showing the current camera's name and time. The strip below the video has one block per camera stretch. Zoom and pan work as in the live view.\n\nWhen a second camera covers the same room on the floor plan, the cut plays whichever had the subject closest, and the control bar offers Second angle for the other view of that stretch."},{"page":"detection/episodes","title":"Episodes","heading":"Why an episode turned out this way","anchor":"why-an-episode-turned-out-this-way","text":"Admins open what went into an episode with the trace icon on its card and in the player: every event that was a candidate and whether it stayed or went, and for the ones that went the reason, from \"never moved\" to \"belongs to another episode\". Below that are the connections it drew between the remaining events, both AI passes with the pictures and instructions the model was given and what it answered, and the cut that came out, including a stretch that was dropped because another camera saw it better. Download bundle packs it together with the clip for a bug report."},{"page":"detection/events-and-detections","title":"Events & detections","heading":"Events & detections","anchor":"","text":"When detection fires, camera.ui groups what it finds into an event."},{"page":"detection/events-and-detections","title":"Events & detections","heading":"The pieces","anchor":"the-pieces","text":"- Event. Everything that happens during one continuous burst of activity at a camera. An event has a start and an end, and can update as it goes.\n- Segment. A stretch of an event where the AI kept seeing objects, with its own thumbnail. Objects leaving and coming back start a new segment, and an event where the AI never sees an object, like a doorbell press with nobody in view, has none.\n- Detection. Something the AI found in a segment, such as a person or a vehicle, with a confidence score.\n- Attribute. Extra detail on a detection, such as a recognized face, a license plate, or a semantic (CLIP) embedding used for search."},{"page":"detection/events-and-detections","title":"Events & detections","heading":"Triggers vs detections","anchor":"triggers-vs-detections","text":"- A trigger is a signal that something happened: motion, a doorbell, a contact sensor, an alarm sound. A trigger can start an event on its own.\n- A detection is what the AI actually recognized in the video, like an object, a face, or a plate.\n\nYou can filter and alert on a doorbell press (a trigger) or only when a person is seen (a detection)."},{"page":"detection/events-and-detections","title":"Events & detections","heading":"The picture an event carries","anchor":"the-picture-an-event-carries","text":"Every card, push and AI description shows one picture for the moment, and camera.ui picks the frame and the crop for it. It frames the subject, so a person arrives as a whole person, not as a close-up of a shoulder. Whatever moves in the picture at the same time is framed with it, whatever stands still is not. When a face is recognized later in the same event, the \"Unknown\" crop is dropped rather than kept alongside the name.\n\nA camera that reports detections without saying where in the frame they were, many Reolink and ONVIF cameras, gives nothing to frame. When camera.ui runs motion detection on the camera's picture itself, the moving part is framed instead, which is less precise. Otherwise the picture shows the whole scene. An object assist plugin gives those cameras real boxes."},{"page":"detection/events-and-detections","title":"Events & detections","heading":"Stationary objects","anchor":"stationary-objects","text":"Ignore stationary objects is on by default in a camera's Settings → Detection: once an object (a parked car, a bin) has held still for a while, camera.ui stops counting it as a detection, across later events too. Its box still shows in the live view. It counts again once it moves off the spot, or if the camera pans or tilts. A person who keeps leaving their spot and coming back keeps counting. Small movements while sitting or standing are not enough for that."},{"page":"detection/events-and-detections","title":"Events & detections","heading":"The detection trace","anchor":"the-detection-trace","text":"Every event keeps a record of what the detector actually saw. Admins open it from the dots menu on a card in Recordings or the trace icon in the event dialog. Opened from a card it starts at the moment the card shows, from the camera view at the moment on screen. For an event that ran for hours, type a clock time above the frame strip to jump there; Earlier frames loads what came before.\n\nIt steps through the event frame by frame with pictures from the recording, keeping only a few frames around each activity, and shows per frame the objects and the tracks they belong to, the motion boxes, the raw detections from 25 % confidence up, the camera's own detection reports, and the zones as they stood at the time. Where something was seen but did not become an event, the trace names the reason: under the confidence threshold, inside a privacy zone, outside the object zones, a label the zone does not accept, or a track too young to be confirmed. Faces and plates dropped by the camera's settings show dashed, with the setting they missed. When a less certain sighting kept a track going, the track shows that frame's confidence next to the last certain one. Each face read shows whom it matched, or who came closest when nobody did (both people when two came about equally close), and how many reads agree on the name. Every track carries its state, from tentative through active, stationary and lost to departed.\n\nFrames zoom like the live view (wheel, pinch, double tap). An event without a recording shows its detection data without frames. If the browser cannot decode the main stream, the pictures come from the smaller one and are matched by time instead of exactly.\n\nDownload bundle packs the whole trace into one file for a bug report. The video in it covers the whole event, or the 10 minutes around the selected frame when the event ran longer."},{"page":"detection/events-and-detections","title":"Events & detections","heading":"Where events show up","anchor":"where-events-show-up","text":"Events drive the rest of camera.ui:\n\n- Recordings — browse and filter past events.\n- Notifications — get alerted about the ones that matter.\n- Semantic search — find events by describing them."},{"page":"detection/faces","title":"Face recognition","heading":"Face recognition","anchor":"","text":"Face recognition spots faces on your cameras, recognizes people you have enrolled, and groups the ones it doesn't know yet.[^license] It builds on object detection, and the camera needs an AI backend in two places under its Plugins tab: Face finds the faces, Face Recognition turns each one into something camera.ui can compare. Without the second one faces are found but never named. If the camera detects objects itself without reporting where they are (many Reolink models), enable Object Assist so faces get a proper crop."},{"page":"detection/faces","title":"Face recognition","heading":"Enrolling a known face","anchor":"enrolling-a-known-face","text":"In the Faces view (sections Known Faces and Unknown Faces), Add Face takes a name and a clear photo of the person; Enroll rejects a photo without a face. From then on the person is recognized in new events, with their name as an attribute on detections."},{"page":"detection/faces","title":"Face recognition","heading":"Getting good results","anchor":"getting-good-results","text":"- Camera position. Mount a camera meant for faces at about head height, or with a shallow angle. Looking down steeply it sees forehead and hair, different people start to look alike, and recognition mixes them up.\n- Pictures per person. At least 10 pictures are recommended, from different situations (daylight, night, with a cap). Every further look up to the maximum helps. With fewer the person is often missed, their card and their dialog point it out.\n- Unclear faces get no name. A face seen from the side, from above, or blurred is neither named nor collected under Unknown Faces. The person is recognized in the next picture that shows them clearly.\n- Small faces. A face smaller than about 40 pixels in the camera picture is too small to recognize.\n- One picture is not enough. A person the camera follows gets a name once three pictures of the face agree, and camera.ui looks again whenever the face shows bigger or clearer, so a good view late in a long visit still counts. When the pictures disagree, the person stays unnamed rather than getting someone else's name. A picture about as close to two enrolled people counts for neither. On a camera whose own AI reports people, faces are only read on its reports, and one picture names them.\n- A name in doubt waits for a new picture. After two people cross, when someone starts moving again or comes back after the camera lost sight of them, and when a picture matches someone else, the name stays hidden until a new picture of the face confirms it. If that picture matches someone else too, the event gets a second face for that person, and the first keeps its name.\n- Two faces, one person. When two people are so close that the camera sees them as one person, each face gets only the name its own picture matches. The same goes for a face outside every detected person."},{"page":"detection/faces","title":"Face recognition","heading":"A person's pictures","anchor":"a-persons-pictures","text":"A person under Known Faces opens with their training pictures. A mark on a picture says what it does to recognition, tap it for the reason:\n\n- No clear face. Turned away, from above or blurred. Such a picture makes recognition worse, remove it.\n- Recognition can't link it. To recognition the picture looks no more like the person's other pictures than a stranger's, usually a strong profile or a view from above. It stays with the person but isn't used to recognize anyone. If it shows someone else, remove it.\n- Face partly visible. Keep it only while better pictures of that view are missing.\n- Also filed under … The same picture is enrolled for someone else as well. Keep it for the right person only.\n- Same as another picture. One of the two is enough.\n\nThe person's card already shows how many pictures carry a mark, in red when one of them is unclear or also filed under someone else.\n\nAssigning an unknown face warns when it is already filed under someone or only partly visible. Unclear faces never reach Unknown Faces in the first place."},{"page":"detection/faces","title":"Face recognition","heading":"Unknown faces","anchor":"unknown-faces","text":"Faces that don't match anyone enrolled collect under Unknown Faces, grouped automatically so the same person's shots cluster together.\n\n- Assign a group to a person to enroll everyone in it at once, or assign a single face.\n- Discard groups or faces you don't care about, or clear all ungrouped faces."},{"page":"detection/faces","title":"Face recognition","heading":"Correcting a recognition","anchor":"correcting-a-recognition","text":"When an event names the wrong person, open that face picture on its recording card, pick Correct person in the dots menu and assign the right name, or Mark as unknown. Only this face changes: other faces in the recording keep their names, even when they carry the same one. The crop is added to that person so recognition gets it right next time, unless it shows no clear face: then only the name changes. The dialog says so beforehand, and warns when the crop is only partly visible or already filed under someone else. Admins only."},{"page":"detection/faces","title":"Face recognition","heading":"Ignoring a face","anchor":"ignoring-a-face","text":"Ignore a face you never want to see again (a passing stranger, the mail carrier) and camera.ui stops collecting it.\n\n- Ignore a face or a whole group. Unknown faces and groups each have an ignore button; selection mode ignores several at once.\n- New sightings are dropped. Once someone is ignored, their later shots don't come back as unknown faces.\n- Ignored faces sit in their own list. They collect under Ignored Faces at the bottom of the Unknown Faces section.\n- Restore to undo. Restore puts an ignored face back among the unknown faces. New sightings of that person come back once none of their other pictures is still ignored."},{"page":"detection/faces","title":"Face recognition","heading":"Settings","anchor":"settings","text":"In Settings → Recordings, face recognition has:\n\n- Max Training Images. How many photos to keep per person (5 to 40, default 20). At the limit the picture most like another one of the same person makes way.\n- Max Unknown People. How many unknown groups are kept (10 to 500). Once the limit is hit, the group that was seen longest ago is dropped. Raise it for busy outdoor cameras.\n\nPer camera, Settings → Detection has two face settings:\n\n- Face confidence. Faces the AI is less sure about than this are ignored for events and automations (0 to 1, default 0.5).\n- Recognize faces. How certain the match against your enrolled people has to be before someone is named: Strict, Balanced (default) or Relaxed. Strict gives fewer wrong names, Relaxed recognizes people more often."},{"page":"detection/faces","title":"Face recognition","heading":"Changing the recognition model","anchor":"changing-the-recognition-model","text":"The model is a setting of the AI backend (Face Embedding Model), shared by all its cameras. After a change camera.ui embeds the enrolled pictures again by itself. Re-embed faces, bottom left in the Faces view and admins only, starts it by hand. Recognition is patchy until it finishes. Pictures in which the new model finds no clear face are removed, and the dialog names the people this affected.\n\nTo try a model, open the AI backend's page and its Face Recognition tab: drop a picture and it shows who camera.ui takes it for and how sure it is."},{"page":"detection/faces","title":"Face recognition","heading":"Rescanning","anchor":"rescanning","text":"After enrolling someone new, select Rescan existing events to re-check past recordings and tag them with the newly known face.\n\n[^license]: Face recognition (matching faces against your enrolled people) builds on recording and requires an active camera.ui subscription. Detecting that a face is present is part of the free detection."},{"page":"detection/find-object","title":"Find an object","heading":"Find an object","anchor":"","text":"Open Find in the timeline, tap a person or a vehicle in the picture, and camera.ui lists the moments that show it.[^license]"},{"page":"detection/find-object","title":"Find an object","heading":"Picking an object","anchor":"picking-an-object","text":"While the Find view of the timeline is open, every picture can be picked. A tap or a click freezes the picture at that frame, pauses the player and picks the object at that spot with the detection assigned to the camera. Outlines then mark every person and vehicle in the frozen picture, and each further tap adds a pick.\n\n- Find this object in the player's control bar (in the dots menu on a small player) or F opens and closes the view. In Camview F also opens the grid's timeline.\n- Press and hold a picture, with a finger or the mouse, to freeze it and pick from any view, then Find opens.\n\nA frozen picture plays on with Continue at its top, play, going live, a match you open, Esc or End search. The picks stay unless you end the search. Scrubbing the timeline moves a frozen picture to the new moment, so you can pick a better frame of someone who looked away.\n\nOnly admins can pick, and only with the NVR plugin and object detection assigned to the camera. A camera without object detection has no Find, in Camview its tile says No object detection while Find is open. The picture is read with the models and confidences set for the camera. When the camera's own detection cannot read a picture, the AI backend set as its Object Assist reads it; without one the search says so.\n\nWith an AI backend as Segmentation in the camera's Plugins tab, the object gets its outline instead of a box, and where people or vehicles overlap, a click picks the one under the pointer."},{"page":"detection/find-object","title":"Find an object","heading":"What a search goes by","anchor":"what-a-search-goes-by","text":"A search goes by everything the picture offers at once and lists each moment once.\n\n- A person. By name when the face was recognized, otherwise by the face compared with the faces of other moments. Also by clothing and build, which finds people who look alike: this needs an AI backend as Person Re-ID in the camera's Plugins tab and finds the moments of cameras that have one. Someone in other clothes looks like someone else to it, and moments from before the camera had Person Re-ID have no clothing to compare. Cameras that report their objects themselves (with or without Object Assist) store no clothing either.\n- A vehicle. By its plate when one was read, with plates read one character off, and by its look: vehicles that look alike. The look needs a CLIP backend, see Semantic search.\n\nHow well people are found by name depends on face recognition."},{"page":"detection/find-object","title":"Find an object","heading":"The matches","anchor":"the-matches","text":"The Find view shows each pick with its crop and the matches of all picks in one list, one card per moment, the surest first: the name or the plate, then the face, then the look-alikes by clothing or look, best match first. With several picks each card shows the pick it came from. Where a match is a comparison, a slider from More precise to Broader sets how close it has to be. Removing a pick drops its matches, End search removes all of them.\n\nWhile a search runs, the timeline shows only the matching events. Turning off Matches only in the timeline's filter menu shows all events again.\n\n- A card of a camera on screen plays that moment there.\n- A card of another camera opens that camera at the moment, with its pick in Find.\n- In the episode player Find is a column beside the player (on a phone below it). A match inside the episode plays there, one outside opens as a recording.\n\nShow all in Recordings lists the matches of all picks in Recordings, where the camera, room and time range filters narrow them and End search returns to the normal list."},{"page":"detection/find-object","title":"Find an object","heading":"Search by picture","anchor":"search-by-picture","text":"In Recordings, Search by picture (admins only) takes a picture of a person or a vehicle: drop it on the field or anywhere on the list, or choose one. Click the person or the vehicle in it, and the list shows the matches as above. The picture is read with the plugins and models of the camera that has most of them assigned."},{"page":"detection/find-object","title":"Find an object","heading":"Trying the models","anchor":"trying-the-models","text":"The AI backend's plugin page has a Person Re-ID and a Segmentation tab. Person Re-ID takes a picture of a person and searches the recordings for people who look alike, Segmentation outlines every person, vehicle and animal in a picture."},{"page":"detection/find-object","title":"Find an object","heading":"Vehicle color and type","anchor":"vehicle-color-and-type","text":"Vehicles in the Recordings filters narrows the list to colors and types: car, van, truck, bus, motorcycle, bicycle. camera.ui reads both from the picture of every vehicle it records, older recordings included, and needs CLIP for it. A reading can be wrong, a gray car in the shade may count as black.\n\n[^license]: Finding objects and the vehicle filter build on recording, which requires an active camera.ui subscription."},{"page":"detection/genai-descriptions","title":"AI descriptions","heading":"AI descriptions","anchor":"","text":"A generative-AI model writes a short summary of each moment in an event: a title, what happened, a notification-friendly line, and a threat level. It names people, plates, and detected movement.\n\nNames and plates come from what face recognition and plate reading actually confirmed, not from what the model thinks it can read in the picture."},{"page":"detection/genai-descriptions","title":"AI descriptions","heading":"Choosing a provider","anchor":"choosing-a-provider","text":"The model comes from Settings → Assistant: allow the NVR plugin under Plugin access and pick one of your models. The plugin sends its requests to camera.ui, so the key stays on the server. Any provider the assistant supports works, as long as the model reads pictures; the picture test in the model list tells you.\n\nDescriptions are written in the Answer language of the assistant settings, in English when it follows the interface, and look at up to four pictures per event. Enable them per camera with the camera's AI descriptions toggle."},{"page":"detection/genai-descriptions","title":"AI descriptions","heading":"Moments and Episodes","anchor":"moments-and-episodes","text":"The model feeds two features, each with its own tab in the recording settings:\n\n- Moments. Enabled turns on AI descriptions of events. Custom Instructions adds free-text guidance to the built-in prompt. Model Timeout is how long a description may take before the push goes out without it: 60 seconds by default, raise it for a slow local model. How the descriptions reach your phone is set per camera, see Notifications.\n- Episodes. Bundles related events across cameras into one story, with its own Custom Instructions. See Episodes."},{"page":"detection/genai-descriptions","title":"AI descriptions","heading":"Privacy","anchor":"privacy","text":"With a local model (Ollama, a local OpenAI-compatible server, a plugin model), everything stays on your own hardware. With a cloud provider, event snapshots are sent to that provider to generate the description.[^license]\n\nA drawn floor plan adds text context to the prompts: how the rooms connect, walking times between cameras, and what nearby sensors read at the time.\n\n[^license]: AI descriptions build on recording, which requires an active camera.ui subscription."},{"page":"detection/","title":"Detection & AI","heading":"Detection & AI","anchor":"","text":"Detection runs on your own hardware and picks up movement, people and vehicles, faces, license plates, and sounds. The results become events you can browse, get notified about, and search."},{"page":"detection/","title":"Detection & AI","heading":"How detection works","anchor":"how-detection-works","text":"Detection is layered:\n\n1. Motion runs continuously and cheaply. It just notices that something changed.\n2. When a trigger fires, the heavier AI wakes up. It runs object detection (people, vehicles, animals), then looks closer at what it found: faces on the people it sees, license plates on the vehicles, and a semantic fingerprint for search. Motion is the usual trigger. A detected sound wakes the AI too, and so can another sensor, for example a door contact.\n\nWhile an event is active, the heavier analysis (objects, faces, plates, and search) switches to the camera's high-resolution stream for sharper crops, then drops back to the lighter stream once the scene goes quiet.\n\nflowchart LR\n cam[Camera frame] --> motion{Motion?}\n cam -. parallel .-> audio[Audio]\n sensor[Sensor triggere.g. door contact] --> obj\n motion -- no --> idle[Heavy AI stays idleunless another trigger fires]\n motion -- yes --> obj[Object detectionperson · vehicle · animal]\n audio -- sound --> obj\n obj -- person --> face[Faces]\n obj -- vehicle --> plate[License plates]\n obj --> clip[CLIP]\n obj --> ev[(Event)]\n face --> ev\n plate --> ev\n audio --> ev"},{"page":"detection/","title":"Detection & AI","heading":"What you can detect","anchor":"what-you-can-detect","text":"- Motion — movement in the frame.\n- Objects — people, vehicles, and animals.\n- Faces — recognize known people and group unknown ones.\n- License plates — read plate numbers.\n- Audio — sounds like glass breaking, alarms, or a dog barking.\n- Semantic search — find moments by describing them in words.\n- Find an object — click a person or vehicle and get the moments that show it.\n- AI descriptions — a written summary of what happened.\n- Episodes — related sightings across cameras told as one story."},{"page":"detection/","title":"Detection & AI","heading":"Plugins do the work","anchor":"plugins-do-the-work","text":"Detection is provided by plugins you enable per camera: a motion engine and an AI backend that matches your hardware. You choose and tune them in a camera's settings, see Set up sensors.\n\nEach detection becomes part of an event. See Events & detections for how those are structured, and Recording (NVR) for browsing them.\n\nThe detection models themselves learn from frames the community labels and contributes. See Model training."},{"page":"detection/license-plates","title":"License plates","heading":"License plates","anchor":"","text":"License-plate recognition reads plate numbers from vehicles. It builds on object detection: a vehicle is detected, then its plate is located and read by OCR. Enable an AI backend with license-plate support on the camera. If the camera detects objects itself without reporting where they are (many Reolink models), enable Object Assist so plates get a proper crop."},{"page":"detection/license-plates","title":"License plates","heading":"Setup","anchor":"setup","text":"In the backend's plugin settings, license plates use two models, a detector that finds the plate and an OCR model that reads the characters. camera.ui downloads them automatically. Each has its own confidence on the camera in Settings → Detection, next to a length filter:\n\n- Plate detection. Plates found with less confidence than this are ignored (0 to 1, default 0.3).\n- Plate reading. Readings the OCR is less sure about than this are ignored (0 to 1, default 0.9).\n- Min. plate length. Readings shorter than this are dropped as fragments (default 4 characters)."},{"page":"detection/license-plates","title":"License plates","heading":"Where plates appear","anchor":"where-plates-appear","text":"A read plate is attached to its vehicle as an attribute. You'll find it:\n\n- On the event, alongside the vehicle.\n- In Recordings, where you can filter by the License plate attribute or search for a specific plate.\n- In notifications and AI descriptions, for example \"Plate ABC-123\"."},{"page":"detection/motion","title":"Motion detection","heading":"Motion detection","anchor":"","text":"Motion is the first and cheapest layer of detection. It watches for change in the frame and, when it sees movement, wakes the heavier AI. On its own it can also trigger recordings and notifications."},{"page":"detection/motion","title":"Motion detection","heading":"Choosing a motion engine","anchor":"choosing-a-motion-engine","text":"camera.ui offers several motion plugins. Enable one for a camera in its settings under the Plugins tab (see Set up sensors):\n\n- Rust Motion. Fast and efficient; a good default for most setups.\n- OpenCV. Several algorithms with fine-grained tuning.\n- OpenCL. GPU-accelerated, for very low CPU use.\n- WASM Motion. Cross-platform WebAssembly.\n- Pam Diff. Lightweight pixel-difference.\n- SMTP. For cameras that send motion alerts by email instead of analysing the video."},{"page":"detection/motion","title":"Motion detection","heading":"Tuning sensitivity","anchor":"tuning-sensitivity","text":"Each engine exposes its own settings (such as area, threshold, and blur) in its plugin settings, with a reset to defaults. Two settings live on the camera itself, in Settings → Detection:\n\n- Motion resolution. Low is fastest, medium is balanced, high is most accurate.\n- Motion timeout. How long motion stays \"active\" after movement stops (minimum 10 seconds).\n\nIf a camera is too sensitive (a busy road, a swaying tree), limit where motion counts with zones.\n\nA camera's on-screen timestamp can trigger motion too, mostly at night and at higher motion resolutions. Motion events with nothing in frame are the sign: draw a motion zone that leaves out the timestamp corner."},{"page":"detection/motion","title":"Motion detection","heading":"The cascade","anchor":"the-cascade","text":"When motion fires, it triggers the heavier detectors (object, face, and so on). You can also let other sensors start this cascade, for example a door contact sensor triggering object detection, under Sensor triggers in the camera's detection settings."},{"page":"detection/semantic-search","title":"Semantic search","heading":"Semantic search","anchor":"","text":"Semantic search lets you find moments in your recordings by describing them in plain words, like \"person with a red jacket\" or \"delivery truck\", instead of filtering by type. It is powered by CLIP."},{"page":"detection/semantic-search","title":"Semantic search","heading":"What you need","anchor":"what-you-need","text":"Semantic search needs an AI backend that provides CLIP (CoreML, OpenVINO, or ONNX) enabled on your cameras. As those cameras record, camera.ui makes every person, vehicle and animal it detects searchable. Events without one of those, like plain motion, won't show up in the results.[^license]"},{"page":"detection/semantic-search","title":"Semantic search","heading":"Searching","anchor":"searching","text":"In Recordings, open AI Search and describe what you're looking for. camera.ui ranks events by how well they match, and you can raise Min. Match Score to keep only close matches. The other filters in the sidebar narrow the matches too, and scrolling down loads more of them.\n\nIf the count keeps its + at the end of the list, more pictures fit the description than one search reads, and matches in the chosen cameras or time range can be missing. Raise Min. Match Score until the + goes away.\n\nThe percentage is scaled per CLIP model, so a Min. Match Score means the same after a model change."},{"page":"detection/semantic-search","title":"Semantic search","heading":"Changing the CLIP model","anchor":"changing-the-clip-model","text":"A backend's plugin settings offer more than one CLIP model. Embeddings are only comparable within the model that produced them, so a search only looks at events embedded with a model your enabled CLIP plugins still serve. Older events stay on disk but drop out of the results until they are re-embedded.\n\nReindex search, bottom left in Recordings and admins only, does that. It picks the events whose embeddings came from a model none of your plugins serves any more and works through them in the background. Search keeps working while it runs, and you can leave the page. Stop ends it after the current event, and starting it again continues where it stopped. Events whose pictures are gone cannot be re-embedded and are counted as failed.[^reindex]\n\n[^license]: Semantic search builds on recording, which requires an active camera.ui subscription.\n\n[^reindex]: Without a CLIP plugin the button does not appear."},{"page":"detection/training","title":"Model training","heading":"Model training","anchor":"","text":"camera.ui's detection models are trained on real footage from real installations, contributed by the community. The Training view collects sample frames from your cameras, you label and verify them, and you decide which ones go to the shared training pool. Nothing is uploaded on its own."},{"page":"detection/training","title":"Model training","heading":"How frames are collected","anchor":"how-frames-are-collected","text":"During detection events each camera saves a frame when something new shows up: a new object, a changed position, a fresh face or license plate. An unchanged scene doesn't repeat, however long the event runs. Of the few seconds around such a moment it keeps one frame and prefers objects the detector was unsure about, small ones and animals over large, obvious ones; about one time in ten, picked at random, it keeps the first frame instead, so ordinary scenes stay in the mix. camera.ui keeps at most 200 frames per camera: once full, the oldest unreviewed frames make room, and unreviewed frames are deleted after 14 days either way. Verified frames stay until you submit or delete them.\n\nA privacy zone is blacked out of the frame before it is saved, so nothing behind one is ever collected or submitted.\n\nCollection is on by default and stays on your disk. To stop it, open the gear menu in the Training view and turn off Collect candidates. The view is only visible to admins."},{"page":"detection/training","title":"Model training","heading":"Labeling frames","anchor":"labeling-frames","text":"Opening a frame starts the editor. Drag to draw a box, tap a box or its label to change it, double tap to zoom. On a keyboard, the question mark button in the editor lists the shortcuts.\n\nShow boxes of this camera's previous image (V) outlines the boxes you last had on a frame of the same camera, the plus on one takes it over, Shift + V takes all. Hide boxes (H) hides every box and outline so the picture underneath shows.\n\nDetections below the camera's detection threshold show as dotted outlines with their score. The plus on one labels it; leave the ones that show nothing.\n\nWhat makes a frame useful:\n\n- Label everything you see. Every person, vehicle, animal and package in the frame, not just what triggered the event, parked cars and vehicles cut off at the edge of the picture included. An unlabeled object teaches the model that it isn't one. The boxes camera.ui drew for you include objects outside your detection zones and labels you never turned on; they belong in the data, leave them in.\n- Pictures of people are not people. A person on a poster, a screen or a photo gets no box. Remove the one camera.ui drew there.\n- Keep boxes tight. The box hugs the object, without a margin.\n- Include hidden parts. Box a partly hidden object in full, as far as you can judge its extent.\n- Faces and license plates get their own boxes, on top of the person or vehicle box.\n- Type the plate text. With a license plate box selected, a text field takes the plate as printed. Optional, and the ground truth for plate reading.\n- False alarms are valuable. If the frame shows none of the objects, delete all boxes and verify it empty. That is exactly how the model unlearns a false trigger.\n\nVerify marks the frame as done. Only verified frames can be submitted."},{"page":"detection/training","title":"Model training","heading":"Submitting to the pool","anchor":"submitting-to-the-pool","text":"The submit button at the bottom right sends every verified frame of the selected camera, whatever the list filter shows. To send only some, use Select candidates. Submitted frames leave the local list. Submitting requires your server to be signed in to camera.ui Cloud.\n\nMy contributions in the toolbar lists everything you have submitted, with the frame, its labels, and whether it is still waiting (Submitted) or already used (In training). Images and labels are tied to your cameraui.com account; deleting a contribution removes it from the pool."},{"page":"home-assistant/app","title":"Home Assistant app","heading":"Home Assistant app","anchor":"","text":"You can run the full camera.ui server inside Home Assistant OS as an app. There are two apps in the repository: camera.ui, the full server with its own panel in the Home Assistant sidebar, and camera.ui Worker, a helper that lends this machine's compute to a camera.ui server running elsewhere. Both are supported on amd64 and aarch64 hosts."},{"page":"home-assistant/app","title":"Home Assistant app","heading":"Install","anchor":"install","text":"1. In Home Assistant, open Settings → Apps → App Store.\n2. From the store's menu (top right), choose Repositories and add https://github.com/cameraui/homeassistant-apps.\n3. Find camera.ui in the store and click Install."},{"page":"home-assistant/app","title":"Home Assistant app","heading":"The camera.ui app","anchor":"the-cameraui-app","text":"The camera.ui app runs the complete server: the UI, the streaming engine, the NVR, and any plugins you install. It runs on the host network, which it needs for WebRTC live view, HomeKit pairing, and camera discovery.\n\nOnce started, the app adds a camera.ui entry to the Home Assistant sidebar. That panel, and the app's Open Web UI button, both open the interface through Home Assistant's ingress proxy, so there is no separate login and no certificate warning.[^direct] The panel signs you in according to your Home Assistant user:\n\n- Admins. Home Assistant admins are signed in as the homeassistant user, a master account. That user can't be renamed.\n- Other users. Anyone else who opens the panel gets a camera.ui account of their own with the user role, named after their Home Assistant username with ha- in front, ha-anna for example. When Home Assistant removes or disables that user or makes it an admin, camera.ui signs the account out everywhere the next time someone uses the panel.\n\nSign-out and password are not offered in the panel, the username can't be changed there, and API tokens are offered only to the master.\n\nOn first start the app downloads the camera.ui server and the default plugin, which needs an internet connection and takes a few minutes.[^firstboot] Follow the first boot in the app's Log tab.\n\n[^direct]: The app also listens on https://:3443 directly over the host network. Reaching it that way shows a self-signed certificate warning once.\n\n[^firstboot]: If first boot fails to fetch the server, check that the host can reach the internet and resolve DNS."},{"page":"home-assistant/app","title":"Home Assistant app","heading":"Options","anchor":"options","text":"- default_plugins. Comma-separated plugins installed on first boot. Defaults to @camera.ui/camera-ui-nvr, the NVR plugin.\n- recordings_path. Where the NVR plugin writes recordings. Defaults to /media/camera.ui. Paths under /media show up in the Home Assistant media browser.\n- internal_avahi. Runs the app's own mDNS stack for HomeKit pairing and camera discovery. On by default. Turn it off to use the host's mDNS instead."},{"page":"home-assistant/app","title":"Home Assistant app","heading":"Hardware acceleration","anchor":"hardware-acceleration","text":"The app passes GPU, AI accelerator, and codec devices through to the container.\n\n- Intel / AMD (VA-API). VA-API drivers ship on amd64 only, covering Intel Quick Sync and AMD Mesa VA-API decoding. aarch64 hosts do not get VA-API.\n- Coral Edge TPU. The Coral runtime is included for both architectures. USB Coral works through the app's USB access; PCIe Coral uses the passed-through /dev/apex_* devices.\n- Hailo. The Hailo device (/dev/hailo0) is passed through, but the Hailo runtime is not part of the image.[^hailo]\n- NVIDIA. Not available in the Home Assistant app. Use Docker on a plain Linux host for an NVIDIA setup.\n\nThe Hardware acceleration page explains how the streaming engine picks a device and how to check that it works.\n\n[^hailo]: Install the Hailo plugin to add the runtime. The app image itself only passes the device through."},{"page":"home-assistant/app","title":"Home Assistant app","heading":"Recordings and the media browser","anchor":"recordings-and-the-media-browser","text":"The /media directory is mapped into the app with read-write access. With the default recordings_path of /media/camera.ui, recordings written by the NVR appear in Home Assistant's Media browser."},{"page":"home-assistant/app","title":"Home Assistant app","heading":"Backups and updates","anchor":"backups-and-updates","text":"Home Assistant backs up the app's /data directory, which holds your configuration, database, and TLS certificates. Three subtrees are excluded from backups: the downloaded server, the Python environment, and installed plugins. These are re-downloaded on restore, so restoring a backup needs an internet connection, the same as a first start. camera.ui's own scheduled backups in their default folder and unfinished restore uploads are left out as well.\n\nThe server updates itself from within the app, so day-to-day updates do not need a new app image. Update the app itself when a new version is published to refresh the base image (OS, drivers, and launcher).\n\nLeave the supervisor's Watchdog toggle on for the app so Home Assistant restarts it if it stops unexpectedly.[^watchdog]\n\n[^watchdog]: Watchdog is a Home Assistant setting on the app's info page, not a camera.ui option."},{"page":"home-assistant/app","title":"Home Assistant app","heading":"Worker app","anchor":"worker-app","text":"The camera.ui Worker app turns this Home Assistant machine into a worker for a camera.ui server (the master) running somewhere else. It lends this machine's CPU, GPU, and Coral to the master for decoding, detection, and plugins. It has no UI and no sidebar panel of its own and connects out to the master. It passes through the same hardware as the full app.\n\nSet it up on the master first, then here:\n\n1. On the master, enable workers and set the master address, then generate a one-time pairing code. Generating a code and pairing both fail unless workers are enabled there. See Workers.\n2. Install the camera.ui Worker app on this machine.\n3. Set master_address to the master's IP or hostname and pairing_code to the code you just generated.\n4. Start the app. It exchanges the code for its own credentials on first connect and reconnects on its own afterwards, so the code is entered only once.\n\n- master_address. IP or hostname of the camera.ui master. Required.\n- pairing_code. The one-time code generated on the master. Required for the first connection. A code is valid for 15 minutes and can be used once. It may stay set after pairing; once the worker was removed on the master, enter a new code and restart the app.\n- worker_name. Name shown for this worker on the master. Optional. Without it, the worker uses the app's host name.\n- master_api_port. The master's HTTPS API port. Optional. Defaults to 3443.\n- internal_avahi. Runs the app's own mDNS stack. Off by default, the worker finds the master without it. Two avahi daemons on one machine, next to the camera.ui app for example, keep changing their host names. Turn it on only if a plugin on this worker needs avahi."},{"page":"home-assistant/app","title":"Home Assistant app","heading":"Next steps","anchor":"next-steps","text":"- Hardware acceleration — how the streaming engine uses your GPU and accelerators, and how to verify it.\n- Workers — masters, workers and pairing.\n- Worker assignments — which cameras and plugins run on a worker, and failover.\n- Docker — run the server outside Home Assistant, including NVIDIA setups."},{"page":"home-assistant/card","title":"Dashboard cards","heading":"Dashboard cards","anchor":"","text":"Three cards put camera.ui on a Home Assistant dashboard: a camera, a saved camview view, and a strip of recent events. A click opens the camera.ui dialog with the live picture and the timeline, inside Home Assistant.\n\nThe cards are served by your camera.ui server, so they always match the version you run. Home Assistant keeps a copy, so a dashboard still renders while the server is down."},{"page":"home-assistant/card","title":"Dashboard cards","heading":"Before you start","anchor":"before-you-start","text":"Set up the integration first. It registers the cards for you, so they appear in the card picker with no resource to add by hand.\n\nWho may use them is an integration option: Administrators only by default, or all Home Assistant users. See card access."},{"page":"home-assistant/card","title":"Dashboard cards","heading":"camera.ui Camera","anchor":"cameraui-camera","text":"One camera, as the snapshot tile you know from the home view or as the full live player.\n\ntype: custom:cameraui-card\nentity: camera.front_door\nmode: snapshot\nclick: popup\n\n- entity. Required. A camera.ui camera entity.\n- mode. snapshot (default) shows the tile with its refreshing picture, live runs the player in the camera's activity mode.\n- click. What a click does: popup (default) opens the camera.ui dialog, ha jumps to the camera in the sidebar panel, none does nothing.\n- title. Overrides the name shown on the card.\n- fit. contain (default) shows the whole picture, cover fills the card and crops.\n- controls and toolbar. Turn the player controls or the toolbar above them off.\n- detection_indicator. Turn off the marker that lights up while the camera detects something."},{"page":"home-assistant/card","title":"Dashboard cards","heading":"camera.ui View","anchor":"cameraui-view","text":"A camview view as a widget: the cameras you arranged there, in the same layout.\n\ntype: custom:cameraui-view-card\nview: Garden\n\n- view. The name of the view to show. Without it the card asks you to pick one.\n- rearrange. Set to false to hide the rearrange button, so the layout cannot be changed from the dashboard.\n- activity_mode. per-camera (default) runs each camera in its own activity mode. always-on, activity or standby sets one mode for the whole view."},{"page":"home-assistant/card","title":"Dashboard cards","heading":"camera.ui Events","anchor":"cameraui-events","text":"The recent events strip, with thumbnails. A click opens the event.\n\ntype: custom:cameraui-events-card\nentities:\n - camera.front_door\n - camera.driveway\n\n- entities. Limits the strip to these cameras. Left out, it shows every camera."},{"page":"home-assistant/card","title":"Dashboard cards","heading":"Next steps","anchor":"next-steps","text":"- Home Assistant integration — entities, automations, card access and the media browser.\n- Camview — build the views the view card shows."},{"page":"home-assistant/import","title":"Import from Home Assistant","heading":"Import from Home Assistant","anchor":"","text":"The Home Assistant plugin brings Home Assistant's own sensors and controls into camera.ui. It is the reverse of the integration: Home Assistant devices come into camera.ui, where you can assign them to cameras and use them as detection triggers."},{"page":"home-assistant/import","title":"Import from Home Assistant","heading":"Setup","anchor":"setup","text":"Install and enable the Home Assistant plugin from the Plugins page, then open its settings:\n\n- Home Assistant URL. The address of your Home Assistant, for example http://homeassistant.local:8123.\n- Access Token. A long-lived access token from your Home Assistant profile, under Security.\n\nRunning camera.ui as the Home Assistant app? Leave both empty. The plugin connects to Home Assistant on its own, no token needed."},{"page":"home-assistant/import","title":"Import from Home Assistant","heading":"What you can import","anchor":"what-you-can-import","text":"The plugin offers every Home Assistant entity it can map, under Discovered on the Sensors page, with its name, entity id, type and room. Nothing comes over until you pick it.\n\n- Sensors. Motion, occupancy, contact, doorbell, smoke, leak, gas, carbon monoxide, and other supported types become camera.ui sensors.\n- Controls. Locks, garage doors, alarm panels, switches, lights, and sirens come in as controls. Operating one in camera.ui operates it in Home Assistant.\n\nUnsupported entity types never show up. To keep specific entities out of the offer as well, list them under Excluded Entities."},{"page":"home-assistant/import","title":"Import from Home Assistant","heading":"Using imported sensors","anchor":"using-imported-sensors","text":"Adopted sensors show up in the Sensors view like any other. Assigned to a camera, one works as a detection trigger like a native sensor: a Home Assistant motion detector or door contact can start a camera event. Imported controls appear in the camera's Overview and in automations. On the Sensors page each imported sensor shows its Home Assistant entity id as its ID, to tell apart entities with the same friendly name and to search by. The entity id is only a label: camera.ui identifies the entity by its registry id, so renaming it in Home Assistant keeps the sensor and its assignments. If you delete the entity in Home Assistant, the sensor stays and is marked as removed until you delete it here too; if Home Assistant is down, it just shows as unavailable.\n\nAdopted entities are remembered by camera.ui, not by the plugin, so they survive restarts of both."},{"page":"home-assistant/import","title":"Import from Home Assistant","heading":"Running the integration too","anchor":"running-the-integration-too","text":"You can import from Home Assistant and run the integration at the same time. The plugin never re-imports the cameras and sensors that camera.ui itself exports to Home Assistant.\n\nImported sensors are never sent back. A sensor pulled in from Home Assistant is never exported to Home Assistant again, not by this plugin and not by the integration, even if you export it to another bridge like HomeKit."},{"page":"home-assistant/","title":"Home Assistant","heading":"Home Assistant","anchor":"","text":"camera.ui and Home Assistant meet in two independent ways: the app runs camera.ui inside Home Assistant, the integration connects a camera.ui server to Home Assistant and surfaces its cameras and sensors as HA entities. Use either or both."},{"page":"home-assistant/","title":"Home Assistant","heading":"Running vs connecting","anchor":"running-vs-connecting","text":"- The app runs camera.ui. It installs the camera.ui server as a Home Assistant OS app, so your cameras, recordings, and detection live on the same box as HA.\n- The integration connects camera.ui. It talks to a camera.ui server (the app, or a Docker/desktop install anywhere on your network) and creates one HA device per camera with live streams, sensors, and PTZ.\n\nThe app alone creates no entities: add the integration as well. If camera.ui already runs elsewhere, you only need the integration."},{"page":"home-assistant/","title":"Home Assistant","heading":"Getting entities in: pick one transport","anchor":"getting-entities-in-pick-one-transport","text":"Two independent transports bring camera.ui cameras and sensors into Home Assistant:\n\n1. The integration (recommended). A custom component that pushes entities to HA over a local connection. No broker needed.\n2. MQTT discovery. An opt-in on the camera.ui server's MQTT bridge that publishes Home Assistant discovery configs to your broker.\n\nDo not run both\nThe two do not merge: running both leaves you with two copies of every camera, device and sensor. Use the integration unless you specifically want to route camera.ui through an existing MQTT broker."},{"page":"home-assistant/","title":"Home Assistant","heading":"The integration","anchor":"the-integration","text":"Live camera changes arrive by push, and it re-checks the camera list once a minute as a backstop.\n\nYou get, per camera:\n\n- A live camera entity with streaming. Snapshots and the live source (high, mid, or low RTSP) come straight from camera.ui.\n- Motion and object-detection sensors. Object sensors cover the labels person, vehicle, animal, and package.\n- Sensors across the binary_sensor, sensor, switch, light, siren, lock, cover, and alarm_control_panel domains. Switches, lights, sirens, locks, covers, and alarm panels are controllable from Home Assistant; contact and measurement sensors are read-only.\n- A PTZ service, cameraui.ptz, for continuous, stop, move, absolute, preset, and home actions.\n\nOn top of that, the integration registers its dashboard cards, embeds the full camera.ui web UI as a sidebar panel, and fires a cameraui_event on the HA event bus for every detection, with device triggers wired up for the automation editor. Setup can start from zeroconf: camera.ui advertises itself over mDNS, so host and port are pre-filled and you only enter an access token.\n\nSee Integration for the full setup."},{"page":"home-assistant/","title":"Home Assistant","heading":"MQTT discovery","anchor":"mqtt-discovery","text":"For setups that already run an MQTT broker. It needs no custom component and no HACS: the camera.ui server publishes retained discovery configs, and Home Assistant's built-in MQTT integration picks them up.\n\nYou turn it on in the camera.ui server's MQTT settings (enable HA discovery). You get status, motion, and object binary sensors, a snapshot camera image, and the same controllable sensor domains as the integration. What you do not get: a live stream (snapshot only), the PTZ service, the sidebar panel, the auto-registered cards, or the cameraui_event bus events. In return it adds a dedicated per-camera connectivity sensor.\n\nMQTT discovery is documented with the rest of the broker settings on the MQTT admin page."},{"page":"home-assistant/","title":"Home Assistant","heading":"Integration vs MQTT discovery","anchor":"integration-vs-mqtt-discovery","text":"| | Integration | MQTT discovery |\n|---|---|---|\n| Needs an MQTT broker | No | Yes |\n| Custom component / HACS | Required | Not needed |\n| Live camera stream | Yes | Snapshot only |\n| Motion & object sensors | Yes | Yes |\n| Controllable sensors | Yes | Yes |\n| PTZ service | Yes | No |\n| Per-camera connectivity sensor | No separate one | Yes |\n| Embedded web UI panel | Yes | No |\n| Dashboard cards auto-registered | Yes | No |\n| cameraui_event + device triggers | Yes | No |\n| Discovery | zeroconf (mDNS) | over MQTT |"},{"page":"home-assistant/","title":"Home Assistant","heading":"Bringing Home Assistant sensors in","anchor":"bringing-home-assistant-sensors-in","text":"The integration and MQTT both send camera.ui out to Home Assistant. The Home Assistant plugin goes the other way: it imports Home Assistant's own sensors and controls into camera.ui, where you can assign them to cameras and use them as detection triggers. It does not clash with either transport.\n\nSee Import from Home Assistant for setup."},{"page":"home-assistant/","title":"Home Assistant","heading":"The dashboard cards","anchor":"the-dashboard-cards","text":"Three cards put a camera, a camview view or the recent events on your dashboards, and a click opens the camera.ui dialog with the timeline. The integration registers them.\n\nSee Dashboard cards for the options."},{"page":"home-assistant/","title":"Home Assistant","heading":"Next steps","anchor":"next-steps","text":"- App — run the camera.ui server inside Home Assistant OS.\n- Integration — connect a camera.ui server and get cameras, sensors, and PTZ as HA entities.\n- Dashboard cards — put cameras, a view or recent events on your dashboard.\n- Import from Home Assistant — bring Home Assistant's own sensors and controls into camera.ui."},{"page":"home-assistant/integration","title":"Home Assistant integration","heading":"Home Assistant integration","anchor":"","text":"The camera.ui integration connects an existing camera.ui server to Home Assistant. Your cameras, their sensors, motion and object detection, PTZ controls and the full camera.ui interface all show up inside Home Assistant.\n\nIt talks to the server directly over your local network, and events arrive by push. No MQTT broker or cloud service is involved. The integration's domain is cameraui. To run camera.ui itself inside Home Assistant OS, see the App page."},{"page":"home-assistant/integration","title":"Home Assistant integration","heading":"Install","anchor":"install","text":"The integration is distributed through HACS as a custom repository and needs Home Assistant 2026.3 or newer.\n\n1. In HACS, add cameraui/homeassistant-integration as a custom Integration repository.\n2. Install camera.ui from HACS.\n3. Restart Home Assistant."},{"page":"home-assistant/integration","title":"Home Assistant integration","heading":"Discovery","anchor":"discovery","text":"camera.ui advertises itself on the local network over mDNS (_camera-ui._tcp.local.). When Home Assistant sees it, a camera.ui found card appears under Settings → Devices & services. Open it and enter your API token; the host and port are filled in from discovery."},{"page":"home-assistant/integration","title":"Home Assistant integration","heading":"Manual setup","anchor":"manual-setup","text":"If discovery doesn't reach the server (a different subnet, for example), add it by hand:\n\n1. Go to Settings → Devices & services → Add integration and search for camera.ui.\n2. Enter the Host, Port (default 3443) and your API token."},{"page":"home-assistant/integration","title":"Home Assistant integration","heading":"API token","anchor":"api-token","text":"Both paths need an API token. Create one in camera.ui under Settings → Account → API tokens.\n\nUse a token from an admin account. Controlling camera.ui sensors from Home Assistant (locks, covers, lights, sirens, alarm panels, PTZ) needs admin rights; with a viewer token the entities show up but their controls are rejected.\n\nSetup checks the token against the server: a rejected token gives an authentication error, an unreachable server a connection error.[^ssl]\n\n[^ssl]: The server is reached over HTTPS. The integration does not verify the certificate, so a self-signed camera.ui certificate works without extra setup."},{"page":"home-assistant/integration","title":"Home Assistant integration","heading":"What you get","anchor":"what-you-get","text":"The integration creates one Home Assistant device per camera and keeps the list in sync. It re-reads the camera list every 60 seconds, and a camera you remove in camera.ui is removed from Home Assistant automatically."},{"page":"home-assistant/integration","title":"Home Assistant integration","heading":"Cameras","anchor":"cameras","text":"Each camera device carries a camera entity that streams live and serves snapshots. The device is labeled with the manufacturer camera.ui and links back to the server's web interface."},{"page":"home-assistant/integration","title":"Home Assistant integration","heading":"Sensors","anchor":"sensors","text":"Every exposed sensor in camera.ui is mirrored into Home Assistant on the matching platform, whether it belongs to a camera or not:\n\n| camera.ui sensor | Home Assistant platform |\n|---|---|\n| Binary / contact | binary_sensor |\n| Measurement | sensor |\n| Switch | switch |\n| Light | light |\n| Siren | siren |\n| Lock | lock |\n| Cover | cover |\n| Alarm system | alarm_control_panel |\n\nCamera hardware (a camera's own spotlight, siren or battery) lands on the camera's device. Every other sensor becomes a device of its own, linked below its camera when it is assigned to exactly one. Whether a sensor comes across at all is the Expose sensor toggle on the Sensors page. Camera hardware has no toggle, it always follows its camera.\n\nThe controllable ones work both ways: a switch and a siren turn on and off, a light turns on and off (and dims, if it reports brightness), a cover opens and closes, a lock locks and unlocks, and an alarm panel arms home, away or night and disarms.\n\nAdding, renaming, re-assigning, exposing or deleting a sensor in camera.ui updates the matching Home Assistant entity live, without a restart. A sensor whose plugin is offline shows as unavailable."},{"page":"home-assistant/integration","title":"Home Assistant integration","heading":"Motion and object detection","anchor":"motion-and-object-detection","text":"Every camera gets a motion binary sensor that turns on while a detection is active.\n\nCameras with object detection also get one binary sensor each for person, vehicle, animal and package. Each turns on while that object is in view and clears when the detection ends.[^objectclass]\n\nIf the connection to camera.ui drops, the entities become unavailable. Once it is back, a detection that is still running carries on without a second start. One that ended in the meantime ends then: motion and object sensors turn off, recognition sensors clear and cameraui_event fires end.\n\n[^objectclass]: Motion and object binary sensors are registered with the motion device class, so they present in Home Assistant as motion sensors rather than as a per-object class."},{"page":"home-assistant/integration","title":"Home Assistant integration","heading":"Recognition sensors","anchor":"recognition-sensors","text":"Cameras with the matching detector get value sensors for face, license plate and classification. The sensor's value is the recognized labels, and the raw detections are available in its attributes."},{"page":"home-assistant/integration","title":"Home Assistant integration","heading":"Updates","anchor":"updates","text":"Home Assistant also gets update entities for the camera.ui server and its plugins. Available updates show up in Home Assistant's updates list and install from there. The update dialog offers to create a backup first, which writes a camera.ui backup and stops the update if that fails. A server update that a worker would not survive stops with the worker's name: update that worker first, or start the update on camera.ui's Updates page and confirm it there (Updating). Turn Allow updates from Home Assistant off in the integration options to keep the updates visible without an install button."},{"page":"home-assistant/integration","title":"Home Assistant integration","heading":"PTZ","anchor":"ptz","text":"Pan, tilt and zoom is exposed as a service, cameraui.ptz, rather than as an entity. Target one or more camera entities and pick an action:\n\n- continuous. Move at a set speed until stopped, using pan, tilt and zoom as speeds.\n- stop. Stop a continuous move.\n- move. Move by a relative step.\n- absolute. Go to an absolute position.\n- home. Return to the home position.\n- preset. Recall a saved preset by name.\n\nThe pan, tilt and zoom fields run from -1 to 1. The preset action needs a preset name. If none of the targeted cameras has PTZ, the service reports an error.\n\naction:\n - service: cameraui.ptz\n target:\n entity_id: camera.driveway\n data:\n action: preset\n preset: gate"},{"page":"home-assistant/integration","title":"Home Assistant integration","heading":"Device triggers","anchor":"device-triggers","text":"Each camera device offers three triggers in the automation editor:\n\n- Detection started. A detection began on the camera.\n- Detection ended. The detection cleared.\n- Object recognized. A face, plate or classification was recognized during a detection."},{"page":"home-assistant/integration","title":"Home Assistant integration","heading":"The event bus","anchor":"the-event-bus","text":"The integration also fires a cameraui_event on the Home Assistant event bus, several times across one detection's lifecycle:\n\n| state | When |\n|---|---|\n| start | A detection begins. |\n| object | A new object label appears during the detection. |\n| recognized | A face, plate or classification is recognized during the detection. |\n| end | The detection clears. |\n\nEach event carries these fields:\n\n| Field | Contents |\n|---|---|\n| camera_id | The camera's id. |\n| camera_name | The camera's name. |\n| state | One of the values above. |\n| detection_types | The detection types involved. |\n| event_id | The id shared by all events of one detection. |\n| labels | Object labels seen. |\n| faces | Recognized faces. |\n| plates | Recognized license plates. |\n| classifications | Other recognized attributes. |\n| attributes | The full raw attribute list. |\n\nFilter on state to act on a specific point in the lifecycle:\n\nautomation:\n - alias: Notify on recognized face\n trigger:\n - platform: event\n event_type: cameraui_event\n event_data:\n state: recognized\n action:\n - service: notify.mobile_app\n data:\n message: >-\n {{ trigger.event.data.faces | join(', ') }}\n seen at {{ trigger.event.data.camera_name }}\n\nautomation:\n - alias: Turn on light when a person appears\n trigger:\n - platform: event\n event_type: cameraui_event\n event_data:\n state: object\n condition:\n - \"{{ 'person' in trigger.event.data.labels }}\"\n action:\n - service: light.turn_on\n target:\n entity_id: light.driveway\n\nThere is no device trigger for the object state, so use the event bus for that one.[^objecttrigger]\n\n[^objecttrigger]: The three device triggers map to start, end and recognized. To react to a specific object label appearing mid-detection, listen for cameraui_event with state: object as shown above."},{"page":"home-assistant/integration","title":"Home Assistant integration","heading":"Sidebar panel","anchor":"sidebar-panel","text":"Setup adds a camera.ui entry to the Home Assistant sidebar that embeds the full camera.ui interface. The panel is admin only: it appears for administrator accounts and stays hidden for everyone else."},{"page":"home-assistant/integration","title":"Home Assistant integration","heading":"Dashboard cards","anchor":"dashboard-cards","text":"Three cards come with the integration and register themselves: a camera, a camview view, and a strip of recent events. See Dashboard cards for their options."},{"page":"home-assistant/integration","title":"Home Assistant integration","heading":"Card access","anchor":"card-access","text":"The integration's options decide who may use them: Administrators only, the default, or All Home Assistant users. The cards reach camera.ui through Home Assistant using the integration's own token, so opening them up hands every Home Assistant user what that token can see. Viewer token takes a second camera.ui token that is used for everyone who is not a Home Assistant administrator, which is how you point them at a restricted camera.ui account instead.\n\nAll dashboards that use one token share its connection limit. With many wall displays, a Viewer token gives the non-administrators a limit of their own."},{"page":"home-assistant/integration","title":"Home Assistant integration","heading":"Recordings in the media browser","anchor":"recordings-in-the-media-browser","text":"Events that have a recording appear in Home Assistant's Media browser under camera.ui: first the cameras, plus an All cameras entry, then the days, then the events themselves, each with a thumbnail and named by its time and what was detected. A click plays the clip, and it starts while the export is still running. Any entry can be sent to a TV or a speaker with media_player.play_media.\n\nClip quality in the options picks what those clips use: the sub stream, small and quick, or the main stream at full quality."},{"page":"home-assistant/integration","title":"Home Assistant integration","heading":"Next steps","anchor":"next-steps","text":"- App — run camera.ui itself as a Home Assistant OS app.\n- Dashboard cards — configure the cards on a dashboard.\n- Sensors — assign sensors to a camera so they reach Home Assistant."},{"page":"","title":"What is camera.ui?","heading":"What is camera.ui?","anchor":"","text":"camera.ui is a self-hosted platform for your security cameras. It combines live viewing, continuous and event-based recording, on-device AI detection, and smart-home integration in one interface, all on hardware you own.\n\nThere is no mandatory cloud, and no third party holds a copy of your recordings. Remote access is optional.\n\nNew here?\nThe Quick start takes you from install to recording in seven steps. For the first run in detail see Getting started.\n::: info Work in progress\nIf something is missing or unclear, tell us on Discord or open an issue on GitHub."},{"page":"","title":"What is camera.ui?","heading":"What you can do","anchor":"what-you-can-do","text":"- Watch live. Low-latency streaming over WebRTC and MSE, multi-camera grids, two-way talk, PTZ control, and snapshots. A floor plan of your home shows which camera is seeing something right now.\n- Record around the clock.[^license] Continuous, event-based, or ad-hoc recording, with a scrubbable timeline, synchronized multi-camera playback, and MP4 / timelapse export.\n- Detect what matters. Motion, object, face, license-plate, and audio detection, all running locally on your own hardware, plus semantic search and AI-generated event descriptions.\n- Stay informed.[^license] Per-camera push notifications, alerts limited by area, object type or the people you name, quiet hours, and critical alerts that always get through.\n- Connect your cameras. Add cameras over ONVIF and dozens of other protocols, and bridge them into your smart home via Apple HomeKit or Home Assistant (over MQTT)[^smarthome].\n- Extend it. A plugin system lets you add cameras, detectors, and integrations from an in-app store."},{"page":"","title":"What is camera.ui?","heading":"Live demo","anchor":"live-demo","text":"Try the live demo before installing."},{"page":"","title":"What is camera.ui?","heading":"Run it your way","anchor":"run-it-your-way","text":"- Desktop app for macOS, Windows, and Linux. The easiest way to get started, running the server and viewer in one app.\n- Server for always-on setups: Docker, Proxmox, or bare-metal Linux.\n- Mobile apps for iOS and Android.\n\nNot sure which to pick? See Which installation?."},{"page":"","title":"What is camera.ui?","heading":"How it works","anchor":"how-it-works","text":"One server connects to your cameras, records, runs detection and serves the web interface. The desktop and mobile apps are viewers. More in How it works."},{"page":"","title":"What is camera.ui?","heading":"Next steps","anchor":"next-steps","text":"- Getting started — set up camera.ui for the first time\n- Add a camera — connect your first camera\n- Recording (NVR) — configure 24/7 and event recording\n- Detection & AI — set up motion and object detection\n\n[^license]: An active camera.ui subscription covers recording (NVR) and the features built on it, such as playback, export, face recognition, semantic search, and AI descriptions, plus push notifications. Live view and real-time detection are free.\n[^smarthome]: Google Home and Alexa support is planned."},{"page":"install/desktop","title":"Desktop app","heading":"Desktop app","anchor":"","text":"The desktop app is the recommended way to run camera.ui on macOS and Windows. One download installs server and viewer together. It also runs on Linux, though there a Docker or Proxmox server is the better choice (see Which installation?)."},{"page":"install/desktop","title":"Desktop app","heading":"Download","anchor":"download","text":"Installers are below and on the download page. The links always point to the latest release.\n\n| Platform | Download |\n|---|---|\n| macOS (Apple Silicon) | camera.ui-arm64.dmg |\n| macOS (Intel) | camera.ui-x64.dmg |\n| Windows 10 / 11 | camera.ui-x64.exe |\n| Linux (AppImage) | x64 · arm64 |\n| Linux (.deb) | amd64 · arm64 |"},{"page":"install/desktop","title":"Desktop app","heading":"macOS","anchor":"macos","text":"Drag camera.ui from the .dmg into Applications. The app is signed and notarized, so it opens without warnings."},{"page":"install/desktop","title":"Desktop app","heading":"Windows","anchor":"windows","text":"The .exe installs for the current user, no administrator rights needed.\n\nThe app is not code-signed yet, so SmartScreen may warn. Select More info → Run anyway."},{"page":"install/desktop","title":"Desktop app","heading":"Linux","anchor":"linux","text":"Install the .deb:\n\nsudo apt install ./camera.ui-*.deb\n\nOr make the .AppImage executable and run it:\n\nchmod +x camera.ui-*.AppImage\n./camera.ui-*.AppImage\n\nPick the .AppImage if you want automatic updates\nOnly the .AppImage updates itself. A .deb install stays on its version until you install a newer .deb."},{"page":"install/desktop","title":"Desktop app","heading":"First launch: choose how to run","anchor":"first-launch-choose-how-to-run","text":"On first launch the app asks \"How should camera.ui run?\":\n\n- Server. Runs the camera.ui server on this machine. The all-in-one option.\n- Worker. Takes on part of the work for another camera.ui server on your network. Needs the main server's address and a pairing code from its Workers settings. See Worker setup.\n- Viewer · Direct. Connects to an existing server by address.\n- Viewer · Cloud. Signs in via cloud.cameraui.com to reach a server through camera.ui Cloud.\n\nServer mode continues with first-run setup. Worker mode has no window, only the tray icon and a status screen."},{"page":"install/desktop","title":"Desktop app","heading":"Starting without the picker","anchor":"starting-without-the-picker","text":"The mode can also come from the command line, then the picker is skipped:\n\n| Mode | Options |\n|---|---|\n| Server | --mode=runtime |\n| Worker | --mode=worker --master=192.168.1.10 --pairing-code=XXXX-XXXX, optionally --worker-name=nas |\n| Viewer · Direct | --mode=viewer-direct --server=192.168.1.10:3443 |\n| Viewer · Cloud | --mode=viewer-cloud |\n\nThe app stores the mode as if it had been picked. Started with a different --mode while it runs, it switches over, the same mode again changes nothing.[^macosargs] Values always follow an =. A missing or wrong value is noted in the log, and the app starts as if the option were not there."},{"page":"install/desktop","title":"Desktop app","heading":"The tray icon","anchor":"the-tray-icon","text":"The tray icon holds the app's own settings:\n\n- Open At Login. Starts camera.ui at login, straight to the tray without a window.[^autostart]\n- Close to tray. Off by default. When on, closing the window only hides it and a server on this machine keeps running.[^closetotray]\n- Switch mode…. Changes how the app runs, without reinstalling.\n- Quit. Stops the app, and in Server mode the server with it.\n\nOn Windows, left-click the tray icon to open the window, right-click for the menu.\n\nGNOME shows a tray icon only with the AppIndicator extension. Switch mode… also sits in the app menu under File, and starting the app with --switch-mode opens the mode picker: in the running instance if one is up, otherwise on that launch.[^macosargs] Both also work for a worker, which has no window of its own.\n\n[^autostart]: macOS and Windows only.\n[^closetotray]: Windows and Linux only. On macOS closing the window always only hides it, so the app and its desktop notifications keep running until you quit it.\n[^macosargs]: On macOS, start it with open -n -a camera.ui --args --mode=runtime. Without -n a running app only comes to the front and never sees the options."},{"page":"install/desktop","title":"Desktop app","heading":"macOS permissions","anchor":"macos-permissions","text":"On macOS 15 and newer, allow local network access on first launch, otherwise the app can't reach your cameras. Two-way talk asks for microphone access, and the geofence trigger's \"My location\" button in Automations for location access."},{"page":"install/desktop","title":"Desktop app","heading":"Updates","anchor":"updates","text":"The app checks for updates at startup and while it runs. It installs one only after a click, on its start screen or on the Updates page from any device. The bundled server updates with the app.\n\nOn Linux only the .AppImage updates itself; a .deb is updated from the download page."},{"page":"install/desktop","title":"Desktop app","heading":"Where your data lives","anchor":"where-your-data-lives","text":"In Server mode, settings and recordings live in the app's per-user data directory:\n\n- macOS: ~/Library/Application Support/camera.ui/camera.ui/volume\n- Windows: %APPDATA%\\camera.ui\\camera.ui\\volume\n- Linux: ~/.config/camera.ui/camera.ui/volume\n\nThe recording settings can move recordings to another disk, an external drive for example. Avoid a network share, see Storage & retention.\n\nSee Backup & restore to keep a copy."},{"page":"install/docker","title":"Docker","heading":"Docker","anchor":"","text":"Docker is the recommended way to run the camera.ui server on Linux. The image is built on Ubuntu 24.04 and downloads the server itself on first start, so server updates don't need a new image. There is one image per hardware target, published as ghcr.io/cameraui/camera.ui."},{"page":"install/docker","title":"Docker","heading":"Before you start","anchor":"before-you-start","text":"You need a Linux host with Docker Engine and the Compose plugin.\n\nDocker Desktop won't work\nDocker Desktop on macOS and Windows can't use host networking, which camera.ui relies on for mDNS and WebRTC. On those platforms, use the desktop app instead."},{"page":"install/docker","title":"Docker","heading":"Quick start","anchor":"quick-start","text":"Save this as docker-compose.yml:\n\nname: cameraui\n\nservices:\n cameraui:\n image: ghcr.io/cameraui/camera.ui:latest\n container_name: cameraui\n restart: unless-stopped\n stop_grace_period: 30s\n network_mode: host\n environment:\n - TZ=Europe/Berlin\n - CAMERAUI_DOCKER_AVAHI=true\n volumes:\n - cameraui-data:/data\n\nvolumes:\n cameraui-data:\n\nStart it:\n\ndocker compose up -d\n\nThen open https://:3443. The first boot downloads the server and takes a few minutes (docker compose logs -f shows progress). The certificate is self-signed, so the browser warns on first visit. Next: Getting started.\n\nFirst boot needs internet\nOn first start the container downloads the server from the npm registry. If your host can't resolve it, add public DNS resolvers (1.1.1.1, 8.8.8.8) to the service."},{"page":"install/docker","title":"Docker","heading":"Hardware acceleration","anchor":"hardware-acceleration","text":"The default image (latest) runs detection and video processing in software. For acceleration, layer the override of the flavor that matches your hardware on top of the base file.\n\n| Flavor | Tag | Hardware acceleration | Arch |\n|---|---|---|---|\n| CPU | latest | software | amd64 + arm64 |\n| Intel | intel | Quick Sync / VA-API + OpenCL | amd64 |\n| NVIDIA | nvidia | NVENC / NVDEC + CUDA 13 | amd64 |\n| NVIDIA (TensorRT) | nvidia-tensorrt | NVENC / NVDEC + CUDA 13 + TensorRT | amd64 |\n| NVIDIA (CUDA 12) | nvidia-cuda12 | NVENC / NVDEC + CUDA 12 | amd64 |\n| AMD | amd | Mesa VA-API + OpenCL | amd64 |\n\nSave the matching override next to your docker-compose.yml:\n\n-group\n\n [docker-compose.intel.yml]\nname: cameraui\nservices:\n cameraui:\n image: ghcr.io/cameraui/camera.ui:intel\n devices:\n - /dev/dri:/dev/dri\n\n [docker-compose.nvidia.yml]\nname: cameraui\nservices:\n cameraui:\n image: ghcr.io/cameraui/camera.ui:nvidia\n environment:\n - NVIDIA_VISIBLE_DEVICES=all\n - NVIDIA_DRIVER_CAPABILITIES=all\n deploy:\n resources:\n reservations:\n devices:\n - driver: nvidia\n count: all\n capabilities: [gpu, compute, video, utility]\n\n [docker-compose.amd.yml]\nname: cameraui\nservices:\n cameraui:\n image: ghcr.io/cameraui/camera.ui:amd\n devices:\n - /dev/dri:/dev/dri\n\nboth files together (Intel shown):\n\ndocker compose -f docker-compose.yml -f docker-compose.intel.yml up -d\n\nThe NVIDIA flavors also require the NVIDIA Container Toolkit on the host. nvidia ships CUDA 13 and needs an NVIDIA driver 580 or newer, which covers RTX 50xx cards. nvidia-cuda12 keeps CUDA 12 for older drivers and pairs with the ONNX Legacy plugin. nvidia-tensorrt adds the TensorRT runtime (about 2 GB) for the ONNX plugin's tensorrt provider. Ready-made compose files for every flavor are in the cameraui/docker repository.\n\nHost drivers, device passthrough for AI accelerators (Coral, Hailo, Intel NPU) and verification: Hardware acceleration."},{"page":"install/docker","title":"Docker","heading":"Networking","anchor":"networking","text":"Host networking (the compose default) is recommended: camera.ui needs it for mDNS / Bonjour (HomeKit pairing, ONVIF discovery) and WebRTC live view. Without it, publish the ports explicitly."},{"page":"install/docker","title":"Docker","heading":"Storage for recordings","anchor":"storage-for-recordings","text":"By default, recordings live in the /data volume. For a separate disk, bind-mount it and point the NVR at it:\n\n volumes:\n - cameraui-data:/data\n - /mnt/recordings:/recordings\n environment:\n - CAMERAUI_PLUGIN_NVR_STORAGE_PATH=/recordings\n\nUse a dedicated local disk for /recordings, not a network share. See System requirements."},{"page":"install/docker","title":"Docker","heading":"Workers","anchor":"workers","text":"A worker is a second machine that takes over camera work (decoding, plugins) from your main server. It runs no UI and no streaming engine of its own. It uses the same image as the server, started in worker mode with CAMERA_UI_WORKER=true.\n\nEnable workers on the main server and generate a pairing code there first. Compose file and environment variables: Worker setup, camera assignment: Worker assignments.\n\nCAMERA_UI_WORKER_CAPABILITIES narrows what the worker takes on: frameDecoding for decoding cameras, pluginHost for running plugins. Leave it unset and the worker offers both."},{"page":"install/docker","title":"Docker","heading":"Ports","anchor":"ports","text":"camera.ui uses these ports (already covered by host networking):\n\n| Port | Proto | Purpose |\n|---|---|---|\n| 3443 | tcp | HTTPS UI / API |\n| 2000 | tcp | go2rtc |\n| 2001 | tcp | RTSP |\n| 2002 | tcp | SRTP |\n| 2003 | tcp | RTMP |\n| 2004 | tcp/udp | WebRTC |\n| 1883 | tcp | MQTT broker, only if you enable the built-in one |\n\nWebRTC media runs over UDP on 2004, with TCP on the same port as fallback. Without host networking, map both."},{"page":"install/docker","title":"Docker","heading":"Data & backups","anchor":"data-backups","text":"All state lives in the cameraui-data volume: config, database, recordings, and TLS certificates. See Backup & restore.\n\nTo install plugins from a private registry or a mirror, mount your .npmrc to /root/.npmrc, see Private registry or mirror."},{"page":"install/docker","title":"Docker","heading":"Updating","anchor":"updating","text":"Pulling a new image updates only the image (OS, GPU libraries, launcher), not the server installed in the volume. Update the server from the Updates page, or run cameraui update-server -H /data in the container and restart it. The image:\n\ndocker compose pull\ndocker compose up -d\n\nSee Updating for details."},{"page":"install/hardware-acceleration","title":"Hardware acceleration","heading":"Hardware acceleration","anchor":"","text":"camera.ui accelerates two things: the video pipeline (decoding camera streams for detection, live view and snapshots, and encoding for streaming) and AI inference (object detection and everything built on it).\n\nVideo acceleration is automatic on every platform. camera.ui probes the hardware at runtime, picks the best available backend and falls back to software if there is none. AI accelerators are automatic too once the hardware is reachable; on Docker that means passing the device through (below)."},{"page":"install/hardware-acceleration","title":"Hardware acceleration","heading":"What runs where","anchor":"what-runs-where","text":"| Platform | Video pipeline (automatic) | AI backends |\n|---|---|---|\n| macOS (desktop app, Intel & Apple Silicon) | VideoToolbox (Apple GPU + media engine) | CoreML (Apple GPU + Neural Engine), ONNX |\n| Windows (desktop app, x64) | NVIDIA (CUDA/NVENC), Intel (QSV), and any GPU via Direct3D 11/12 | OpenVINO (Intel GPU/NPU), ONNX (NVIDIA CUDA) |\n| Linux x64 (Docker, bare-metal, Proxmox) | NVIDIA (CUDA/NVENC), Intel (VA-API/QSV), AMD (VA-API), plus Vulkan/OpenCL | OpenVINO (Intel GPU/NPU), ONNX (CUDA/TensorRT), NCNN (Vulkan), Coral, Hailo |\n| Linux arm64 (Rockchip) | Rockchip (RKMPP) | Coral, Hailo, NCNN (CPU/NEON) |\n\nThe table shows what camera.ui can use. The matching GPU driver still has to be installed on the host. A backend whose driver is missing is skipped silently, never an error.\n\nThe desktop app needs no setup: macOS ships video acceleration with the OS, Windows uses the system graphics driver. Each detection plugin logs what every model runs on (Loaded model: …) and shows it under Active Hardware in its plugin settings.\n\nThe rest of this page is for Linux and Docker, where hardware has to cross the container boundary in three steps:\n\n1. Host driver. The kernel-side driver must be installed on the host (the only part a Docker image can never ship).\n2. Device passthrough. The device node goes into the devices: section of your compose file.\n3. Verify. The container tells you what actually arrived.\n\nGPUs are matched by picking the right image flavor. AI accelerators work with any flavor: the Coral runtime is baked into every image, and the Hailo runtime ships with its plugin. The one exception is the Intel NPU, whose user-space driver is bundled in the intel flavor only."},{"page":"install/hardware-acceleration","title":"Hardware acceleration","heading":"Check your host","anchor":"check-your-host","text":"The cameraui/docker repository ships a host helper that diagnoses your machine and installs the drivers that need more than a package manager:\n\ncurl -fsSLO https://raw.githubusercontent.com/cameraui/docker/main/scripts/host/cameraui-host.sh\nsudo bash cameraui-host.sh check # read-only: drivers, docker runtime, device nodes\nsudo bash cameraui-host.sh nvidia # install the NVIDIA Container Toolkit\nsudo bash cameraui-host.sh hailo # build + install the Hailo PCIe kernel driver\nsudo bash cameraui-host.sh coral # install the gasket/apex driver (PCIe/M.2 Coral)\n\ncheck prints a ✓/✗ line for every accelerator class with the fix for anything missing. Run it first."},{"page":"install/hardware-acceleration","title":"Hardware acceleration","heading":"Verify from the container","anchor":"verify-from-the-container","text":"On every start the container logs which device nodes it can actually see:\n\n[setup] accelerator devices: /dev/dri ✓ · /dev/kfd ✗ · /dev/accel ✗ · /dev/apex_0 ✓ · /dev/hailo0 ✗ · /dev/bus/usb ✗ · nvidia ✗\n\nIf a device shows ✗ here but exists on the host, it is missing from your compose devices: list. blocked means the container sees the device but may not open it: it was mounted as a volume instead of listed under devices:, or the container runs as a Docker Swarm service. The detection plugins additionally log what they found (Available devices: …) and what each model actually runs on (Loaded model: … (AUTO -> CPU)), and show it under Active Hardware in their plugin settings."},{"page":"install/hardware-acceleration","title":"Hardware acceleration","heading":"Quick reference","anchor":"quick-reference","text":"| Hardware | Accelerates | Host driver | Pass to the container |\n|---|---|---|---|\n| Intel / AMD GPU | Video (VA-API/QSV) + OpenCL/Vulkan inference | in the kernel (i915/amdgpu) | /dev/dri |\n| NVIDIA GPU | Video (NVENC/NVDEC) + CUDA | NVIDIA driver + Container Toolkit | via the toolkit runtime |\n| Coral Edge TPU (PCIe/M.2) | Object detection | gasket/apex (coral command) | /dev/apex_0 |\n| Coral Edge TPU (USB) | Object detection | none | /dev/bus/usb |\n| Hailo-8 / Hailo-8L | Object detection | hailo_pci + firmware (hailo command) | /dev/hailo0 |\n| Intel NPU (Core Ultra) | OpenVINO inference | intel_vpu kernel module + firmware | /dev/accel (intel flavor) |\n| AMD ROCm | Compute | ROCm stack | /dev/kfd + /dev/dri |"},{"page":"install/hardware-acceleration","title":"Hardware acceleration","heading":"Intel / AMD GPU","anchor":"intel-amd-gpu","text":"The driver is part of the kernel, nothing to install on the host. Use the intel or amd flavor and pass /dev/dri:\n\n devices:\n - /dev/dri:/dev/dri\n\nOlder Intel iGPUs (Gen8 to Gen11, e.g. UHD 630)\nIntel's current OpenCL runtime only covers Gen12 and newer. The intel flavor therefore bundles Intel's legacy runtime alongside it, so OpenVINO and OpenCL inference also work on Gen8 to Gen11 iGPUs (Broadwell through Comet Lake). Only chips older than Gen8 (Haswell and earlier) have no OpenCL runtime at all: there inference falls back to CPU, and a Coral or Hailo is the way to fast detection. Video decoding is fully accelerated on all of them either way."},{"page":"install/hardware-acceleration","title":"Hardware acceleration","heading":"NVIDIA GPU","anchor":"nvidia-gpu","text":"Two host-side pieces: the NVIDIA driver (e.g. sudo ubuntu-drivers install) and the Container Toolkit, which injects the driver libraries into containers. sudo bash cameraui-host.sh nvidia installs the toolkit and registers it with Docker. Then use the nvidia flavor override from the Docker page. Setting NVIDIA_DRIVER_CAPABILITIES=all also enables the OpenCL and Vulkan paths the detection plugins can use.\n\nOutside Docker (bare-metal Linux, Windows), CUDA detection with the ONNX plugin additionally needs the CUDA 13 libraries and cuDNN 9 installed system-wide from NVIDIA. In Docker, the nvidia image ships them and needs an NVIDIA driver 580 or newer; the nvidia-cuda12 image keeps CUDA 12 for older drivers and pairs with the ONNX Legacy plugin. The nvidia-tensorrt image additionally ships the TensorRT runtime for the ONNX plugin's tensorrt provider, which roughly doubles detection throughput at the cost of about twice the VRAM; outside Docker, install TensorRT 10 yourself if you want it. The line between the two runs at Turing: GTX 16xx / RTX 20xx and newer use the ONNX plugin on the CUDA 13 images, GTX 900 and 10xx (Maxwell, Pascal, Volta) only work with the ONNX Legacy plugin on nvidia-cuda12, and RTX 50xx runs exclusively on the CUDA 13 side. Cards older than GTX 900 are not supported."},{"page":"install/hardware-acceleration","title":"Hardware acceleration","heading":"Coral Edge TPU","anchor":"coral-edge-tpu","text":"The runtime library (libedgetpu) is already in every image flavor. Only the host side differs by form factor:\n\n- PCIe / M.2. Needs the gasket/apex kernel driver. sudo bash cameraui-host.sh coral installs it (a maintained community build, since Google's original package no longer compiles on kernels ≥ 6.13). Then pass the device:\n\n devices:\n - /dev/apex_0:/dev/apex_0\n \n\n- USB. No driver needed, just pass the USB bus:\n\n devices:\n - /dev/bus/usb:/dev/bus/usb\n \n\nWithout a reachable TPU the Coral plugin runs on CPU and says so in its log. Check its Available devices: line if detection seems slow.\n\nTo see which device detection actually runs on, run the benchmark in Metrics → Cameras: its per-camera page names the runtime, the model and the device. See What detection costs."},{"page":"install/hardware-acceleration","title":"Hardware acceleration","heading":"Hailo-8 / Hailo-8L","anchor":"hailo-8-hailo-8l","text":"The kernel driver is built from source on the host: sudo bash cameraui-host.sh hailo installs build dependencies, compiles the driver, installs the firmware and udev rules. Reboot afterwards so the firmware loads, then pass the device:\n\n devices:\n - /dev/hailo0:/dev/hailo0\n\nThe userspace runtime ships inside the Hailo plugin."},{"page":"install/hardware-acceleration","title":"Hardware acceleration","heading":"Intel NPU (Core Ultra)","anchor":"intel-npu-core-ultra","text":"The intel flavor bundles the NPU user-space driver (Level Zero + the Intel NPU UMD and compiler); other flavors do not. On the host you need the intel_vpu kernel module (upstream since Linux 6.8) and its firmware; if /dev/accel is missing, see intel/linux-npu-driver. Then pass the device through:\n\n devices:\n - /dev/accel:/dev/accel\n\nThe OpenVINO plugin then lists NPU in its Available devices: log line and its device setting."},{"page":"install/","title":"Which installation?","heading":"Which installation?","anchor":"","text":"camera.ui has one server that does the work, and apps (desktop, mobile, browser) that connect to it. Which install method fits depends on the platform the server runs on."},{"page":"install/","title":"Which installation?","heading":"The quick answer","anchor":"the-quick-answer","text":"- macOS or Windows → the desktop app (recommended). Runs the server and the viewer together, fit for 24/7 operation.\n- Linux → Docker (or Proxmox), with GPU acceleration.\n- Phone only → the mobile apps. They still need a server running somewhere."},{"page":"install/","title":"Which installation?","heading":"Choose a method","anchor":"choose-a-method","text":"| Method | Runs on | Recommended for |\n|---|---|---|\n| Desktop app | macOS, Windows, Linux | macOS & Windows. The easiest way; server + viewer in one |\n| Docker | Linux (x86-64 / arm64) | Linux servers, mini-PCs, NAS, and homelabs, with GPU acceleration |\n| Proxmox | Proxmox VE | Homelabs already running Proxmox |\n| Linux (bare-metal) | Linux | A Linux box without Docker |\n| Mobile apps | iOS, Android | Watching on the go (connects to your server) |\n\nKnown-good hardware\nFor always-on recording, a small Intel N100 mini-PC (with Docker) or an Apple Silicon Mac (with the desktop app) are both well-suited as low-power servers. See System requirements."},{"page":"install/","title":"Which installation?","heading":"A few notes","anchor":"a-few-notes","text":"- One server is enough. Desktop app, mobile apps and browser all view the same server. From outside your network, use remote access.\n- The desktop app can also be just a viewer. Install it in Viewer mode to watch a server running elsewhere. See Desktop app."},{"page":"install/linux","title":"Linux (bare-metal)","heading":"Linux (bare-metal)","anchor":"","text":"For always-on servers without Docker: camera.ui installs from npm and runs as a systemd service. Ubuntu 24.04 (or newer) is the tested baseline. See System requirements."},{"page":"install/linux","title":"Linux (bare-metal)","heading":"Prerequisites","anchor":"prerequisites","text":"- 64-bit Linux (x64 or arm64) with systemd\n- Node.js 22.18 or newer, 24.22.1 or newer recommended, e.g. via NodeSource:\n\n curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash -\n sudo apt-get install -y nodejs\n \n\nFFmpeg, go2rtc and a portable Python runtime for the detection plugins are downloaded and managed by camera.ui."},{"page":"install/linux","title":"Linux (bare-metal)","heading":"Install","anchor":"install","text":"sudo npm install -g camera.ui\nsudo cameraui install --user cameraui\n\ncameraui install creates the service user if it doesn't exist, registers a systemd service named cameraui and starts it on boot. The first start downloads the server and takes a few minutes (sudo cameraui logs --user cameraui shows progress). The web UI then runs on https://:3443.\n\nIf the service user needs GPU access for hardware acceleration, add it to your distribution's video/render groups. --group video only applies when the install creates the user. For an existing user, run sudo usermod -aG video cameraui and restart the service."},{"page":"install/linux","title":"Linux (bare-metal)","heading":"Worker mode","anchor":"worker-mode","text":"To run this machine as a worker for another camera.ui server, add --worker to the install command:\n\nsudo cameraui install --user cameraui --worker\n\nThe flag is stored with the service, so the machine comes back up as a worker after a reboot. Generate a pairing code on the main server, put the snippet into this machine's config file as described on the workers page, then run sudo cameraui restart --user cameraui."},{"page":"install/linux","title":"Linux (bare-metal)","heading":"Managing the service","anchor":"managing-the-service","text":"cameraui status # service state\nsudo cameraui logs --user cameraui # tail the logs\nsudo cameraui restart --user cameraui # also: start / stop\nsudo cameraui update-server # update to the latest server release\nsudo cameraui update-server 1.2.3 # or pin a specific version\n\nThe service runs as its own user, so everything except status needs sudo and the same --user you installed with. If you installed with -H , pass that instead.\n\nThe Updates page does the same from the web interface; update-server is the CLI equivalent."},{"page":"install/linux","title":"Linux (bare-metal)","heading":"Updating Node.js","anchor":"updating-nodejs","text":"Below Node.js 22.18 the server keeps running, logs a warning and holds back server updates. Run the NodeSource commands from Prerequisites again, then restart the service (sudo cameraui restart --user cameraui). The first start on a new Node.js major version reinstalls the server for it, which takes a few minutes. A worker needs the same on its own machine."},{"page":"install/linux","title":"Linux (bare-metal)","heading":"Updating the launcher","anchor":"updating-the-launcher","text":"The camera.ui package from npm is the launcher: it writes the service, starts the server and rolls back a server update that does not start. The Updates page does not update it. Do that on each machine:\n\nsudo npm install -g camera.ui\nsudo cameraui reinstall\n\nreinstall writes the service again with the current launcher and keeps the user, data directory and worker setting of the installed service."},{"page":"install/linux","title":"Linux (bare-metal)","heading":"Data","anchor":"data","text":"Everything lives in the service user's home under ~/.camera.ui (config, database, recordings, TLS certs). That's the directory to back up. A different location can be set with -H during install."},{"page":"install/linux","title":"Linux (bare-metal)","heading":"Hardware acceleration","anchor":"hardware-acceleration","text":"Video acceleration is automatic, but on bare metal the host provides the drivers that the Docker images would otherwise bundle:\n\n- Intel GPU: sudo apt install intel-media-va-driver-non-free (VA-API/QSV). For OpenVINO or OpenCL inference on the GPU, also install the OpenCL runtime: sudo apt install ocl-icd-libopencl1 intel-opencl-icd\n- AMD GPU: sudo apt install mesa-va-drivers\n- NVIDIA: the regular driver (e.g. sudo ubuntu-drivers install); the Container Toolkit is Docker-only, you don't need it here. CUDA detection with the ONNX plugin additionally needs the CUDA 13 libraries and cuDNN 9 on the system (from NVIDIA's apt repo: sudo apt install cuda-libraries-13-2 cudnn9-cuda-13); the ONNX Legacy plugin wants CUDA 12 instead. The Docker images ship these, bare metal does not\n- Coral Edge TPU: the libedgetpu runtime plus (for PCIe/M.2) the gasket kernel driver, see the Coral section\n- Hailo: the kernel driver, see the Hailo section\n\nNo device passthrough is needed. The detection plugins log what they found (Available devices: …)."},{"page":"install/linux","title":"Linux (bare-metal)","heading":"Uninstall","anchor":"uninstall","text":"sudo cameraui uninstall # removes the systemd service\nsudo npm remove -g camera.ui\n\nThe data directory (~/.camera.ui) is left untouched."},{"page":"install/mobile","title":"Mobile apps","heading":"Mobile apps","anchor":"","text":"The iOS and Android apps are viewers for live view, recordings and notifications. They connect to a server running elsewhere (the desktop app or a server install)."},{"page":"install/mobile","title":"Mobile apps","heading":"Download","anchor":"download","text":"- iOS / iPadOS. Currently in beta. Join through TestFlight: install Apple's TestFlight app, then open the invite link on your device.[^appstore]\n- Android. Currently in beta. Join the beta group with the Google account you use in the Play Store, become a tester, then install from Google Play.[^androidbeta]\n\n| Platform | Requirement |\n|---|---|\n| iOS / iPadOS | 17.4 or newer |\n| Android | 8.0 or newer (API 26) |"},{"page":"install/mobile","title":"Mobile apps","heading":"Connecting to your server","anchor":"connecting-to-your-server","text":"Sign in with your camera.ui account (Google, Apple, or email) and your paired servers appear. The app picks the route itself:\n\n- At home. On the server's network it connects directly, so live view starts faster. It trusts the server's certificate for local addresses automatically.\n- Away. Off the network, it goes through camera.ui Cloud.\n\nOn the server side this needs:\n\n- Remote access via camera.ui Cloud enabled.\n- The server paired to your account."},{"page":"install/mobile","title":"Mobile apps","heading":"Notifications","anchor":"notifications","text":"A signed-in app receives push notifications for events. What you get alerted about is set under Notifications.[^license]"},{"page":"install/mobile","title":"Mobile apps","heading":"Biometric login","anchor":"biometric-login","text":"Settings → Account → Biometric login signs you in with Face ID or Touch ID instead of your password. The credentials are kept in the device's secure storage; sign out and back in once after enabling it so they get stored. Turning it off removes them."},{"page":"install/mobile","title":"Mobile apps","heading":"App lock","anchor":"app-lock","text":"App lock with Face ID / Fingerprint in the app's settings asks for biometrics (or the device passcode as fallback) at every start, and again after a few minutes in the background. A brief switch away doesn't re-prompt. It is independent of biometric login."},{"page":"install/mobile","title":"Mobile apps","heading":"Updates","anchor":"updates","text":"The apps update through the App Store and Google Play (during the beta: TestFlight on iOS, the Play Store testing track on Android), plus over-the-air updates to the interface. See Updating.\n\nIf the server or its NVR plugin is older than the app needs, the app shows Update recommended with the version it needs and the one installed. Update server and plugin on the Updates page. Got it hides the notice for these versions.\n\n[^license]: An active camera.ui subscription covers recording (NVR) and the features built on it, such as playback, export, face recognition, semantic search, and AI descriptions, plus push notifications. Live view and real-time detection are free.\n[^appstore]: The app moves to the App Store with the public release.\n[^androidbeta]: Google requires testers to stay enrolled for at least 14 days before the app can go public, so please stay opted in. For beta builds, turn on Beta updates under Settings → System in the app."},{"page":"install/proxmox","title":"Proxmox","heading":"Proxmox","anchor":"","text":"camera.ui runs on Proxmox VE inside an LXC container that hosts Docker. One command on the Proxmox host sets up everything: the container, Docker, and camera.ui.\n\nWhy an LXC and not a VM?\nAn LXC can share the iGPU: /dev/dri stays available to the host and other containers at the same time, while a VM with PCIe passthrough monopolizes the GPU, and an LXC has less overhead. A VM is the better choice for NVIDIA cards (see below)."},{"page":"install/proxmox","title":"Proxmox","heading":"Quick start","anchor":"quick-start","text":"Run this on the Proxmox host (as root):\n\ncurl -fsSLO https://raw.githubusercontent.com/cameraui/docker/main/proxmox/install-cameraui-lxc.sh\nbash install-cameraui-lxc.sh\n\nThe script downloads the Ubuntu 24.04 LXC template, creates an unprivileged container with nesting (privileged containers can no longer load Docker's AppArmor profile on current Proxmox releases), installs Docker inside it, deploys camera.ui via Docker Compose and prints the URL of the web UI. Tested with Proxmox VE 9.\n\nEnvironment variables:\n\n| Variable | Default | Meaning |\n|---|---|---|\n| CTID | next free ID | container ID |\n| CT_HOSTNAME | cameraui | container hostname |\n| CORES / RAM_MB / DISK_GB | 4 / 4096 / 32 | container resources |\n| BRIDGE | vmbr0 | network bridge (container gets its IP via DHCP) |\n| STORAGE | auto-detect | rootfs storage (prefers local-lvm, local-zfs, local, otherwise the first active storage that can hold a container rootfs) |\n| TEMPLATE_STORAGE | local | storage the LXC template is downloaded to (needs content type \"Container template\") |\n| FLAVOR | cpu | cpu, intel or amd (picks the image flavor); nvidia is experimental |\n| GPU_PASSTHROUGH | 1 when flavor ≠ cpu | pass /dev/dri into the container |\n| TZ | host timezone | timezone inside the container |\n\nExample for an Intel machine: FLAVOR=intel bash install-cameraui-lxc.sh"},{"page":"install/proxmox","title":"Proxmox","heading":"Getting a shell in the container","anchor":"getting-a-shell-in-the-container","text":"The script creates the container without a root password, so the Proxmox console and SSH won't let you in. From the host shell:\n\npct enter \n\nSingle commands:\n\npct exec -- docker logs cameraui\n\nConsole and SSH work once you set a password:\n\npct exec -- passwd"},{"page":"install/proxmox","title":"Proxmox","heading":"Hardware acceleration","anchor":"hardware-acceleration","text":"- Intel / AMD iGPU. Set FLAVOR=intel (or amd). The script passes the GPU render node through the container into Docker, nothing else to configure. Details: Hardware acceleration.\n- NVIDIA. Use a VM with PCIe passthrough instead of an LXC. Inside it, follow the standard Docker setup with the nvidia flavor. An LXC would need the host driver and the container's user-space libraries in permanent version lockstep. The VM does monopolize the card. If other services on the host need the same GPU (Ollama, Jellyfin), see NVIDIA in an LXC below.\n- AI accelerators (Coral, Hailo). Install the host driver on the Proxmox host, then pass the device node (/dev/apex_0, /dev/hailo0) into the container."},{"page":"install/proxmox","title":"Proxmox","heading":"NVIDIA in an LXC (experimental)","anchor":"nvidia-in-an-lxc-experimental","text":"FLAVOR=nvidia shares the card between the container and other services on the host, which a VM cannot do. The catch is a hard version coupling: the NVIDIA user-space libraries inside the container must exactly match the host's kernel driver, after every driver update.\n\nIt needs a working NVIDIA driver on the Proxmox host first (.run installer with --dkms plus pve-headers; nvidia-smi on the host must work), and the installed driver version must exist on download.nvidia.com. Then:\n\nFLAVOR=nvidia bash install-cameraui-lxc.sh\n\nThe script passes all /dev/nvidia* device nodes into the container, installs the matching user-space driver (--no-kernel-module) plus the NVIDIA Container Toolkit (with no-cgroups=true, because an unprivileged container cannot manage device cgroups), and registers a boot-time sync service that re-installs the matching user space after host driver updates. Verify with:\n\npct exec -- docker exec cameraui nvidia-smi\n\nExperimental\nThis path depends on NVIDIA's installer archive and on the host driver staying healthy; after a host driver update the container needs a reboot to re-sync. If it breaks, the VM route always works. Reports and fixes are welcome in the docker repo."},{"page":"install/proxmox","title":"Proxmox","heading":"Recordings on a dedicated disk or NAS","anchor":"recordings-on-a-dedicated-disk-or-nas","text":"Recordings default to the container's own volume. For a bigger disk, NAS share or merged pool, hand that storage into the container and set the NVR's storage path to it.\n\nUnprivileged containers cannot mount NFS/CIFS shares themselves. Mount the share on the Proxmox host and hand it into the container as a mount point:\n\npct set -mp0 /mnt/nas/recordings,mp=/mnt/recordings\n\nThen set the NVR Storage Path to /mnt/recordings (Settings → Recordings → Storage), or see the storage section of the Docker page.\n\nFUSE mounts (mergerfs, rclone) need extra care\nA FUSE mount (such as a mergerfs pool merging several drives) does not propagate into an (unprivileged) container the way a normal filesystem does. If the FUSE mount isn't active on the host before the container starts, the container sees the empty mountpoint on the host's root disk instead of the pool. Recordings then land on the small OS disk and get rotated away within minutes.\n\nMount the pool on the host before the container starts (e.g. via an fstab entry or a systemd unit ordered Before=pve-container@.service), and make sure the mount is shared into the container. Then always verify from inside the container."},{"page":"install/proxmox","title":"Proxmox","heading":"Verify your storage size","anchor":"verify-your-storage-size","text":"After setting a custom storage path, check that the container sees the full volume:\n\npct exec -- df -h /mnt/recordings\n\nThe reported size must match your real disk or pool. The small container root size means the mount didn't propagate. The NVR also logs its resolved storage and size on every start, and shows a warning in Settings → Recordings when the volume is unexpectedly small."},{"page":"install/proxmox","title":"Proxmox","heading":"Verify","anchor":"verify","text":"On every start the container logs which devices actually arrived. See Verify from the container. The camera.ui UI is available at https://:3443 (the script prints the IP at the end)."},{"page":"install/proxmox","title":"Proxmox","heading":"Updating","anchor":"updating","text":"The server updates from the Updates page. The image (OS, GPU libraries, launcher) updates inside the container:\n\npct exec -- sh -c 'cd /opt/cameraui && docker compose pull && docker compose up -d'\n\nOlder versions of the script wrote /opt/cameraui/docker-compose.yml without stop_grace_period. If yours lacks it, add stop_grace_period: 30s to the cameraui service once, as in the Docker quick start, so Docker gives camera.ui time to shut down."},{"page":"install/tv","title":"Apple TV","heading":"Apple TV","anchor":"","text":"The Apple TV app puts your cameras on the big screen: a live wall, your events with playback, and your sensors.[^license] It connects to a server running elsewhere (the desktop app or a server install)."},{"page":"install/tv","title":"Apple TV","heading":"Download","anchor":"download","text":"Currently in beta. Join through TestFlight: install Apple's TestFlight app on your Apple TV, then open the invite.[^appstore]\n\n| Platform | Requirement |\n|---|---|\n| Apple TV | tvOS 17 or newer |"},{"page":"install/tv","title":"Apple TV","heading":"Connecting to your server","anchor":"connecting-to-your-server","text":"The app connects straight to your server, not through camera.ui Cloud.\n\nOn the same network the app lists your server by itself. You can also add one by address:\n\n- On your network. Enter the server's local address, for example https://192.168.1.20:3443.\n- From anywhere. Enter your own public address, set up with a custom domain and a reverse proxy.\n\nSign in with your username and password, plus a two-factor code if you use one, or paste an API token.[^token]\n\nThe app verifies the server's certificate. From the first sign-in on it accepts only the certificate it saw then, others signed by the server's own certificate authority, and publicly trusted ones. For any other certificate the status at the top shows Certificate changed: choose Sign out under Settings and add the server again to accept the new one.\n\nWhen the server no longer accepts the sign-in, after a password change for example, the status at the top shows Signed out. Choose Sign out under Settings and add the server again. The same applies after a backup restore as a copy, and after a move when the server's address changed.\n\nYou can add several servers and switch between them under Settings → Switch server. With a single server the app opens it right away. With several it starts on the server picker, unless Open at launch in one server's settings is set to This server."},{"page":"install/tv","title":"Apple TV","heading":"Around the app","anchor":"around-the-app","text":"- Home. The latest activity up top, plus camera and storage status and today's event count. Selecting a notification opens its recording. Recent activity follows the Recent Events filter of your account.\n- Camview. A live wall of your cameras. Grid or mixed layout, rearrangeable tiles, fullscreen per camera. Fill wall drops the borders and fills the screen, Whole picture letterboxes each camera in its tile instead of cropping it. Both are remembered per view.\n- Events. Your events grouped by day, with filters for persons, faces, vehicles, animals, doorbell, sensors, sounds, and motion. Selecting one plays it back from that moment.\n- Episodes. Episodes show up as mosaic cards among your events and on the Home screen. Opening one plays the whole story and switches cameras by itself. Left and right jump between cameras.\n- Talk back. In the fullscreen player, the microphone button speaks through the camera, if the camera supports two-way audio.\n- PTZ. A camera that can pan, tilt or zoom gets a move button in the player. Hold a direction to keep moving, a short press nudges. Home position and presets appear when the camera has them. Admin accounts only. See PTZ & autotrack.\n\nPlayback follows each camera's playback source, episodes included. On Auto a gap in the high recording plays the next quality that has footage, the same as in the web interface."},{"page":"install/tv","title":"Apple TV","heading":"Sensors","anchor":"sensors","text":"The Sensors page lays out every sensor as a tile, grouped by type, with its current state.[^sensors] Switching them takes an admin account, other accounts see the states only.\n\n- Click to toggle. Lights, switches, sirens, locks and garages flip on select.\n- Hold for more. Holding select, or pressing play/pause, opens brightness, volume, alarm modes, or lock and garage controls.\n- Live. Sensors and states update as they change on the server."},{"page":"install/tv","title":"Apple TV","heading":"Updates","anchor":"updates","text":"During the beta the app updates through TestFlight.\n\nIf the server or its recording plugin is older than the app needs, the app shows an Update needed screen naming what to update. Update it on the server, then reopen the app.\n\n[^license]: An active camera.ui subscription covers recording (NVR) and the features built on it, such as playback. Live view is free.\n[^appstore]: The app moves to the App Store with the public release.\n[^token]: Create an API token in camera.ui under Settings, Account, API tokens.\n[^sensors]: These are the same sensors as the web Sensors page. Detection sensors tied to a single camera stay hidden here."},{"page":"install/updating","title":"Updating","heading":"Updating","anchor":"","text":"camera.ui has two parts that update: the server and the app you installed it with. Both are handled on the Updates page, whichever way you installed."},{"page":"install/updating","title":"Updating","heading":"The Updates page","anchor":"the-updates-page","text":"Updates in the navigation collects everything that can be updated, in three sections: Server, Plugins and Workers. A row with a newer version opens that version's release notes, Check for updates looks again. Admins only.\n\nUpdate all installs workers first, then plugins, and the server last, because installing the server restarts it. If a worker fails, the server is skipped, since a worker left on the old version cannot connect to the new one, and the plugins stopped for the server step start again. A failed item can be retried on its own. Only one run happens at a time, so the button waits while a plugin or worker update you started by hand is running.\n\nBefore the server installs a new version, from Update all or on its own, camera.ui checks the workers. A worker that would no longer connect afterwards, because it is offline or cannot be updated from here, is named in a dialog, and Update anyway goes ahead without it.\n\nA row can also say why it cannot be updated from here. Updates with the desktop app means an app too old to install its own updates remotely, so update it on its machine. No launcher, update it on its own machine means a worker that runs without the camera.ui service, which is what installs new versions. Update it once on its own machine means a worker on a release too old to take updates from the master. Requires Node.js … means the new version needs a newer Node.js than this machine runs, see Updating Node.js.\n\ncamera.ui checks for new versions every 10 minutes and notifies you when the server, a plugin or a worker has one."},{"page":"install/updating","title":"Updating","heading":"Updating the server","anchor":"updating-the-server","text":"How the server updates depends on the install method:\n\n- Docker or bare-metal. The Updates page installs the new version and restarts the server. Pulling a new Docker image does not update the server (see Updating the Docker image). On bare metal the launcher is updated by hand, see Updating the launcher.\n- Desktop app. The app and the server move together, so the Server row is the app itself. See Updating the desktop app.\n\nWith Docker or bare metal, a server update needs about 2 GB free on the disk with camera.ui's data, on workers too. With less it does not start, and the message says how much is free.\n\nA pre-release notification is titled Beta update."},{"page":"install/updating","title":"Updating","heading":"Beta updates","anchor":"beta-updates","text":"With Beta updates on in Settings → System, the server offers pre-release versions, and Manage on that page lists them to install a specific one. The switch reaches everything that follows this server: the desktop app moves to the beta feed, a server on a beta pulls its desktop workers onto the matching beta, and on the mobile app it also turns on beta over-the-air updates of the interface. Pre-releases can be less stable. Once you are on a pre-release, the server keeps offering the next beta even if you turn the switch back off. Install a stable version from Manage to leave the beta track."},{"page":"install/updating","title":"Updating","heading":"Beta versions of plugins","anchor":"beta-versions-of-plugins","text":"Plugins have their own switch, and the server's Beta updates does not reach them. Beta versions in the gear menu next to the search on the Plugins page adds pre-releases to each plugin's version picker and offers them as updates. Turned off, an installed beta stays until you pick another version. See Plugins."},{"page":"install/updating","title":"Updating","heading":"Updating the desktop app","anchor":"updating-the-desktop-app","text":"The desktop app checks for updates at startup and every 10 minutes while it runs. At startup it offers a found update on its start screen for 30 seconds, then starts on the current version. A found update also appears as the Server row on the Updates page: installing it downloads the update and relaunches the app, also from another device. The app never installs an update without a click.\n\nOne exception on Linux: only the .AppImage updates itself. The .deb is updated by installing the new .deb from the download page."},{"page":"install/updating","title":"Updating","heading":"Updating the Docker image","anchor":"updating-the-docker-image","text":"The server and the Docker image update separately. The launcher installs the server into the volume on first start and keeps that version across every later pull.\n\nTo update the image (base OS, GPU libraries, launcher):\n\ndocker compose pull\ndocker compose up -d\n\nTo update the server, use the Updates page, or run cameraui update-server -H /data in the container and restart it. The -H /data matters: without it the update lands outside the data volume and the restart boots the old version again."},{"page":"install/updating","title":"Updating","heading":"Updating the mobile apps","anchor":"updating-the-mobile-apps","text":"The mobile apps update through the App Store and Google Play (during the beta: TestFlight on iOS, the Play Store testing track on Android), and also receive over-the-air updates to the interface."},{"page":"intro/architecture","title":"How it works","heading":"How it works","anchor":"","text":"camera.ui is server-centric: one server, running on your hardware, does all the work. It connects to your cameras, streams and records video, runs detection, and serves the interface. The browser and the mobile apps are viewers of that server. The desktop app can be a viewer too, or run the server itself."},{"page":"intro/architecture","title":"How it works","heading":"One self-contained server","anchor":"one-self-contained-server","text":"The camera.ui server bundles everything it needs to run: the web interface, a built-in streaming engine (our own go2rtc build) for live video, and its own video processing for decoding and recording. Your settings, cameras, users, and events live in a small local database on the same machine.\n\nNothing leaves your network unless you turn on remote access. There is no external database, and no cloud account is required to run a server."},{"page":"intro/architecture","title":"How it works","heading":"The big picture","anchor":"the-big-picture","text":"flowchart LR\n cam[CamerasRTSP · ONVIF]\n\n subgraph server[camera.ui server · your hardware]\n direction TB\n g[Streaming engine] --> fw[Frame workerone per camera]\n fw --> det[Detectionmotion → objects → faces · platesaudio in parallel]\n det --> nvr[(NVR pluginrecordings + push)]\n g --> api[Web UI + API]\n nvr --> api\n end\n\n cam --> g\n api --> viewers[Viewersweb · desktop · mobile]\n api -. optional .-> remote[Remote accessCloud · tunnel]\n remote -. from anywhere .-> viewers"},{"page":"intro/architecture","title":"How it works","heading":"From camera to notification","anchor":"from-camera-to-notification","text":"1. Streaming. The streaming engine connects to each camera and turns its feed into browser-friendly live video (WebRTC and MSE). Streams are kept \"warm\" so live view and snapshots load instantly.\n2. Per-camera analysis. Every camera gets its own frame worker, a background process that decodes the video and runs detection. A problem with one camera does not affect the others.\n3. Layered detection. Cheap motion detection runs first and only wakes the heavier AI on movement: object detection, then faces, license plates, classification, and semantic (CLIP) analysis. Audio is analyzed in parallel. This \"cascade\" keeps CPU and GPU use low.[^detect]\n4. Events & recording. When detection fires, the server builds an event with segments, thumbnails, and the objects, faces, or plates it found. The NVR plugin records the footage, stores it, and serves it back for playback.[^license]\n5. Notifications. Events can trigger push notifications and run automations.[^license]"},{"page":"intro/architecture","title":"How it works","heading":"Plugins make it extensible","anchor":"plugins-make-it-extensible","text":"Much of camera.ui is delivered by plugins from the in-app store. Each plugin runs in its own process, so a failing plugin can't take down the server, and it restarts automatically after a crash.\n\nPlugins provide:\n\n- Camera sources. ONVIF and other camera protocols.\n- Detectors. Motion engines and the AI backends (CoreML, ONNX, OpenVINO, NCNN, Coral, Hailo).\n- Recording & notifications. The NVR plugin records your footage and sends push notifications.\n- Smart-home bridges. Apple HomeKit.\n\nLearn more under Plugins."},{"page":"intro/architecture","title":"How it works","heading":"Apps: desktop, mobile, web","anchor":"apps-desktop-mobile-web","text":"Every app shows the same interface, but their roles differ:\n\n- The desktop app can be the server itself (all-in-one), a viewer that connects to another server, or a worker that helps another server with decoding, detection, or a plugin. You choose on first launch, and can switch anytime. See Scaling across machines.\n- The mobile apps and the browser are always viewers.\n\nHow viewers reach the server:\n\n- On your network, the browser and a desktop viewer connect directly.\n- The mobile apps connect directly over your local network at home, and through camera.ui Cloud when you're away.[^cloud-optional]\n- A browser away from home uses whichever remote access option you set up: camera.ui Cloud, a Cloudflare tunnel, or your own domain.\n\nYou can also save more than one server as an Instance and switch between them from the same app."},{"page":"intro/architecture","title":"How it works","heading":"Scaling across machines","anchor":"scaling-across-machines","text":"Extra machines can join as workers. A worker decodes some cameras, or runs an entire plugin (for a detector that needs hardware the main server lacks). Cameras and plugins assigned to a worker fall back to the main server while the worker is offline and move back once it reconnects. See Workers."},{"page":"intro/architecture","title":"How it works","heading":"Reaching it from outside","anchor":"reaching-it-from-outside","text":"From outside your network you can use camera.ui Cloud, a Cloudflare tunnel, a custom domain or port-forwarding. All are optional. See Remote access.\n\n[^detect]: Detection needs a detection plugin that matches your hardware (CoreML, ONNX, OpenVINO, NCNN, or an edge accelerator like Coral or Hailo). See Detection & AI.\n[^license]: An active camera.ui subscription covers recording (NVR) and the features built on it, such as playback, export, face recognition, semantic search, and AI descriptions, plus push notifications. Live view and real-time detection are free.\n[^cloud-optional]: camera.ui Cloud is optional. On your own network everything stays local, and your server never has to connect to the cloud. See Reaching it from outside."},{"page":"intro/getting-started","title":"Getting started","heading":"Getting started","anchor":"","text":"From a running server to your first camera. If the server isn't installed yet, start with Installation."},{"page":"intro/getting-started","title":"Getting started","heading":"1. Open camera.ui","anchor":"1-open-cameraui","text":"camera.ui runs over HTTPS. How you open it depends on how you installed it:\n\n- Desktop app. It opens automatically when you launch it (in Server mode it starts the server for you).\n- Server (Docker, Proxmox, bare-metal). Open https://:3443 in your browser, or https://localhost:3443 on the same machine.\n\nFirst-visit certificate warning\ncamera.ui uses a self-signed certificate, so the browser warns on the first visit. Continue with Advanced → Proceed. To get rid of the warning, install the certificate from Settings → System → Certificate."},{"page":"intro/getting-started","title":"Getting started","heading":"2. Sign in","anchor":"2-sign-in","text":"The first time, sign in with the default account:\n\n- Username: admin\n- Password: admin\n\nYou'll replace these in the next step."},{"page":"intro/getting-started","title":"Getting started","heading":"3. First-run setup","anchor":"3-first-run-setup","text":"The first sign-in opens a setup wizard:\n\nWelcome → Appearance → Account → Finish"},{"page":"intro/getting-started","title":"Getting started","heading":"Welcome","anchor":"welcome","text":"- New Installation. Set up a fresh server.\n- Restore from Backup. Upload a backup archive from another camera.ui install. camera.ui shows what it holds and restores it after you confirm, see Restoring.\n- From Another Server. Enter the move code of the old server and fetch its backup directly, see Moving to another server."},{"page":"intro/getting-started","title":"Getting started","heading":"Appearance","anchor":"appearance","text":"Theme (light, dark, or follow the system) and language."},{"page":"intro/getting-started","title":"Getting started","heading":"Account","anchor":"account","text":"Your own username, password and optional avatar. This account replaces the default admin / admin.\n\nChoose a strong password\nThis is an administrator account. Use a strong, unique password, especially if you plan to reach camera.ui from outside your network."},{"page":"intro/getting-started","title":"Getting started","heading":"Finish","anchor":"finish","text":"Enjoy! saves and signs you out. Sign back in with the username and password you just set."},{"page":"intro/getting-started","title":"Getting started","heading":"4. Add your first camera","anchor":"4-add-your-first-camera","text":"After signing in you land on the Home screen. Select Get started, or open Cameras.\n\nThe Cameras page scans your network and lists the cameras it finds under Discovered. Select one, enter its username and password, and it's added. If your camera isn't found, use Add camera to enter its stream details by hand.\n\nFor the full walkthrough, see Adding a camera."},{"page":"intro/getting-started","title":"Getting started","heading":"Getting around","anchor":"getting-around","text":"The sidebar on the left is grouped by purpose. Your daily pages sit at the top. A Manage group holds cameras, sensors, faces, automations, plugins and instances. A System group keeps the diagnostic pages and starts collapsed. On a phone the same groups show up in the menu.\n\nThe pencil at the top of the sidebar starts edit mode: drag pages to another spot or group, hide settings pages with the eye, and confirm with the check. Reset to defaults brings back the original layout. The layout is saved to your account and follows you to every browser and device.\n\nIn Settings → Appearance you can also add the settings pages to the sidebar as their own group, and choose which groups can collapse."},{"page":"intro/getting-started","title":"Getting started","heading":"Next steps","anchor":"next-steps","text":"- Live view — watch your cameras live\n- Recording (NVR) — turn on 24/7 or event recording\n- Detection & AI — add motion and object detection\n- Remote access and the mobile apps — reach your cameras from anywhere"},{"page":"intro/quick-start","title":"Quick start","heading":"Quick start","anchor":"","text":"The short path from install to a camera that detects and records. Each step links to the page with the details."},{"page":"intro/quick-start","title":"Quick start","heading":"1. Install","anchor":"1-install","text":"- macOS or Windows. The desktop app.\n- Linux. Docker: save the compose file from that page and run docker compose up -d. Proxmox users take the Proxmox script.\n\nThe desktop app opens camera.ui by itself. Otherwise open https://:3443. The certificate is self-signed, so the browser warns on the first visit."},{"page":"intro/quick-start","title":"Quick start","heading":"2. Sign in","anchor":"2-sign-in","text":"Sign in with admin / admin. The setup wizard asks for theme and language, then for your own username and password, which replace the default account. Sign in again with the new account. See Getting started."},{"page":"intro/quick-start","title":"Quick start","heading":"3. Add a camera","anchor":"3-add-a-camera","text":"Open Cameras. Cameras found on your network show up under Discovered: select one, enter its username and password, confirm. Discovery needs ONVIF turned on in the camera. A camera that isn't found is added with Add camera and its RTSP URL. Without an RTSP URL, a brand plugin from the store brings the camera in, Reolink for example:[^cloud] install it, set it up on its page, and its cameras appear under Discovered. See Adding a camera."},{"page":"intro/quick-start","title":"Quick start","heading":"4. Install the detection plugins","anchor":"4-install-the-detection-plugins","text":"The NVR plugin for recording is already installed. Under Plugins → Search plugins add:\n\n- A motion engine. Rust Motion fits most setups.\n- An AI backend for your hardware: CoreML on Apple Silicon, OpenVINO on Intel, ONNX on everything else.\n\nSee Object detection & AI backends for the other options."},{"page":"intro/quick-start","title":"Quick start","heading":"5. Link the server to your account","anchor":"5-link-the-server-to-your-account","text":"Recording and the mobile apps run on a camera.ui account, created at cloud.cameraui.com.\n\n1. Settings → Remote Access → Register server, then sign in. The same screen pairs the mobile apps.\n2. Settings → Recordings, card License & Cloud: click the cloud icon and sign in. The license activates on its own.\n\nSee Turning on recording."},{"page":"intro/quick-start","title":"Quick start","heading":"6. Turn on detection","anchor":"6-turn-on-detection","text":"In the camera's settings, open the Plugins tab and the Detection category. Set Motion to Rust Motion and Object to your AI backend. Detection starts right away, sensitivity and confidence are under Detection in the same settings. See Set up sensors."},{"page":"intro/quick-start","title":"Quick start","heading":"7. Check recording","anchor":"7-check-recording","text":"With an active license every camera records around the clock in all stream qualities, as long as your plan has a free camera slot. To record only events, switch the camera to Event in its recording settings. See Recording modes."},{"page":"intro/quick-start","title":"Quick start","heading":"Next steps","anchor":"next-steps","text":"- Live view — watch your cameras\n- Notifications — pushes for what your cameras see\n- Zones & privacy — limit where detection counts\n- Remote access — reach camera.ui from outside your network\n\n[^cloud]: There are also plugins for cameras that depend on a vendor cloud, such as Eufy, Tuya, Wyze or Ring. They work, but these cameras are not recommended and not covered by support. Use local RTSP / ONVIF cameras for a reliable experience."},{"page":"intro/requirements","title":"System requirements","heading":"System requirements","anchor":"","text":"What camera.ui needs depends on how you run it and what you ask it to do. Live-viewing a couple of cameras is light; recording many cameras around the clock with AI detection needs real hardware."},{"page":"intro/requirements","title":"System requirements","heading":"At a glance","anchor":"at-a-glance","text":"| | Minimum | Recommended |\n|---|---|---|\n| CPU | 4 modern cores | Apple Silicon (M1+), Intel N100, or better |\n| RAM | 8 GB | 16 GB |\n| Hardware acceleration | none (software) | a recent GPU / accelerator, or Apple Silicon |\n| Disk (app) | ~16 GB SSD | SSD |\n| Recordings | a separate, dedicated disk, sized to your cameras and retention | |\n\nKnown-good hardware\nA small Intel N100 mini-PC or an Apple Silicon Mac (M1 or newer) comfortably runs a typical setup (several cameras with recording and AI detection) at low power.\npushes you higher: more cameras, higher resolutions, continuous recording, and especially AI detection, which is far lighter with a supported GPU, accelerator, or Apple Silicon (see below)."},{"page":"intro/requirements","title":"System requirements","heading":"Running the server","anchor":"running-the-server","text":"You can run the camera.ui server on Linux, macOS, or Windows:\n\n- Linux. In Docker, on Proxmox, or bare-metal. Ubuntu 24.04 (or newer) is the tested baseline. A bare-metal install only needs Node.js 22.18 or newer, since camera.ui manages everything else (FFmpeg, go2rtc, a portable Python runtime) itself; the Docker images bundle everything.\n- macOS. Through the desktop app in Server mode. Apple Silicon Macs (M1 and newer) run it at low power with full hardware acceleration, including the Neural Engine.\n- Windows. Through the desktop app in Server mode."},{"page":"intro/requirements","title":"System requirements","heading":"Hardware acceleration","anchor":"hardware-acceleration","text":"Detection and video processing run in software on any CPU, but a GPU, accelerator, or Apple Silicon lowers CPU use significantly.\n\n| Platform | Acceleration | Notes |\n|---|---|---|\n| Apple Silicon | GPU + Neural Engine + VideoToolbox | macOS, via the desktop app |\n| Windows | Native GPU video acceleration | desktop app · x86-64 |\n| Intel | Quick Sync / VA-API (+ OpenCL) | intel Docker flavor · x86-64 |\n| NVIDIA | NVENC / NVDEC + CUDA | nvidia Docker flavor · x86-64 |\n| AMD | Mesa VA-API (+ OpenCL) | amd Docker flavor · x86-64 |\n| CPU only | software | cpu flavor · x86-64 + arm64 |\n| Coral Edge TPU | dedicated TPU | via the Coral plugin · Linux · x64 + arm64 |\n| Hailo-8 / Hailo-8L | dedicated NPU | via the Hailo plugin · Linux · x64 + arm64 |\n\nOn Linux, pick the Docker flavor that matches your GPU. On macOS and Windows, the desktop app uses your GPU for hardware-accelerated video automatically. Your AI detection plugin should match your hardware: CoreML on Apple Silicon, an Intel / NVIDIA backend, or a Coral / Hailo edge accelerator on Linux."},{"page":"intro/requirements","title":"System requirements","heading":"Storage for recordings","anchor":"storage-for-recordings","text":"How much space recordings need depends on your camera count, resolution and bitrate, recording mode, and how long you keep footage.\n\nUse a dedicated local disk for recordings, separate from the system disk and not a network share (NAS). Continuous recording writes constantly, and the recordings folder also holds the databases that index your events, which network shares cannot lock reliably. See Storage & retention.\n\ncamera.ui shows each camera's recording rate per day and lets you cap usage with the Quota and Retention settings."},{"page":"intro/requirements","title":"System requirements","heading":"Desktop app","anchor":"desktop-app","text":"The desktop app runs on macOS, Windows, and Linux. On macOS, version 15 or newer is recommended, since the operating system requires it for the app to reach cameras on your local network. In Server mode the desktop app runs the full server, so the requirements above apply; as a viewer it's lightweight."},{"page":"intro/requirements","title":"System requirements","heading":"Mobile apps","anchor":"mobile-apps","text":"The mobile apps are viewers with minimal requirements:\n\n- iOS 17.4 or newer\n- Android 8.0 or newer (API 26)"},{"page":"intro/requirements","title":"System requirements","heading":"Cameras","anchor":"cameras","text":"Almost any IP camera works: anything that provides an RTSP or ONVIF stream (H.264 or H.265). See Connection protocols for the full list.[^cloud]\n\n[^cloud]: Cameras that depend on a vendor cloud (such as Ring or Nest) can be connected, either directly or through a plugin, but they are not recommended and not covered by support. Use local RTSP / ONVIF cameras for a reliable experience."},{"page":"notifications/","title":"Notifications","heading":"Where alerts arrive","anchor":"where-alerts-arrive","text":"- In the app. Every alert lands in the in-app list (the bell), on every device.\n- Desktop app. While it's running, including in the tray, it shows native system notifications for the server it's connected to, except while its window is in front. No cloud or account needed.\n- Phone (push). The mobile app gets push even when it's closed, delivered end-to-end encrypted through camera.ui Cloud.[^license]"},{"page":"notifications/","title":"Notifications","heading":"Getting push on your phone","anchor":"getting-push-on-your-phone","text":"Phone push needs all of these. If one is missing, alerts only land in the in-app bell:\n\n1. The native app. Push only reaches the mobile app from the App Store or Play Store. A browser or the web app (PWA) shows the in-app bell, but never push.\n2. camera.ui Cloud. Push is delivered through camera.ui Cloud, so the server needs it enabled and paired to your account. It also needs an active subscription.\n3. On, and synced. In the app, open Settings → Notifications, turn the master switch on, and tap Sync this device once. Repeat on every phone, for every server you connect to.\n\nPush repairs itself after a reinstall. Reinstalling the app, clearing its data or a new phone resets the push token, and the app re-registers with the server in the background. If it can't (the phone's notification permission is off, say), a Push notifications stopped working note with a Fix button appears on any screen. Re-sync in Settings → Notifications does the same. The This device row only asks for a sync when the server really doesn't know this phone.\n\nMute from the push. Press and hold a camera push and pick Mute this camera for 1 hour or Mute all cameras for 1 hour. Both apply to this phone only, and alerts about camera.ui itself still come through."},{"page":"notifications/","title":"Notifications","heading":"Push stays private","anchor":"push-stays-private","text":"Your server seals every alert before it leaves your network. camera.ui Cloud, Google and Apple only pass the sealed envelope along; the camera name, the alert text and the link inside are unreadable to all three. The thumbnail takes no detour at all: your phone loads it straight from your server. When Video in Push is on, the clip is delivered the same way: it stays inside the sealed push and streams from your own server, so the cloud never sees a frame."},{"page":"notifications/","title":"Notifications","heading":"Your notification settings","anchor":"your-notification-settings","text":"Settings → Notifications belongs to your account and applies on every device you sign in on.[^device]\n\n- Master switch. All notifications on or off. Off for a new account, and the other sections only appear once it's on.\n- This device. On a phone, tap Sync this device once to start receiving push. In the desktop app, this switches native notifications on or off for that computer alone.\n- Quiet hours. Silence non-critical alerts during a daily window. Critical alerts still come through, and the in-app list stays current.\n- System notifications. Alerts about camera.ui itself: server and app updates, plugin updates, an update that failed, a plugin crashing or recovering, a worker with cameras or plugins that is offline for two minutes and comes back, remote access that is down for ten minutes and comes back,[^remote] and a scheduled backup that failed. Each one has its own switch.[^admin]\n- Plugin notifications. Alerts that plugins send, such as a camera going offline or storage filling up. Each plugin has its own switch. Tapping a camera offline or online alert opens that camera's log, where the reason for the outage is usually shown. The NVR's storage, license and cloud sign-in alerts open Settings → Recordings.\n- Devices. The phones and other targets your alerts go to. Rename one, switch Active off to pause it, or remove it to unregister it for good."},{"page":"notifications/","title":"Notifications","heading":"What each camera alerts you about","anchor":"what-each-camera-alerts-you-about","text":"Each camera has its own notification settings, in the camera's settings under Notifications:[^nvr]\n\n- Send notifications. The camera's own switch. Off means this camera never pushes, while its events still land on the timeline and in the app. On by default.\n- Which objects alert you. Set by the camera's alert zones, not here. Without an alert zone the camera alerts on everything it detects. Draw one to narrow it down by type and by area.\n- Which faces and plates alert you. Also set by the alert zones. A zone can name the people it pushes for, with entries for unknown faces and for anyone enrolled, and the same for plates. Nothing selected notifies for everyone.\n- Notify for sounds. Which sounds, such as alarms or glass breaking.\n- Notify for sensors. A doorbell, a contact sensor, a siren, or a security system.\n- Notification speed. How long to wait for a good picture. Immediate sends right away, with a picture only if one is ready. Balanced (the default) waits up to 2 seconds, Best up to 4. A recognized face always sends immediately, and the picture is updated quietly afterwards.\n- Cooldown (seconds). A quiet period after an alert. 30 seconds by default, up to 600. Doorbell and alarms bypass it.\n- Video in push. Attach a short clip of the moment to the push. On iPhone it plays right in the notification, on Android the expanded notification animates the clip frame by frame,[^androidframes] and the in-app alert opens with a player. Uses the lowest recorded quality. Off by default."},{"page":"notifications/","title":"Notifications","heading":"Pushes with an AI description","anchor":"pushes-with-an-ai-description","text":"With AI descriptions on, the alert carries what the AI saw instead of the bare \"person detected\".[^ai] This is Moment Pushes in the camera's settings, and the alert waits for the description. Doorbell rings and alarms still arrive immediately, and when the model does not answer in time the plain detection push goes out instead.\n\nPush Timing decides how often a busy event reaches you:\n\n- Per Moment. Each moment pushes as its description is ready. This is the default.\n- Per Event. The descriptions collect and one push goes out after the scene has been quiet for a while. Tapping it opens the beginning of the event.\n\nAn alert that is already on your lock screen is updated quietly when something better arrives, a recognized name or a sharper picture, without buzzing again."},{"page":"notifications/","title":"Notifications","heading":"Critical alerts","anchor":"critical-alerts","text":"Critical alerts, like a doorbell ring or an alarm, are delivered immediately and ignore both the cooldown and quiet hours.\n\n[^license]: Push notifications to the mobile apps require an active camera.ui subscription.\n[^device]: The This device row is the exception. It only affects the phone or computer you are on.\n[^admin]: Only admins see the system notifications section.\n[^remote]: camera.ui checks its connection to the camera.ui Cloud and its own Cloudflare tunnel. A custom domain behind your own proxy is not checked.\n[^nvr]: Sending the push is the NVR plugin's job, so a camera without one saves the settings and starts using them once a plugin is installed.\n[^ai]: AI descriptions come from the NVR plugin. Turn them on in its settings.\n[^androidframes]: Android notifications cannot play video, so the current app pulls frames from the clip and cycles them. Tapping still opens the recording."},{"page":"plugins/homekit","title":"HomeKit","heading":"HomeKit","anchor":"","text":"The HomeKit plugin adds your cameras and sensors to Apple Home. Cameras stream and record through the Home app across your Apple devices; sensors appear as accessories you can see and control there."},{"page":"plugins/homekit","title":"HomeKit","heading":"Setup","anchor":"setup","text":"1. Install and enable the HomeKit plugin.\n2. Assign it to the cameras you want in Home, on the plugin's Cameras tab or from a camera's Plugins tab.\n3. Open a camera's HomeKit settings. Each camera is added to Home individually and has its own QR code and PIN.\n4. In the Apple Home app, choose Add Accessory and scan the camera's QR code, or enter its PIN.\n\nTo pair on the device camera.ui is open on, choose More options in the Home app and type the PIN; the copy button next to it copies the PIN.\n\nHomeKit Secure Video recording works once you've set it up in Home."},{"page":"plugins/homekit","title":"HomeKit","heading":"Secure Video on iOS 27","anchor":"secure-video-on-ios-27","text":"Cameras with an HEVC (H.265) main stream use Apple's newer secure video services. Live view at home and away, recording and two-way audio all run on the camera's own HEVC stream without transcoding, so a 4K camera doesn't cost a CPU core. This needs iOS 27 or tvOS 27 on the devices you watch on and on the home hub.\n\nAway from home, a camera with a second source in Low Resolution or Mid Resolution also offers a small picture from it, transcoded on the server unless that source is HEVC already. The transcoded picture takes the aspect ratio set under Interface in the camera settings. The Home app switches to it on slow connections such as LTE. Without such a source, live view over LTE shows no picture.\n\nCameras with an H.264 main stream stay on the classic HomeKit camera services, because Apple's new remote view only takes HEVC. Any other codec, MJPEG for example, uses the classic services too, with the stream transcoded to H.264. The plugin picks the path from the main stream's codec when the camera is published.\n\nIf your home stays on iOS 26 or older, turn on Force legacy path under Secure Video in the camera's settings. The camera then uses the classic services and its HEVC stream is transcoded to H.264. As with MJPEG, the picture takes the width the Home app asks for, never more than the camera sends. Aspect ratio and frame rate stay the camera's, and the bitrate follows the picture size.\n\nOn the classic services, remote view starts small and the Home app asks for more once the connection holds. Until then, a camera with a source in Low Resolution or Mid Resolution sends that one, transcoded to H.264 when needed, and then moves to the main stream without a break. A transcoded small picture takes the aspect ratio set under Interface, as on the Secure Video path."},{"page":"plugins/homekit","title":"HomeKit","heading":"What ends up where","anchor":"what-ends-up-where","text":"A camera is always its own accessory, with a motion sensor on it. That motion sensor is fed by camera.ui's detection, so it works whether or not a motion sensor is assigned.\n\nWhat a sensor turns into depends on its camera assignment:\n\nAssigned to a camera, the sensor becomes part of that camera's accessory instead of a separate tile:\n\n- Motion feeds the camera's motion sensor and gets no accessory of its own.\n- Doorbell adds the doorbell button to the camera.\n- Battery adds the charge level and the low-battery warning.\n- Spotlight and siren become controls on the camera, as long as they are the camera's own hardware.\n\nNot assigned to a camera, the sensor becomes its own accessory behind the single camera.ui Bridge: contact, occupancy, smoke, leak, temperature, humidity, lock, garage door, switch and security system always land here, and so do motion sensors, doorbells, lights and sirens that belong to no camera. A standalone light or siren stays on the bridge even if you assign it to a camera; only hardware built into the camera moves onto it.\n\nThe bridge runs as soon as the plugin does, with or without sensors. Pair it once and every sensor you expose later joins automatically. Its QR code and PIN sit in the plugin's settings.\n\nWhether a sensor comes across at all is the Expose sensor toggle on the Sensors page. Camera hardware has no toggle, it always follows its camera."},{"page":"plugins/homekit","title":"HomeKit","heading":"Fast streaming","anchor":"fast-streaming","text":"Opening a camera in Home takes a few milliseconds, with H.264 or H.265, as long as the source is kept ready: enable hot mode and preload on the cameras you bridge.\n\nHot mode on the main stream also decides whether HomeKit Secure Video recordings include the seconds before an event. Without it the camera is not kept connected, a recording opens the stream when the event starts and has nothing from before. Plugins switch hot mode off for battery cameras, so they can sleep.\n\nThe picture in Home follows the camera's snapshot setting. While an event runs or the doorbell rings, HomeKit fetches a fresh one."},{"page":"plugins/homekit","title":"HomeKit","heading":"Settings","anchor":"settings","text":"Each camera's HomeKit settings include:\n\n- QR code and PIN. The pairing details for that camera.\n- Force legacy path. Under Secure Video: keeps the camera on the classic HomeKit services, see Secure Video on iOS 27. Off by default.\n- Allow transcoding for mobile data. Under Secure Video as well: lets camera.ui transcode a low or mid resolution source that is not HEVC, so remote view works over LTE. An HEVC source goes out as it is unless Always transcode is on. On by default; when off, such a camera may show no picture over LTE. The log says on each remote start which source goes out and whether it is transcoded.\n- Use Hardware Acceleration. Uses the GPU, if one is available, when a stream has to be transcoded. On by default.\n- Use Hardware Acceleration for HKSV. The same for HomeKit Secure Video recordings. On by default; turn it off for a camera whose recordings break up.\n- Always transcode. Under Advanced: transcodes live view even when HomeKit could play the camera's stream as it is, on the classic and the Secure Video path. Puts extra load on the server. Recordings have a switch of their own. Off by default.\n- Stream as requested. Under Advanced as well: sends live view in the size, frame rate and bitrate HomeKit asks for, so every live stream is transcoded. A source smaller than the request keeps its size. Off by default.\n- Always transcode for HKSV. Under Advanced as well: transcodes HomeKit Secure Video recordings even when HomeKit could take the camera's stream as it is. They are then H.264 in the size, frame rate and bitrate HomeKit asks for. Puts extra load on the server. Off by default.\n- Override Port and mDNS advertiser. Under Advanced: a fixed port instead of an automatic one (0), and the backend that announces the camera on the network.\n- Reset Pairing. Generate a new code and re-add the camera if pairing breaks.\n\nThe plugin's own settings hold the same set for the sensor bridge: its QR code, PIN, port, mDNS advertiser, and a Reset Bridge Pairing button."},{"page":"plugins/homekit","title":"HomeKit","heading":"Good to know","anchor":"good-to-know","text":"- Each camera is its own HomeKit accessory, so you add cameras one at a time. Standalone sensors share the one bridge and only need a single pairing.\n- Cameras that support two-way audio can be talked to from the Home app.\n- Cameras and the bridge listen on the Server addresses under Settings → Remote Access → Network, or for a plugin on a worker on the addresses set with Edit Worker (see Worker assignments). With none set they listen on all of them; if streams stutter or fail, pick the wired LAN address. An address that does not exist on the machine is skipped, and the log names it with the addresses it found.\n- The pairings are kept in the plugin's settings on the main server, so cameras and the bridge stay paired when the plugin moves to a worker or back.\n- A backup restored as a Copy (see Restoring) gives the bridge and the cameras new pairing codes, so they don't answer for the original server. Add them to Home again on the copy. A Move keeps the pairings."},{"page":"plugins/","title":"Plugins","heading":"Plugins","anchor":"","text":"Most of what camera.ui does beyond live view comes from plugins: the AI backends and motion engines behind detection, audio detection, smart-home bridges like HomeKit, and camera-brand integrations. Managing plugins is an admin task."},{"page":"plugins/","title":"Plugins","heading":"Two things, kept separate","anchor":"two-things-kept-separate","text":"- Installing a plugin adds it to your server, once. This page covers that.\n- Assigning a plugin to a camera decides which camera uses it, for example which motion engine a camera runs. That's done per camera, in Set up sensors."},{"page":"plugins/","title":"Plugins","heading":"Managing plugins","anchor":"managing-plugins","text":"Plugins lists everything installed, each card with a live status (ready, started, stopped, error). From a card you can:\n\n- Enable or disable it, and start, stop, or restart its process.\n- Update it: the update button starts right away, click it again while it runs for the live install output. For release notes or a specific version, use the card menu.\n- Open its settings, or view its logs.\n- Uninstall it, optionally removing its stored data.\n\nRecordings are the exception: if you delete a recording plugin's stored data, its footage is kept.\n\nUpdate all in the toolbar updates every outdated plugin. While an update run from the Updates page is going on, plugins are stopped and can't be managed here.\n\nThe gear button next to the search bar holds two settings, both off by default:\n\n- Beta versions. Shows pre-release versions in the version picker and offers beta releases as updates.\n- Allow plugin build scripts. Lets plugins run install scripts and native builds while installing or updating. Only turn this on for plugins you trust.\n\nThe button next to the plus switches to a compact table view with status, version and actions per row; your choice is remembered.\n\nInstalls, updates and uninstalls run through a server-side queue, two at a time. Every open browser sees the queue, whoever started it.\n\nEach plugin runs in its own process, so a crashing plugin doesn't take down the rest. A badge on the card shows its language (Node, Python or Go). A plugin built for a different plugin API doesn't start: its card says whether the plugin or camera.ui needs an update, and you get a notification."},{"page":"plugins/","title":"Plugins","heading":"Managing several at once","anchor":"managing-several-at-once","text":"The select button works in both views: update, enable, disable or uninstall several plugins in one step."},{"page":"plugins/","title":"Plugins","heading":"Installing a plugin","anchor":"installing-a-plugin","text":"Search plugins opens the store. The list comes from npm, so any published camera.ui plugin can appear; camera.ui curates on top of it:\n\n- Trust. Each plugin is marked Official (published by camera.ui), Verified (a community plugin the camera.ui team has reviewed), or Community (published by anyone, not reviewed). You can filter by trust level.\n- Safety. Plugins known to be malicious or compromised are blocked: they don't show up, can't be installed, and one you already have is disabled automatically if it's later blocked.\n- Compatibility. The store flags a plugin that needs a newer camera.ui, and installing a version that can't run on your server is refused with the reason.\n- Browsing. Filter by category, start from the featured plugins, and sort by name, weekly downloads, or last update.\n\nA specific version can be picked at install. Afterwards, enable the plugin and assign it where it's needed."},{"page":"plugins/","title":"Plugins","heading":"Private registry or mirror","anchor":"private-registry-or-mirror","text":"Plugins come from whichever npm registry your .npmrc points at, so a company mirror or a private registry works for the store, for installs and for a plugin's own dependencies. Put the file in the home directory of the user that runs camera.ui:\n\nregistry=https://npm.example.com/\n//npm.example.com/:_authToken=\n\n- One registry for everything with registry=, or a single scope with @example:registry=https://npm.example.com/.\n- Login with //npm.example.com/:_authToken=, or basic auth through //npm.example.com/:username= plus //npm.example.com/:_password=. Credentials are only sent to the host and path they are written for.\n- Token from the environment. CAMERAUI_NPM_TOKEN (or NPM_TOKEN) replaces the token line. It applies to the registry from registry=, so a scope with its own registry keeps its token in .npmrc.\n- Docker: mount the file to /root/.npmrc. The home directory is not part of the /data volume.\n- Linux service: the home directory of the service user. Desktop app: the home directory of the logged-in user.\n- Remote workers install plugins themselves, so each one needs the same file or the same variable.\n\nThe store lists packages named camera-ui-* or @scope/camera-ui-* that carry the keyword camera-ui-plugin. Registries without a search API, GitHub Packages for example, return no list: type the exact package name instead, and the store looks it up directly."},{"page":"plugins/","title":"Plugins","heading":"Settings","anchor":"settings","text":"A plugin's own settings live on its page, under Settings, and apply server-wide. Settings that affect a single camera are edited in that camera's settings instead. Recording-plugin settings are kept with the rest of recording."},{"page":"plugins/","title":"Plugins","heading":"Setup guides","anchor":"setup-guides","text":"- HomeKit — add your cameras to Apple Home.\n- Home Assistant — cameras, sensors and detection events as Home Assistant entities.\n- Google Home and Alexa — not available yet, both are on the roadmap."},{"page":"recording/browsing","title":"Browsing & filters","heading":"Browsing & filters","anchor":"","text":"The Recordings view lists past events across your cameras, ordered by when something last happened in them. An event that runs for hours shows while it runs, with its newest span on the card. Related events across cameras can appear as an episode card, and a recording that belongs to one shows Open episode. See Episodes.\n\nWhen the list says recordings are unavailable, the NVR plugin is not running. The list fills once the plugin answers again."},{"page":"recording/browsing","title":"Browsing & filters","heading":"Filtering","anchor":"filtering","text":"Show at the top of the sidebar picks what the list holds: All, only Events, or only Episodes. Episodes have no detections of their own, so in that view only camera, time range, Only with recording and Favorites only narrow them down.\n\nThe other filters:\n\n- Camera and time range.\n- Rooms. Selects the cameras placed in those rooms on the floor plan.\n- Labels. Person, vehicle, animal, or other.\n- Trigger. Motion, a doorbell, a sensor, or an audio alarm.\n- Attributes. Events that contain a face or a license plate.\n- Vehicles. Color and type of a vehicle, read from the picture. See Find an object.\n- Confidence. Hide low-confidence detections.\n- Grid search. With a single camera selected, draw a region on its snapshot to only show events detected inside that part of the frame."},{"page":"recording/browsing","title":"Browsing & filters","heading":"View options","anchor":"view-options","text":"Behind the gear, top right:\n\n- Ungroup. One card per moment instead of one card per event. Only events with several moments split, most have one. Episodes stay, pick Events under Show to hide them. Off by default. Your choice is saved to your account, so it follows you to other devices.\n- Only with recording. On by default. Turn it off to also see events where a trigger fired but nothing was recorded, and episodes from times without footage. Those can't be downloaded or previewed.\n- Favorites only. Admins only, see below."},{"page":"recording/browsing","title":"Browsing & filters","heading":"Reading a card","anchor":"reading-a-card","text":"Each card is one moment.\n\n- Time and camera. Selecting the card opens playback at that moment.\n- Faces and plates. When a moment also has a recognized face or a read plate, arrows step through its pictures in place, one per person or plate, with the name or plate shown. Someone the camera keeps losing and finding again still gets one picture. The small tiles at the bottom jump to one. With several people in one moment, the main picture lists every name and counts the unknown faces, like Azra +1.\n- Grouped cards. A grouped card shows the event's other moments as small previews at the bottom. Each opens its own moment.\n- Split cards. With Ungroup on, each card shows its place in the event, like 2/3. Hovering one highlights the other cards of the same event.\n- The dots menu. Holds what the card does not show itself: the detection trace (admins only), Ask the assistant, the download, Correct person (admins only) when a face picture is on screen, and Delete event and Delete recording (admins only, see below).\n- Hold to preview. On a phone or tablet, hold a card to play its short preview (lift to stop, tap to open). On a computer it plays on hover. The preview is zoomed to what the moment was about, with a clock for the frame's time. No preview means there is no playable footage, or it did not load in time. An episode card previews its cut: each camera in turn, with name and time."},{"page":"recording/browsing","title":"Browsing & filters","heading":"Favorites","anchor":"favorites","text":"The star on a recording or episode card keeps that moment: automatic cleanup skips it, so its footage stays playable however old it gets and however tight the disk. Favorites only in the view options lists them. Both are admin-only."},{"page":"recording/browsing","title":"Browsing & filters","heading":"Selecting and deleting","anchor":"selecting-and-deleting","text":"Admins can delete events here: Delete event in a card's dots menu removes that event, the dots button in the corner enters select mode for several or all loaded ones. Deleted events disappear everywhere at once: Home, the timeline, this list, and every open browser.[^delete-disk]\n\nDelete recording removes the event and its video right away. It works like deleting a range on the timeline: per minute, so other events in those minutes go too, and the most recent minutes can't be deleted yet."},{"page":"recording/browsing","title":"Browsing & filters","heading":"Search","anchor":"search","text":"The search in the filter sidebar has one field, and the buttons above it switch what it searches. A running search stays when you switch.\n\n- Search. Labels, names, plates and the text of AI descriptions, as you type.\n- AI Search. Moments by description, like \"a person carrying a package\". See Semantic search.\n- Ask the assistant. With the assistant set up. Type the search in your own words (\"cars in the driveway yesterday\", \"favorites with the doorbell\", \"episodes from the entrance in the last hour\") and it sets the filters below: cameras and rooms, time range, event types, vehicle color and type, sensors, favorites, and an AI Search when the words describe a scene the labels cannot. A note under the box says what the filters cannot express, an exact day for example.\n- Search by picture. Admins only. A picture of a person or a vehicle, see Find an object."},{"page":"recording/browsing","title":"Browsing & filters","heading":"Heatmap","anchor":"heatmap","text":"A heatmap shows where activity was concentrated over a period. See Object detection.\n\n[^delete-disk]: Deleting an event removes it from the list and its detections. The video file is left in place and removed later by the storage cleanup on its normal schedule."},{"page":"recording/export","title":"Export & download","heading":"Export a clip","anchor":"export-a-clip","text":"On the timeline, drag the trim handles to the start and end, then export an MP4. The clip is saved in the quality you are watching, so a lower quality gives a smaller file."},{"page":"recording/export","title":"Export & download","heading":"Export multiple recordings","anchor":"export-multiple-recordings","text":"The download button in the Recordings view opens Export recordings: one or more cameras, a time range across days, a quality (Best quality or Smallest files), and optionally a timelapse interval. You get one MP4 per camera per day (first and last day cut to the selected range, days in between in full) and a size estimate up front. Several files come as one ZIP, a single file as a plain MP4. The dialog's quality choice is independent of what you are watching.\n\nBoth exports convert the recordings of an MJPEG camera to H.264. That takes longer, and the files end up well below the dialog's size estimate."},{"page":"recording/export","title":"Export & download","heading":"Timelapse","anchor":"timelapse","text":"Both export flows can produce a timelapse instead, sampling frames at an interval to compress a long period into a short clip."},{"page":"recording/","title":"Recording (NVR)","heading":"Recording (NVR)","anchor":"","text":"Recording and everything in this section need an active camera.ui subscription.[^license]\n\nPreview phase\nSubscriptions aren't open yet. While camera.ui is in preview, recording is free for everyone with a camera.ui account, no card needed: create one at cloud.cameraui.com and follow the steps below. If the license does not activate, ask on our Discord."},{"page":"recording/","title":"Recording (NVR)","heading":"Turning on recording","anchor":"turning-on-recording","text":"Recording comes from the camera.ui NVR plugin, which is installed by default, and runs on a subscription tied to your camera.ui account. To turn it on:\n\n1. In Settings → Remote Access, choose Register server and sign in with your camera.ui account. This links the server to your account; you don't need to enable remote cloud access for it.\n2. Open Settings → Recordings and, in the License & Cloud card, click the cloud icon and sign in.\n3. Your license then activates automatically. If the status doesn't update, use the Refetch License button.\n\nIf signing in ends with Server limit reached for your plan, the license is still held by another server, an earlier test install for example. Remove that server in the cloud dashboard and sign in again.\n\nWith an active license, new cameras start recording on their own while a camera slot is free: continuously, in every stream quality. A battery camera that its plugin reports as one starts in Ad-hoc instead. Mode, qualities and on/off are in the camera's recording settings.\n\nRecording takes H.264, H.265 and MJPEG. An MJPEG camera records its pictures as it sends them, which needs several times the storage of H.264, and a picture wider or taller than 2040 pixels is cropped to 2040 around its center. A camera that sends another codec records nothing, and its timeline shows a recording error that says so. Live view shows how to transcode such a camera on the server."},{"page":"recording/","title":"Recording (NVR)","heading":"Camera slots","anchor":"camera-slots","text":"The License & Cloud card in Settings → Recordings shows how many recording slots your plan includes, how many cameras are active, and how many currently use a slot.\n\nSlots go to your enabled cameras in order. A camera past the limit keeps detecting, its events and its live view, but does not record and shows no warning, so check that card if footage is missing from one camera. Turning off recording on a camera frees its slot for another.\n\nIf your subscription lapses, recording stops and playback, export, face recognition and search ask for a valid license. Nothing on disk is deleted, it all comes back when the license is valid again.\n\nWithout internet the license keeps working. For the first 14 days without a connection to cameraui.com nothing changes. After that, playback, export, face recognition and search pause, while recording carries on until the paid period ends, plus 14 days for a subscription that renews. Then recording stops as well. Settings → Recordings shows the date, and admins get a notification three days before. One connection brings everything back. For a server that stays offline for good, the yearly plan fits best."},{"page":"recording/","title":"Recording (NVR)","heading":"Recording modes","anchor":"recording-modes","text":"Each camera records in one of three modes, set in its recording settings:\n\n- Continuous. Records around the clock. Uses the most storage.\n- Event. Records only when something happens (motion, an object, a sensor). A pre-buffer keeps a few seconds before each trigger.\n- Ad-hoc. Connects to the camera only when an event fires. For cameras that stop sending events while a stream is open, such as some battery cameras."},{"page":"recording/","title":"Recording (NVR)","heading":"Quality","anchor":"quality","text":"Record one or more stream qualities: high, mid, low. Also recording the low stream makes scrubbing the timeline fast."},{"page":"recording/","title":"Recording (NVR)","heading":"Next","anchor":"next","text":"- Storage & retention — where recordings live and how long they're kept.\n- Timeline & playback — review footage.\n- Export & download — save a clip.\n- Browsing & filters — find past events.\n\n[^license]: Recording (NVR) requires an active camera.ui subscription."},{"page":"recording/storage","title":"Storage & retention","heading":"Where recordings are stored","anchor":"where-recordings-are-stored","text":"By default, recordings live with the rest of camera.ui's data. For more than a couple of cameras, put them on a dedicated local disk, separate from the system disk. See System requirements.\n\nA local disk, or a symlink to one, is fine. A network share (NAS, SMB, NFS) is not: detection events, timeline markers and the search index sit next to the footage as databases, and those need file locking that network filesystems do not provide reliably. What breaks there is the event database, not the video. If your storage lives on a NAS, run camera.ui on the NAS and record locally there."},{"page":"recording/storage","title":"Storage & retention","heading":"Moving recordings to another disk","anchor":"moving-recordings-to-another-disk","text":"Changing the storage path does not move your recordings: camera.ui starts fresh at the new location and the old folder stays as it is. A new path that camera.ui can't create or write to is not taken, recording stays in the old folder and a notification says why. To move the data:\n\n1. Stop the server.\n2. Move the whole storage folder to the new disk, including the events and clip folders. They hold your events, timeline markers and search index for that footage.\n3. Start the server and set the new path in Settings → Recordings.\n\nNothing inside stores an absolute path, so everything works at the new location without a rebuild. Without events and clip the footage arrives bare, and they cannot be recreated from the video files. Enrolled faces live outside the recordings folder and are not affected."},{"page":"recording/storage","title":"Storage & retention","heading":"Rough sizing","anchor":"rough-sizing","text":"Continuous recording size follows the stream's bitrate:\n\nGB per day, per camera ≈ bitrate in Mbit/s × 10.8\n\n| Stream | Bitrate | Continuous, per day | Per camera, 7 days |\n|---|---|---|---|\n| 1080p H.264 | 4 Mbit/s | ~43 GB | ~300 GB |\n| 4K H.264 | 8 Mbit/s | ~86 GB | ~600 GB |\n| H.265 (HEVC) | about half the bitrate | roughly half the above | roughly half the above |\n| 720p MJPEG, 15 fps | ~4 Mbit/s | ~40 GB | ~280 GB |\n\nMJPEG has no fixed bitrate, it follows the camera's resolution, frame rate and JPEG quality.\n\nEvent recording uses a fraction of that, depending on how busy the scene is. When in doubt, set a retention window or storage cap and watch the per-camera rate (below) for a week."},{"page":"recording/storage","title":"Storage & retention","heading":"How long they're kept","anchor":"how-long-theyre-kept","text":"- Retention (Days). Keep footage for up to this many days (0 means no day limit).\n- Max Storage (GB). Cap the total space recordings may use (0 means no cap).\n\nWhen a limit is reached, the Retention Mode decides:\n\n- Overwrite (default). The oldest recordings are deleted to make room. Recording only pauses when other data fills the disk and no older recordings are left to delete.\n- Strict. Nothing is deleted beyond the retention window. If the disk fills, recording pauses until you free space.\n\nCleanup takes the oldest hours of all cameras side by side, so a quiet camera cannot lose its whole history while a busy one keeps everything, and it stops once enough space is free. Favorites are skipped and stay playable however old they are."},{"page":"recording/storage","title":"Storage & retention","heading":"Free disk space","anchor":"free-disk-space","text":"Independent of those limits, Min Free Space (GB) keeps part of the disk clear: cleanup works to keep at least that much available. Below half of it cleanup deletes faster, and recording pauses there with Strict, or with Overwrite once nothing is left to delete. At 0 the reserve is a share of the whole volume.\n\nSet your own value when the recordings share a disk with other data, otherwise the derived reserve on a large disk can delete recordings, or with Strict pause recording, long before your storage cap is reached. Values below 10 GB count as 10, or as a quarter of the disk if that is less."},{"page":"recording/storage","title":"Storage & retention","heading":"Footage from removed cameras","anchor":"footage-from-removed-cameras","text":"If you delete a camera or unassign the NVR from it, its recordings stay on disk. They still count towards Max Storage and are aged out and cleaned up like an active camera's.\n\nTo keep that footage, put an empty file named .cameraui-keep into the camera's folder in your recordings directory. camera.ui then leaves the folder alone: not counted, not aged out, not touched when space runs low.[^keepmarker]\n\n[^keepmarker]: The marker only applies to cameras camera.ui no longer knows. The folder of an active camera follows retention and the storage cap either way."},{"page":"recording/storage","title":"Storage & retention","heading":"Keeping an eye on usage","anchor":"keeping-an-eye-on-usage","text":"Metrics → Storage shows disk usage in the Storage Overview and, in the Camera Storage table, per camera: size on disk, days of footage held, recording rate per day, and mode.\n\nA warning that the storage volume is small means it has little room for recordings, so they rotate out quickly. Usually the larger disk you meant to use isn't mounted, so check the storage path. Separate warnings appear when free space drops below 8% and once recording has paused for lack of space.\n\nIf a custom storage path held recordings before and its disk is missing at startup, camera.ui records into its default folder instead of the empty mount point until the next start, and marks this on the timeline. If the disk drops out while camera.ui records, recording pauses, admins get one notification, and recording resumes by itself once the path is back."},{"page":"recording/storage","title":"Storage & retention","heading":"Uninstalling the NVR","anchor":"uninstalling-the-nvr","text":"Uninstalling the NVR plugin with Also delete stored data removes its settings, databases and caches but keeps your recordings folder, which is protected by a .cameraui-keep file inside it. To delete the recordings too, remove that file first, then uninstall."},{"page":"recording/timeline-playback","title":"Timeline & playback","heading":"Timeline & playback","anchor":"","text":"Each camera's timeline scrubs through its recorded footage, with marks where events happened."},{"page":"recording/timeline-playback","title":"Timeline & playback","heading":"Reviewing footage","anchor":"reviewing-footage","text":"- Scrub along the timeline. Scrubbing uses the low-resolution stream.\n- Play and pause, skip 30 seconds back or forward, and change the speed between 0.25x and 8x.\n- Find an object. Admins only. Open Find, tap a person or a vehicle in the picture, and get the moments that show it. See Find an object.\n- Event marks show a thumbnail. Click one to jump to that moment.\n- Rings and alarms. A doorbell press, siren, security system or contact event shows as a full event bar with its own icon and a snapshot, even when the camera saw nothing. A ring during a running motion event appears right away, not after the motion ends.\n- Filter by event type. The filter button picks which event types show on the timeline and in the detections list. The choice is remembered.\n- Filter by zone. In the filter, turn on Filter by zone and mark areas on the camera picture to keep only events detected inside them.\n- Zoom from an overview down to seconds. At the deepest level scrubbing is frame-accurate in the playback quality and the timeline shows a Fine Scrubbing badge. When a full picture takes too long to load, dragging shows the low-resolution stream and the picture turns sharp when you let go.\n- Jump to a day with the calendar. Days with footage are highlighted."},{"page":"recording/timeline-playback","title":"Timeline & playback","heading":"Playback quality","anchor":"playback-quality","text":"Each camera has a Playback source in its settings, under Interface: Auto, High, Mid or Low. Auto is the default and the one to keep.\n\nA recording can have a hole in one quality and not in another, for example when the high stream dropped out while the low one kept going. On Auto, playback takes the best quality that has footage at that spot, with a badge naming it while it is not the top one, and switches back once the high recording resumes. A pinned quality stays put, hole included."},{"page":"recording/timeline-playback","title":"Timeline & playback","heading":"When a camera was not recording","anchor":"when-a-camera-was-not-recording","text":"Bands under the timeline say what the camera was doing when there is no footage:\n\n- offline means the camera recorded nothing at all.\n- recording without high (or another quality) means only that stream stopped. There is still footage, just not in every quality, and Auto plays it.\n\nA band spans exactly from where the recording stopped to where it resumed. Blips shorter than 15 seconds paint no band.\n\nWhen an outage has phases (the high stream goes first, the rest follows, one comes back before the other), they sit side by side in one band, each in its own color, labeled with the total duration. Hover it to read the phases."},{"page":"recording/timeline-playback","title":"Timeline & playback","heading":"Timeline, detections and find","anchor":"timeline-detections-and-find","text":"The panel switches between its views: Timeline, Detections and, for admins with the NVR plugin, Find for the object search, with the number of matches next to it. Detections lists what was detected: day, time, duration, a preview image and icons for the detected types. Click an entry to jump to it. Playback runs from one entry to the next, and a detection shorter than 5 seconds plays for 5 seconds.\n\nAbove the vertical timeline the views are a switch, in the strip below the picture icons in its top row. A narrow strip shows only the icon of the current view, tap it for the others. On a phone, dots at the bottom of the timeline show the view, tap one or swipe sideways."},{"page":"recording/timeline-playback","title":"Timeline & playback","heading":"Several cameras at once","anchor":"several-cameras-at-once","text":"In Camview, the timeline follows the camera you open and shows its recordings and events. Only that camera decodes, so scrubbing stays fast in a busy layout. The event filter can show the other cameras' events too, and clicking one jumps to it."},{"page":"recording/timeline-playback","title":"Timeline & playback","heading":"Trimming","anchor":"trimming","text":"Export on the timeline turns on trimming: drag the handles to set start and end, then export the selection. See Export & download."},{"page":"recording/timeline-playback","title":"Timeline & playback","heading":"Deleting a range","anchor":"deleting-a-range","text":"Admins can delete footage from the timeline: Delete recordings, drag the handles over the span, then Delete selection. The footage and every event inside it are removed for good, per minute, on every open device. The most recent minutes are still being written and can't be deleted yet."},{"page":"remote/certificate","title":"Certificate","heading":"Certificate","anchor":"","text":"camera.ui generates its own certificate on first start and serves it on every connection. Browsers do not know the issuer, so they warn. For your own domain you can upload a certificate you obtained yourself, no reverse proxy needed."},{"page":"remote/certificate","title":"Certificate","heading":"What the built-in certificate is for","anchor":"what-the-built-in-certificate-is-for","text":"The mobile apps, the Apple TV app and remote workers verify the server against camera.ui's own certificate authority. They reach the server by IP address, and no public issuer signs certificates for private addresses, so that part keeps running on the internal certificate no matter what you upload.\n\nYour certificate is served only when a client asks for a name it covers. Everything else, including every connection to an IP address, still gets the internal one.\n\nThe internal certificate covers 127.0.0.1, the machine's private network addresses, and whatever you picked under Settings → Remote Access → Network: the server addresses and the local address. A public address is only included when you select it there. Changing those reissues the certificate, so a browser exception stored for the old one no longer applies and the warning returns. Devices that installed the downloaded certificate keep trusting the server, because the certificate authority stays the same until a backup restore."},{"page":"remote/certificate","title":"Certificate","heading":"Uploading your own","anchor":"uploading-your-own","text":"Settings → System → Certificate → Upload. Uploading and removing is limited to the master account, and the desktop app has no such card.\n\n- Certificate. Your certificate including the intermediates above it. Let's Encrypt writes this as fullchain.pem. Commercial issuers often send the certificate and the chain as two files, then add the second one in the chain field.\n- Private key. privkey.pem, or the .key your issuer sent. It must not be protected by a passphrase.\n- Chain. Only needed when your issuer ships the intermediates separately.\n\nThe upload is checked before anything is stored: the files have to parse, the key has to belong to the certificate, and it has to be valid right now. Afterwards the card lists the names it covers, who issued it and how long it is valid.\n\nA certificate that holds no intermediates is accepted with a warning. From a public issuer that means the chain is missing: desktop browsers fetch it themselves and look fine, phones refuse the connection. From a local certificate authority that signs directly there is nothing to add.\n\ncamera.ui uses the new certificate immediately. The streaming engine keeps its own copy and offers a restart afterwards, which interrupts running streams for a moment."},{"page":"remote/certificate","title":"Certificate","heading":"Renewal","anchor":"renewal","text":"Uploaded files live in certs/custom/ inside the storage directory, as cert.pem, key.pem and chain.pem. camera.ui watches them, so a replaced file is picked up without a restart.\n\nFor automatic renewal, write to that directory:\n\n- Docker. Mount your certificate directory over certs/custom, read-only.\n- Bare metal. A certbot deploy hook that copies fullchain.pem and privkey.pem there under those names.\n\nAn upload through the interface has to be repeated at every renewal. Let's Encrypt certificates are valid for 90 days."},{"page":"remote/certificate","title":"Certificate","heading":"Removing it","anchor":"removing-it","text":"Settings → System → Certificate → Remove. Every name goes back to the internal certificate."},{"page":"remote/certificate","title":"Certificate","heading":"Next steps","anchor":"next-steps","text":"Reverse proxy — terminate TLS in front of camera.ui instead, useful when you already run one.\nCustom domain — reach your server under your own name from outside."},{"page":"remote/cloud","title":"camera.ui Cloud","heading":"camera.ui Cloud","anchor":"","text":"camera.ui Cloud lets you reach your server from anywhere without touching your router. Your server registers with the cloud, and you connect through your camera.ui account. It's free and separate from the recording subscription."},{"page":"remote/cloud","title":"camera.ui Cloud","heading":"Your server stays yours","anchor":"your-server-stays-yours","text":"camera.ui Cloud is a way to reach your own server, not a copy of it. Your cameras, recordings, settings, and the interface all stay on your server. The cloud's only job is to help your app and your server connect, and your session then runs directly to your own instance. Your video, credentials, and other data go straight to your server and are never visible to camera.ui.\n\nPush notifications are the one thing that does travel through the cloud, and they are end-to-end encrypted: your server seals every alert, only your phone can unlock it, and the cloud forwards the envelope without storing anything. See Notifications."},{"page":"remote/cloud","title":"camera.ui Cloud","heading":"Enabling it","anchor":"enabling-it","text":"In Settings → Remote Access, open the Cloud card and turn on Enabled. The server's name is how it appears in your cloud dashboard.\n\nThe status row shows whether the server is Registered, and a tunnel indicator shows the live connection. To connect a server, choose Register server and complete the pairing. If the status shows Re-authentication required, the server lost its cloud credentials: choose Reconnect and pair again. A registered server can be taken off your account with Unregister server. The same flow on a phone is covered in Pairing mobile apps."},{"page":"remote/cloud","title":"camera.ui Cloud","heading":"Cloud keeps direct access on","anchor":"cloud-keeps-direct-access-on","text":"While Cloud is on, your server also keeps a public address for the apps, so the Enabled switch in the Direct connection card is locked on and External URL shows the address in use.\n\nIf you haven't set up a direct address, or the one you configured can't be reached, camera.ui starts a temporary Cloudflare tunnel instead. To use your own address, set it up under Direct connection.\n\nThe fallback is not permanent. camera.ui keeps checking your own address and goes back to it once it answers, a custom domain every minute and a Cloudflare tunnel after ten minutes, then less often. No restart needed. While the fallback is up, External URL shows the temporary address, and a Cloudflare retry that fails again hands out a new one."},{"page":"remote/cloud","title":"camera.ui Cloud","heading":"The cloud dashboard","anchor":"the-cloud-dashboard","text":"The dashboard lists your registered servers by name, and you pick one to connect to. It is at cloud.cameraui.com in a browser and is the cloud server list in the mobile apps. You can share a server with another camera.ui account by invite, who then signs in with their own login.\n\nThe bin icon next to a server removes it from your account, also when you can no longer reach the server itself. That frees its place in your plan, so a new server that stopped at Server limit reached for your plan can connect afterwards. On a server shared with you, the bin only ends your own access."},{"page":"remote/cloud","title":"camera.ui Cloud","heading":"Turning it off","anchor":"turning-it-off","text":"Disabling cloud access ends any active remote sessions and stops share links from working. Local connections on your network keep working."},{"page":"remote/cloudflare","title":"Cloudflare tunnel","heading":"Cloudflare tunnel","anchor":"","text":"A Cloudflare tunnel gives your server a public address through Cloudflare's network, without opening ports on your router. In Settings → Remote Access, set the Direct connection method to Cloudflare, then pick a variant:\n\n- Quick Tunnel. A temporary address with no Cloudflare account needed. Good for a quick test. The address changes each time, and from about four people watching remotely at once a named tunnel holds up better.\n- Named Tunnel (Token). Use your own domain. Create a tunnel in your Cloudflare dashboard and give it a public hostname that points at your server, for example https://192.168.1.10:3443. The server uses a self-signed certificate, so turn on No TLS Verify in the tunnel's origin settings. Then paste Hostname and Tunnel Token here. Your domain must already be a zone in your Cloudflare account.\n- Named Tunnel (OAuth). Use your own domain, with camera.ui doing the setup. Enter a hostname, choose Connect Cloudflare, sign in to authorise your zone, and camera.ui creates the tunnel and DNS record for you.\n\nWith cloud access on, camera.ui keeps a public address up at all times. If your named tunnel stops connecting, it starts a Quick Tunnel so the server stays reachable and tries your tunnel again after ten minutes, then after twice the wait each time, up to every four hours. Saving the settings under Remote Access starts over at ten minutes. Each failed retry leaves you on a new Quick Tunnel address, and the apps need a moment to find the server again.\n\nEvery variant connects out to Cloudflare over TCP on port 7844. Behind a firewall that filters outgoing traffic, allow TCP on that port.\n\nTo use QUIC instead, add cloudflaredProtocol: quic to camera.ui.yaml in your storage directory and restart the server. In Docker, CAMERA_UI_CLOUDFLARED_PROTOCOL under environment: does the same. quic needs UDP on port 7844, auto tries QUIC and falls back to TCP. Over QUIC, a few stalled viewers, a locked phone with live cameras open for example, can freeze the tunnel for everyone."},{"page":"remote/custom-domain","title":"Custom domain","heading":"Custom domain","anchor":"","text":"In Settings → Remote Access, set the direct connection method to Custom domain and enter the URL where the server is reachable, for example https://nvr.example.com.\n\nThe server must already be reachable at that address through your own DNS and a reverse proxy or port forwarding: camera.ui only records the address, it does not change your network. Forwarding a port sends traffic to 3443, and the server answers it with its own self-signed certificate, so browsers and apps show a warning. A reverse proxy with a real certificate avoids that, and Certificate covers uploading one to camera.ui itself. Reverse proxy covers what a proxy has to forward.\n\nThat one port carries the interface, the API and MSE playback. WebRTC media travels separately, over port 2004 (UDP, with TCP on the same port as fallback), so forward that too for the low-latency path from outside. Without it the live view falls back to MSE.\n\nTest connection checks the saved address, so Save first. It requests the address from the server itself, which fails on routers without hairpin NAT or with split DNS even when the address works fine from outside. Remote access keeps running either way, camera.ui only gives up a custom domain when the name stops resolving publicly.\n\nIf you don't run your own domain or reverse proxy, camera.ui Cloud or a Cloudflare tunnel is simpler, since neither needs router changes."},{"page":"remote/","title":"Remote access","heading":"Remote access","anchor":"","text":"Without remote access, camera.ui is reachable on your local network only. Remote access is free and doesn't need the recording subscription.[^free]\n\nRemote access is an admin setting, under Settings → Remote Access. The page shows your current connection status (local, local network, remote, or cloud) and the address you're connected on. The info button next to it opens Connection details: the address this app is using, what the server sees from its side, and every address the app tried in its last attempt with the time it took and why it failed. Copy details copies all of it for a support message. The same page holds the Network card, where you pick which of the server's IP addresses apps may use and can give it a Local address on your own network. Its list holds VPN addresses such as WireGuard too, but leaves out container and VM networks.\n\nOn connecting, and whenever the path changes (you get home and the app leaves the internet path, say), a short notice at the bottom says Connected via local network, Connected via internet or Connected via cloud."},{"page":"remote/","title":"Remote access","heading":"Choosing how to connect","anchor":"choosing-how-to-connect","text":"- camera.ui Cloud. Register your server and reach it from anywhere, no router setup or port forwarding. Recommended for most people, and required for the mobile apps when away from home.\n- Direct connection. Expose your server with a public address of your own, either through a Cloudflare tunnel or your own domain.\n\nYou can also keep access local only by leaving remote access off.\n\n[^free]: Remote access and camera sharing are included at no cost. The subscription covers recording (NVR), not remote access."},{"page":"remote/pairing","title":"Pairing mobile apps","heading":"Pairing mobile apps","anchor":"","text":"The mobile apps connect to your server through camera.ui Cloud. You pair the app with your server once."},{"page":"remote/pairing","title":"Pairing mobile apps","heading":"Pairing","anchor":"pairing","text":"1. Make sure camera.ui Cloud is enabled on the server.\n2. On the server, open Settings → Remote Access and choose Register server. A QR code, a pair code, and a pair URL appear.\n3. Scan the QR code with your phone's camera, or open the pair URL in a browser, and sign in to your camera.ui account. The code is valid for 10 minutes.\n4. Press Connect. The server appears in your account."},{"page":"remote/pairing","title":"Pairing mobile apps","heading":"Connecting","anchor":"connecting","text":"Paired servers appear in the app's cloud server list. Pick one to connect, and switch between them in the app."},{"page":"remote/proxy-sign-in","title":"Sign-in through a proxy","heading":"Sign-in through a proxy","anchor":"","text":"Behind Authelia, Authentik, oauth2-proxy or another login proxy, camera.ui can take over the user the proxy signed in and skip its own login form. The proxy names the user in a header, and camera.ui signs in the account with exactly that username, capitals included. It creates no accounts: everyone the proxy lets through needs an account in Settings → Users."},{"page":"remote/proxy-sign-in","title":"Sign-in through a proxy","heading":"Setup","anchor":"setup","text":"Add a proxyAuth block to camera.ui.yaml in your storage directory and restart the server:\n\nproxyAuth:\n port: 8082\n trust:\n - 10.0.0.5\n\ncamera.ui opens a separate plain HTTP port for the proxy, bound to the same address as the main port. Point the proxy at http://10.0.0.27:8082 instead of the main port, with WebSockets and long timeouts as on Reverse proxy. Only this port reads the header. Sent to the main port, the plain HTTP port or through camera.ui Cloud, it does nothing.\n\n| Field | Environment variable | Meaning |\n|---|---|---|\n| port | CAMERA_UI_PROXY_AUTH_PORT | Port the proxy connects to. |\n| header | CAMERA_UI_PROXY_AUTH_HEADER | Header with the username, Remote-User by default. |\n| trust | CAMERA_UI_PROXY_AUTH_TRUST | Addresses of the proxy, as IP addresses or ranges like 10.0.0.0/24. In the variable, separated by commas. |\n| secret | CAMERA_UI_PROXY_AUTH_SECRET | At least 16 characters the proxy sends as X-Proxy-Secret on every request. |\n| logoutUrl | CAMERA_UI_PROXY_AUTH_LOGOUT_URL | Logout page of the proxy, see Signing out. |\n\nA variable takes precedence over the file. The port needs trust, secret or both, and with both set a request has to pass both. A setting camera.ui can't use keeps the port closed, and the log says why."},{"page":"remote/proxy-sign-in","title":"Sign-in through a proxy","heading":"Which header","anchor":"which-header","text":"| Proxy | Header | Logout page |\n|---|---|---|\n| Authelia | Remote-User | https://auth.example.com/logout |\n| Authentik | X-authentik-username | https://nvr.example.com/outpost.goauthentik.io/sign_out |\n| oauth2-proxy | X-Forwarded-Preferred-Username | https://nvr.example.com/oauth2/sign_out |\n\nWith forward auth, the web server has to copy the header from the auth response onto the request: copy_headers in Caddy, authResponseHeaders in Traefik, auth_request_set plus proxy_set_header in nginx."},{"page":"remote/proxy-sign-in","title":"Sign-in through a proxy","heading":"Who may sign in","anchor":"who-may-sign-in","text":"Whoever connects from a trusted address, or with the secret, can sign in as any user, master included. camera.ui's password and two-factor authentication don't apply on this port, the proxy's login does. So trust only the proxy:\n\n- Docker. camera.ui sees the proxy's address on the shared Docker network. Give the proxy a fixed address there (ipv4_address in Compose) or use the secret instead of trust. The log names every address it turned away.\n- Same machine. A proxy next to a bare-metal install connects over 127.0.0.1. Trusting that address lets every program on the machine sign in, plugins included, so set a secret as well.\n- Paths without a login. The proxy has to replace the header on every request it passes on. If it lets a path through without asking for a login, it must not forward a header of that name sent by the browser. Put camera.ui behind the login as a whole, or have the proxy remove the header."},{"page":"remote/proxy-sign-in","title":"Sign-in through a proxy","heading":"In the app","anchor":"in-the-app","text":"The browser opens camera.ui signed in. All browsers of one user share one session, listed as Reverse proxy under Logged in users. A request without the header gets the normal login form.\n\nIf the proxy names a user camera.ui doesn't have, the browser shows an error instead of the app and the log names the username. Since camera.ui finds the account by its username, the account settings show the username but don't let you change it while you are signed in through the proxy. If an admin renames the account, change the name on the proxy side too."},{"page":"remote/proxy-sign-in","title":"Sign-in through a proxy","heading":"Signing out","anchor":"signing-out","text":"Without logoutUrl, the menu has no Logout, since the proxy would sign you back in at once. With logoutUrl, Logout ends the camera.ui session and opens that page, which ends the session at the proxy."},{"page":"remote/proxy-sign-in","title":"Sign-in through a proxy","heading":"Next steps","anchor":"next-steps","text":"Reverse proxy — WebSockets, timeouts and certificates for the proxy.\nUsers & roles — create the accounts the proxy signs in."},{"page":"remote/reverse-proxy","title":"Reverse proxy","heading":"Reverse proxy","anchor":"","text":"Behind Apache, nginx, Traefik or any other reverse proxy, camera.ui needs more than plain HTTP: live video, events and the camera list travel over WebSockets. A proxy that forwards only ordinary requests gives you a UI that loads and stays empty."},{"page":"remote/reverse-proxy","title":"Reverse proxy","heading":"What the server expects","anchor":"what-the-server-expects","text":"The main port serves HTTPS. camera.ui listens on port 3443 by default, using a certificate it generates itself. A proxy pointed at that port has to connect with https:// and skip certificate verification, since there is no public issuer to check against. Or switch on a plain HTTP port.\n\nWebSocket upgrades have to reach the server. They use the same host and port as the rest of the app, so the simplest setup forwards everything under / with upgrades.\n\nConnections stay open for a long time. A live stream holds one connection for as long as you watch. Proxy timeouts in the low seconds cut the picture at regular intervals.\n\nOnly a trusted proxy passes on the client's address. camera.ui reads X-Forwarded-For from a proxy on the same machine, which connects over 127.0.0.1 or ::1, and from the addresses you trust. Behind any other proxy, the session lists show the proxy's address."},{"page":"remote/reverse-proxy","title":"Reverse proxy","heading":"Apache","anchor":"apache","text":"Enable proxy, proxy_http, proxy_wstunnel, ssl and headers, then use this virtual host:\n\n ServerName nvr.example.com\n\n ProxyPreserveHost On\n ProxyRequests Off\n\n SSLProxyEngine On\n SSLProxyVerify none\n SSLProxyCheckPeerCN off\n SSLProxyCheckPeerName off\n SSLProxyCheckPeerExpire off\n\n ProxyTimeout 300\n\n ProxyPass \"/\" \"https://10.0.0.27:3443/\" upgrade=websocket timeout=300\n ProxyPassReverse \"/\" \"https://10.0.0.27:3443/\"\n\n RequestHeader set X-Forwarded-Proto \"https\"\n RequestHeader set X-Forwarded-Port \"443\"\n\nThe upgrade=websocket option needs Apache 2.4.47 or newer (apachectl -v). On older versions, drop that option and route the upgrades with mod_rewrite instead:\n\n ProxyPass \"/\" \"https://10.0.0.27:3443/\"\n ProxyPassReverse \"/\" \"https://10.0.0.27:3443/\"\n\n RewriteEngine On\n RewriteCond %{HTTP:Upgrade} =websocket [NC]\n RewriteCond %{HTTP:Connection} upgrade [NC]\n RewriteRule /(.*) wss://10.0.0.27:3443/$1 [P,L]\n\nThe rewrite target must be wss://. With ws:// the handshake goes unencrypted to a TLS port, Apache logs error reading status line from remote server, and every WebSocket fails with a 502 while normal pages load fine."},{"page":"remote/reverse-proxy","title":"Reverse proxy","heading":"nginx","anchor":"nginx","text":"map $http_upgrade $connection_upgrade {\n default upgrade;\n '' close;\n}\n\nserver {\n listen 443 ssl;\n server_name nvr.example.com;\n\n client_max_body_size 100m;\n\n location / {\n proxy_pass https://10.0.0.27:3443;\n proxy_ssl_verify off;\n\n proxy_http_version 1.1;\n proxy_set_header Upgrade $http_upgrade;\n proxy_set_header Connection $connection_upgrade;\n\n proxy_set_header Host $host;\n proxy_set_header X-Forwarded-Proto $scheme;\n proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n\n proxy_buffering off;\n proxy_read_timeout 300s;\n }\n}\n\nclient_max_body_size matters if you restore a backup through the proxy, since nginx rejects uploads over 1 MB by default."},{"page":"remote/reverse-proxy","title":"Reverse proxy","heading":"Proxy on another machine","anchor":"proxy-on-another-machine","text":"For the session lists to show the client's address behind a proxy on another host, add the proxy to camera.ui.yaml in your storage directory and restart the server:\n\ntrustedProxies:\n - 10.0.0.5\n\nAn entry is an IP address or a range like 10.0.0.0/24. In Docker, CAMERA_UI_TRUSTED_PROXIES=10.0.0.5 does the same, with several entries separated by commas. camera.ui skips anything else and names it in the log. A listed address can pass on any client address it likes, so list the proxy and nothing else."},{"page":"remote/reverse-proxy","title":"Reverse proxy","heading":"Plain HTTP instead","anchor":"plain-http-instead","text":"camera.ui can open a second listener that speaks plain HTTP and forwards everything, WebSockets included, to the main port. Add a port to camera.ui.yaml in your storage directory:\n\ninsecurePort: 8081\n\nIn Docker you can set CAMERA_UI_INSECURE_PORT=8081 instead. Restart the server, then point the proxy at http://10.0.0.27:8081 and drop the SSLProxy* lines or proxy_ssl_verify.\n\nThis listener binds to the same address as the main port, so it is unencrypted on your whole network. Use it only where that traffic stays trusted, such as a proxy on the same machine or a network segment you control."},{"page":"remote/reverse-proxy","title":"Reverse proxy","heading":"When the interface loads but stays empty","anchor":"when-the-interface-loads-but-stays-empty","text":"Cameras show a placeholder, the event row never fills, and nothing updates. That is a WebSocket problem, not an authentication problem. In the browser's developer tools, filter the network tab by WS and reload. A failing entry with status 502 or 400 confirms it, and your proxy's error log names the reason."},{"page":"remote/reverse-proxy","title":"Reverse proxy","heading":"Behind a login proxy","anchor":"behind-a-login-proxy","text":"With Pangolin, Authelia or another proxy that asks for its own login, page loads always go through the proxy, so it can ask again once its session runs out. If a browser still stays on Cannot reach your home server, clear its site data for camera.ui once and open the page again. To skip the camera.ui login behind such a proxy, see Sign-in through a proxy."},{"page":"remote/reverse-proxy","title":"Reverse proxy","heading":"Telling camera.ui about the address","anchor":"telling-cameraui-about-the-address","text":"Once the proxy works, enter the public address under Settings → Remote Access as described in Custom domain.\n\nIf the proxy also answers on your own network, put its address into Local address in the Network card on the same page, for example https://camera.home.lan. Apps then talk to the proxy at home instead of the server's IP. Apps that connect through camera.ui Cloud do this only for a local name, one ending in .lan, .local or .home.arpa for example, or one without a dot. They treat any other name as an internet address. The proxy needs a certificate for that name and has to pass WebSocket connections through, the same as for outside access. It also has to forward OPTIONS requests to camera.ui and pass its Access-Control-* headers on unchanged, since the apps call it from another origin. Otherwise they take the internet path at home. Camera streams keep using the IP addresses in Server addresses above, so leave those alone.\n\nTest connection checks the address by requesting it from the server itself. If your router has no hairpin NAT, or internal DNS sends the name somewhere else, that request never comes back and the test fails even though the address works from outside. Remote access keeps running anyway: camera.ui gives up a custom domain only when the name stops resolving on the public internet. Pointing your internal DNS at the proxy makes the test pass too."},{"page":"remote/reverse-proxy","title":"Reverse proxy","heading":"Next steps","anchor":"next-steps","text":"Custom domain — enter the public address in camera.ui.\ncamera.ui Cloud — remote access without touching your network."},{"page":"sensors/controls","title":"Controls & states","heading":"Controls & states","anchor":"","text":"Once a camera has sensors set up, you can see their state and operate the ones that support it."},{"page":"sensors/controls","title":"Controls & states","heading":"Where sensors appear","anchor":"where-sensors-appear","text":"- The camera's Overview (in the settings drawer) lists every sensor assigned to the camera with its live state and, for controllable ones, the controls.\n- Shortcuts let you place a sensor on the video to read its state on hover and operate it on click.\n- The Sensors page lists every sensor in the system. Its edit dialog shows the same controls, including for sensors assigned to no camera."},{"page":"sensors/controls","title":"Controls & states","heading":"Read-only sensors","anchor":"read-only-sensors","text":"These show their current state but aren't operated:\n\n- Contact. Open or closed, for a door or window.\n- Temperature and humidity. The current reading.\n- Occupancy. Occupied or empty.\n- Smoke and leak. Clear or triggered.\n- Gas, carbon monoxide, heat, cold, vibration, tamper, problem, and power. Clear or triggered.\n- Illuminance and CO₂. The current reading.\n- Battery. Level and charging."},{"page":"sensors/controls","title":"Controls & states","heading":"Controllable accessories","anchor":"controllable-accessories","text":"These you can operate (admin only):\n\n- Light. On or off, and brightness where supported.\n- Switch. On or off.\n- Lock. Lock or unlock.\n- Siren. On or off, and volume where supported.\n- Garage. Open or close.\n- Doorbell. Trigger a ring.\n- Security system. Choose a mode: Off, Home, Away or Night. If the alarm goes off, the control shows an alarm banner and the modes stay locked while it's active.\n\nPTZ cameras have their own controls and autotrack."},{"page":"sensors/","title":"Sensors & accessories","heading":"Sensors & accessories","anchor":"","text":"A sensor is any non-video capability in camera.ui: a detection, an environmental reading, or a controllable accessory. Most sensors come from plugins; virtual sensors you create yourself.\n\nEvery sensor is its own entity. It keeps its name, settings, and history no matter which cameras it is attached to, and it can be assigned to one camera, to several, or to none at all."},{"page":"sensors/","title":"Sensors & accessories","heading":"Types of sensor","anchor":"types-of-sensor","text":"- Detection. Motion, object, audio, face, license plate, and semantic (CLIP). These are covered under Detection & AI.\n- Environmental & state. Contact, temperature, humidity, occupancy, smoke, leak, battery, gas, carbon monoxide, CO₂, heat, cold, vibration, tamper, problem, power, and illuminance. These are read-only. The binary ones (like gas or tamper) can trigger a camera's detection the same way a contact sensor does.\n- Accessories. Light, switch, lock, siren, garage, security system, and doorbell. These can be controlled.\n- PTZ. Pan, tilt, and zoom. See PTZ & autotrack."},{"page":"sensors/","title":"Sensors & accessories","heading":"Camera hardware and standalone sensors","anchor":"camera-hardware-and-standalone-sensors","text":"- Camera hardware. A camera's own plugin registers them on that camera: its doorbell button, spotlight, siren, or battery. The assignment is fixed; you can't move them to another camera.\n- Standalone. Everything else, from a smart plug brought in by an integration to a virtual sensor you created. You decide which cameras it belongs to, on the Sensors page. An assigned sensor shows up on those cameras and can trigger their detection.\n\nDeleting a camera takes its camera-hardware sensors with it. A standalone sensor that was assigned to it stays and only loses the assignment."},{"page":"sensors/","title":"Sensors & accessories","heading":"Setting up and using sensors","anchor":"setting-up-and-using-sensors","text":"Plugin sensors are enabled per camera, and every sensor is managed on the Sensors page, where integrations offer what they find under Discovered. See Set up sensors, and Controls & states to read state and operate accessories.\n\nVirtual sensors need no plugin, see Virtual sensors."},{"page":"sensors/","title":"Sensors & accessories","heading":"Export to your smart home","anchor":"export-to-your-smart-home","text":"Each sensor has an Expose sensor toggle. Exposed sensors show up in HomeKit, Home Assistant, and MQTT; turn the toggle off to keep a sensor local to camera.ui. Camera-fixed types such as PTZ and battery are always handled by their camera and have no toggle."},{"page":"sensors/","title":"Sensors & accessories","heading":"In automations","anchor":"in-automations","text":"Sensors can trigger automations, a contact opening for example, and automations can operate accessories such as a light."},{"page":"sensors/ptz","title":"PTZ & autotrack","heading":"PTZ & autotrack","anchor":"","text":"PTZ cameras (pan, tilt, zoom) can be moved from camera.ui, and with autotrack they follow detected objects on their own.\n\nPTZ is a sensor a plugin provides, the ONVIF plugin for example, enabled in the camera's settings under the Plugins tab. Only the moves the plugin reports (pan, tilt, zoom, presets and home) are shown."},{"page":"sensors/ptz","title":"PTZ & autotrack","heading":"Controlling a PTZ camera","anchor":"controlling-a-ptz-camera","text":"Hovering a PTZ camera's player shows a bar at the top right:\n\n- Go to home. Send the camera back to its home position. Shown when the camera has one.\n- Presets. Jump to a position saved on the camera. Shown when the camera reports at least one. camera.ui reads presets from the camera but cannot create or delete them.\n- PTZ. Show the on-screen controls: the zoom slider on the left, the pan and tilt joystick on the right.\n\nThe bar is for admins, needs a player at least 350 pixels wide, and hides while the timeline or a recording is open."},{"page":"sensors/ptz","title":"PTZ & autotrack","heading":"Autotrack","anchor":"autotrack","text":"Autotrack lets a PTZ camera follow detected objects automatically using pan and tilt. It needs object detection on the camera.\n\nIn the camera's settings, under Autotrack:\n\n- Enable autotrack. Turn tracking on.\n- Target labels. Which object types the camera should follow, such as people or vehicles.\n- Minimum confidence. Only track detections above this confidence score.\n- Dead zone. A central area where a target can move without the camera adjusting.\n- Tracking speed. How aggressively the camera moves to re-center the target. Higher is snappier, lower is smoother.\n- Motion prediction. Aims ahead of a moving target by this many milliseconds, covering the time the camera needs to move and settle. Lower it if the camera overshoots, raise it for fast targets. The prediction fades out as the target slows down. 1500 by default, up to 4000. Set it to 0 to turn it off.\n- Pan-rate calibration. Calibrates how far each move step travels on your camera. Lower it if the camera stops short of the target, raise it if it overshoots.\n- Return to home. Move the camera back to its home position when no target is seen for a while.\n- Wait before returning. How long to wait without a target before returning home.\n- Smallest object. Ignore objects that take up less than this share of the picture height. 0 turns the limit off.\n- Largest object. Stop following once an object fills more than this share of the picture height. The camera holds its position until the object backs off. 0 turns the limit off.\n- Only during these hours. Run autotrack between two times, for example at night. The times are read in the timezone of the browser you set them in, and a window that ends before it starts runs across midnight. Outside the window the camera stays where it is, and goes home if Return to home is on. Off means all day.\n\nThe size limits apply when autotrack picks a target. A target it is already following stays followed as it gets smaller, and a target that grows past the largest size keeps its place while the camera waits.\n\nWhen the target is hidden for a moment, the camera holds its position for up to three seconds and keeps following it once it shows up again. After that it follows the largest target in view.\n\nIf you move the camera yourself, with the joystick or your camera's own app, autotrack pauses for 45 seconds, counted from when you stop moving."},{"page":"sensors/setup","title":"Set up sensors","heading":"Set up sensors","anchor":"","text":"Most sensors come from plugins: you enable the providing plugin and the sensor in the camera's settings. Every sensor, including ones without a camera, is managed on the Sensors page. Both need admin."},{"page":"sensors/setup","title":"Set up sensors","heading":"Open the Plugins tab","anchor":"open-the-plugins-tab","text":"The Plugins tab in a camera's settings is grouped into categories:\n\n- Detection. The detectors: motion, object, audio, face, face recognition, person re-ID, segmentation, license plate, and CLIP.\n- Accessories. Controllable accessories and state sensors, such as lights, locks, switches, sirens, and contact sensors.\n- Core. Single-provider sensors such as PTZ and battery.\n- Hub. Smart-home and platform integrations, such as a HomeKit bridge.\n- More. Enable or disable whole plugins for this camera.\n\nA camera added through a plugin (such as ONVIF or Reolink) also has a category for that plugin, holding its settings."},{"page":"sensors/setup","title":"Set up sensors","heading":"Assign and enable sensors","anchor":"assign-and-enable-sensors","text":"Within a category, pick a sensor type, then choose the plugin that should provide it. Detection types take one provider at a time; accessories can come from several plugins. Each enabled sensor appears in the Sensors list, where you can rename it.\n\nA doorbell sensor assigned to a camera starts a camera event on every ring, without enabling it under Detection."},{"page":"sensors/setup","title":"Set up sensors","heading":"Configure settings","anchor":"configure-settings","text":"Plugin settings for this camera and per-sensor settings appear as a form in the same place. Changes take effect immediately.\n\nEnabled sensors show up in the camera's Overview and on the video. See Controls & states to use them, and PTZ & autotrack for PTZ cameras."},{"page":"sensors/setup","title":"Set up sensors","heading":"The Sensors page","anchor":"the-sensors-page","text":"The page has two lists. Adopted sensors holds everything camera.ui manages, plugin-provided and virtual. A camera's own sensors (motion, battery, PTZ) are listed only where their plugin is enabled for that category on that camera. The settings button next to the search field holds Show only enabled camera sensors (on by default) and Hide camera sensors; turn both off to see everything when cleaning up.\n\nEach row shows the connection state, name, ID, type, providing plugin and assigned cameras. The ID is the sensor's address in its source system (for Home Assistant, the entity id) and tells same-named sensors apart; search matches it too. It is a label, not the identity: rename the entity in Home Assistant and the sensor keeps its cameras, automations and history, only the ID changes. Sensors you created yourself have none.\n\nThe dot before the name shows one of four states: connected, not connected (the plugin is stopped), unavailable (the plugin runs but cannot reach the sensor, or the source reports it as unavailable), and removed in the source (the entity was deleted there). Nothing is deleted on its own: a removed sensor stays until you delete it, so nothing that referenced it breaks silently. A lock next to the camera marks camera hardware, a crossed-out eye marks a sensor hidden from your smart home, and the icon glows while the sensor is active. The floating buttons create a virtual sensor and start select mode for deleting several at once.\n\nDiscovered lists what plugins found, with name, ID, type, room and plugin. Nothing is adopted on its own: a plugin like Home Assistant offers everything it understands and leaves the choice to you. An adopted sensor (one row, or many via the select button) moves into the list above, where it can be assigned to cameras. Rescan asks the plugins again; the list also refreshes by itself while the page is open.\n\nClick a row to edit the sensor:\n\n- Name. How the sensor appears everywhere.\n- ID. The sensor's address in the system it comes from, read-only. Only shown when the sensor has one.\n- Assigned cameras. Pick any number of cameras. The sensor shows up on those cameras and can trigger their detection. Camera hardware is fixed to its camera and can't be re-assigned.\n- Expose sensor. Whether the sensor shows up in HomeKit, Home Assistant, and MQTT.\n\nControllable sensors can also be operated right in this dialog.\n\nRow actions:\n\n- History. The sensor's recorded state changes.\n- Delete sensor. Any adopted or virtual sens"},{"page":"sensors/setup","title":"Set up sensors","heading":"The Sensors page","anchor":"the-sensors-page","text":"or, connected or not. Only a camera's own hardware sensors cannot be deleted while their camera is connected, they would come back with it.[^delete]"},{"page":"sensors/setup","title":"Set up sensors","heading":"Virtual sensors","anchor":"virtual-sensors","text":"Sensors without a plugin behind them are created on the Sensors page, see Virtual sensors.\n\n[^delete]: Deleting removes the sensor and its assignments. A deleted adopted sensor shows up under Discovered again; adopting it once more creates a new sensor, and anything that referenced the old one (automations, smart-home exports) must be pointed at the new one."},{"page":"sensors/virtual","title":"Virtual sensors","heading":"Virtual sensors","anchor":"","text":"A virtual sensor is a sensor with no plugin behind it. You create it yourself, camera.ui holds its state, and you set that state from the UI or from an automation.\n\nTypical uses: a doorbell you trigger from an automation, a contact you flip when an MQTT message arrives, a switch for something no plugin reports. Otherwise it behaves like any other sensor: assign it to cameras, place it on the video as a shortcut, export it to your smart home, use it as an automation trigger.\n\nCreating, renaming, and deleting virtual sensors needs admin."},{"page":"sensors/virtual","title":"Virtual sensors","heading":"Create a virtual sensor","anchor":"create-a-virtual-sensor","text":"Create virtual sensor on the Sensors page asks for:\n\n- Type. What the sensor represents. Can't be changed later.\n- Name. How the sensor appears in camera.ui, for example \"Front Door\". Two virtual sensors of the same type can't share a name.\n\nThe sensor starts in its idle state: off, closed, locked, or disarmed, depending on the type. It belongs to no camera until you assign cameras from its row; it then shows up in their Overview.[^cameradelete]"},{"page":"sensors/virtual","title":"Virtual sensors","heading":"Available types","anchor":"available-types","text":"There are 23 types:\n\n- Doorbell. A ring you trigger. It resets itself two seconds later.\n- Switch. On or off.\n- Contact. Open or closed, for a door or window.\n- Occupancy. Occupied or empty.\n- Smoke. Clear or triggered.\n- Leak. Clear or triggered.\n- Light. On or off, with brightness.\n- Siren. On or off, with volume.\n- Lock. Locked or unlocked.\n- Garage. Open or closed.\n- Security System. Off, Home, Away, or Night.\n- Temperature. A temperature reading.\n- Humidity. A humidity reading.\n- Gas. Clear or triggered.\n- Carbon Monoxide. Clear or triggered.\n- Heat. Clear or triggered.\n- Cold. Clear or triggered.\n- Vibration. Clear or triggered.\n- Tamper. Clear or triggered.\n- Problem. Clear or triggered.\n- Power. Clear or triggered.\n- Illuminance. A light-level reading in lux.\n- CO₂. A carbon dioxide reading in ppm."},{"page":"sensors/virtual","title":"Virtual sensors","heading":"Control it","anchor":"control-it","text":"Doorbell, Switch, Light, Siren, Lock, Garage and Security System get the same controls as plugin-provided ones (see Controls & states): on an assigned camera's Overview tab, and in the sensor's edit dialog on the Sensors page, which also covers sensors assigned to no camera.\n\nContact, Occupancy, Smoke, Leak, Temperature, Humidity, Gas, Carbon Monoxide, Heat, Cold, Vibration, Tamper, Problem, Power, Illuminance, and CO₂ are read-only. Their value comes from an automation.\n\nLock, Garage and Security System switch immediately, with no \"locking\" or \"opening\" state in between.\n\nThe last state survives a restart."},{"page":"sensors/virtual","title":"Virtual sensors","heading":"Control it from an automation","anchor":"control-it-from-an-automation","text":"In the automation editor, a Control sensor action sets a virtual sensor, and a Sensor change trigger reacts when its state changes. For the read-only types this is the only way to set a value: an automation that receives an MQTT message, for example, can put that value on a virtual temperature sensor."},{"page":"sensors/virtual","title":"Virtual sensors","heading":"Rename or delete","anchor":"rename-or-delete","text":"On the Sensors page: click the row to rename, use the row's delete button to remove.[^delete]\n\nA new name applies everywhere (Overview, shortcuts, automations), and automations using the sensor keep working.\n\n[^cameradelete]: Deleting a camera only removes the assignment. The sensor itself stays and can be assigned to other cameras.\n\n[^delete]: Deleting a virtual sensor stops any automation that references it. Creating a new sensor with the same type and name does not restore the link, since it is a new sensor. Point the automation at it again."},{"page":"troubleshooting/","title":"Troubleshooting","heading":"Troubleshooting","anchor":"","text":"The most common issues and where to look. The Logs show what the server is actually doing."},{"page":"troubleshooting/","title":"Troubleshooting","heading":"A camera won't connect","anchor":"a-camera-wont-connect","text":"- Check the camera's sources. The Sources tab shows each stream's live status, detected codecs, and URL.\n- Make sure the stream URL and credentials are correct. Test the RTSP URL in a player like VLC to rule out the camera itself.\n- Confirm the camera is on the same network as the server, or otherwise reachable from it.\n- For brand or discovery-based cameras, check the relevant protocol or plugin."},{"page":"troubleshooting/","title":"Troubleshooting","heading":"Live view is black or won't play","anchor":"live-view-is-black-or-wont-play","text":"- If the player shows Stream unavailable, the line below it is the reason the streaming engine gave. Small players leave it out, the camera page shows it.\n- Check the source's codec in the Sources tab; very unusual codecs may need transcoding.\n- Try a different streaming mode in the camera's interface settings.\n- Enable hot mode and preload on the source for faster, more reliable opening."},{"page":"troubleshooting/","title":"Troubleshooting","heading":"Detection isn't firing","anchor":"detection-isnt-firing","text":"- Make sure a motion engine and, for objects, an AI backend are assigned to the camera. See Set up sensors.\n- Lower the confidence or motion sensitivity in Settings → Detection if events are missed.\n- Check your zones cover the area, and that the type you expect is listed on one of them.\n- Remember the cascade: object, face, and plate detection only run after motion or another trigger. See Detection."},{"page":"troubleshooting/","title":"Troubleshooting","heading":"OpenVINO can't find the GPU on bare metal","anchor":"openvino-cant-find-the-gpu-on-bare-metal","text":"The OpenVINO plugin logs libOpenCL.so.1: cannot open shared object file when the host is missing Intel's OpenCL runtime. Install it with sudo apt install ocl-icd-libopencl1 intel-opencl-icd, and make sure the service user is in the render and video groups. See Linux → Hardware acceleration."},{"page":"troubleshooting/","title":"Troubleshooting","heading":"No notifications","anchor":"no-notifications","text":"- Check the notification settings on the device: the master switch, quiet hours, and whether the device is registered.\n- Check the camera's notification settings: which objects, sounds, and sensors it alerts on.\n- For push to a phone, you need the native mobile app (a browser gets no push), camera.ui Cloud set up, and a tap on Sync this device. If push went quiet, the app usually shows a note with a Fix button that sets the phone up again. See Getting push on your phone."},{"page":"troubleshooting/","title":"Troubleshooting","heading":"Recording isn't working","anchor":"recording-isnt-working","text":"- Recording needs an active subscription. See Recording.\n- Confirm the camera's recording mode is set, either continuous or event.\n- Check storage: a full disk with Strict retention pauses recording until you free space. Overwrite only pauses once other data fills the disk and no older recordings are left to delete. Recording also pauses while the storage path can't be reached.\n- If Settings → Recordings warns that the storage volume is small, recordings rotate away quickly. Either the disk really is that small, or a storage mount you expected never mounted: check the path in storage."},{"page":"troubleshooting/","title":"Troubleshooting","heading":"Can't reach the server remotely","anchor":"cant-reach-the-server-remotely","text":"- Open Settings → Remote Access and select the info button next to the connection status. Connection details names the address in use, what the server sees, and every address that was tried with its timing and failure reason. Copy details copies the lot for a bug report.\n- Confirm cloud access is enabled and the server is registered, with the tunnel connected.\n- For a direct connection, use Test connection to check the address is reachable.\n- The mobile apps need camera.ui Cloud to connect from outside your network.\n- Behind a login proxy such as Pangolin or Authelia, a browser stuck on Cannot reach your home server needs its site data for camera.ui cleared once, see Reverse proxy.\n- If the app connects over the internet while you're at home, check the Network card on the same page: an IP address missing from Server addresses, or a Local address that doesn't answer, sends the app out over the internet instead."},{"page":"troubleshooting/","title":"Troubleshooting","heading":"I can't sign in","anchor":"i-cant-sign-in","text":"If you know the account but not the password, another administrator can set a new one in Settings → Users (user menu → Edit).\n\nThe master account is the exception: no other administrator can change it. To reset it, create an empty file named reset-password in camera.ui's volume folder and restart camera.ui:\n\n- Docker. /data/volume in the container, for example docker exec touch /data/volume/reset-password.\n- Proxmox. The same inside the LXC: pct exec -- docker exec cameraui touch /data/volume/reset-password.\n- Linux. ~/.camera.ui/volume of the service user.\n- Desktop app. The folder under Where your data lives.\n- Home Assistant app. /data/volume inside the app. The sidebar panel signs in without a password, only the direct address on port 3443 asks for it.\n\nThe master then signs in with its username and the password admin and has to set a new password right away. Its sessions are signed out and two-factor authentication is off. The log names the account, and the file removes itself."},{"page":"troubleshooting/","title":"Troubleshooting","heading":"Getting more detail","anchor":"getting-more-detail","text":"- Open the Logs and filter or search for the issue. You can download the log to share when asking for help.\n- For deeper inspection, the Terminal gives a shell on the server."},{"page":"troubleshooting/","title":"Troubleshooting","heading":"Still stuck?","anchor":"still-stuck","text":"Ask the community on Discord or Reddit, or open an issue on GitHub. Include your logs and the steps that led to the problem."}]