@camera.ui/server 2.0.4 → 2.0.5

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 (281) hide show
  1. package/dist/interface/assets/{404-B8P9h05I.js → 404-DRRPa9Hr.js} +1 -1
  2. package/dist/interface/assets/About-Db5MifHo.js +1 -0
  3. package/dist/interface/assets/{Adminpanel-BTIcUxWm.js → Adminpanel-ByDZ8Xpn.js} +1 -1
  4. package/dist/interface/assets/{Automation-BNbTehB5.js → Automation-BqJ8PyPa.js} +2 -2
  5. package/dist/interface/assets/{AutomationImport-BEm0hc0d.js → AutomationImport-BvuLSZcZ.js} +2 -2
  6. package/dist/interface/assets/{AutomationStore-qLsO1tHb.js → AutomationStore-Ca07W8uH.js} +2 -2
  7. package/dist/interface/assets/{AutomationStoreDetail-BAW2nfPd.js → AutomationStoreDetail-DmFvT2az.js} +2 -2
  8. package/dist/interface/assets/{Automations-CUCVYZSr.js → Automations-Dr8K-cDz.js} +2 -2
  9. package/dist/interface/assets/{Camera-eIsFEdVo.js → Camera-Bu1U8Oal.js} +2 -2
  10. package/dist/interface/assets/{CameraConsole-t6nY7h-V.js → CameraConsole-BcvP76iJ.js} +1 -1
  11. package/dist/interface/assets/{Cameras-Bswv6ojJ.js → Cameras-Dwr5fbzg.js} +2 -2
  12. package/dist/interface/assets/{Camview-DMywoy_4.js → Camview-F0vVBWeZ.js} +1 -1
  13. package/dist/interface/assets/Config-CIAVylkc.js +2 -0
  14. package/dist/interface/assets/{ConfigActionCameraControl-C0hvC4QW.js → ConfigActionCameraControl-C1dsQnYF.js} +1 -1
  15. package/dist/interface/assets/{ConfigActionDelay-Rnxk--Qt.js → ConfigActionDelay-DTjjadCG.js} +1 -1
  16. package/dist/interface/assets/{ConfigActionHttp-BF8vrrHR.js → ConfigActionHttp-IFqZaHDZ.js} +1 -1
  17. package/dist/interface/assets/{ConfigActionImageInput-C8_H3ubM.js → ConfigActionImageInput-DY8RQJ2h.js} +1 -1
  18. package/dist/interface/assets/{ConfigActionNotification-4VYN5Hkr.js → ConfigActionNotification-CJL8uNR-.js} +1 -1
  19. package/dist/interface/assets/{ConfigActionOutput-CxB3eLLl.js → ConfigActionOutput-Butmz0jI.js} +1 -1
  20. package/dist/interface/assets/{ConfigActionPlugin-DrPmBv9V.js → ConfigActionPlugin-D5oE8oXm.js} +1 -1
  21. package/dist/interface/assets/{ConfigActionSensor-CyaEZRfx.js → ConfigActionSensor-CJ-mk6eS.js} +1 -1
  22. package/dist/interface/assets/{ConfigActionSnapshot-l7VvnulO.js → ConfigActionSnapshot-DCCiYu-s.js} +1 -1
  23. package/dist/interface/assets/{ConfigActionVariable-DIlUMHDy.js → ConfigActionVariable-F28sicqK.js} +1 -1
  24. package/dist/interface/assets/{ConfigConditionIfElse-0XRDQcTw.js → ConfigConditionIfElse-BJhRgP7t.js} +1 -1
  25. package/dist/interface/assets/{ConfigConditionSensorState-BvygRJ79.js → ConfigConditionSensorState-B6MdgPfo.js} +1 -1
  26. package/dist/interface/assets/{ConfigConditionSwitch-C7pp1EZI.js → ConfigConditionSwitch-BKToCaFK.js} +1 -1
  27. package/dist/interface/assets/{ConfigConditionTime-B0zctDc4.js → ConfigConditionTime-ChXeDbZB.js} +1 -1
  28. package/dist/interface/assets/{ConfigTriggerDetection-CKw-fo2n.js → ConfigTriggerDetection-BWd3QqYf.js} +1 -1
  29. package/dist/interface/assets/{ConfigTriggerGeofence-DjBRl2tE.js → ConfigTriggerGeofence-BoT9t-8V.js} +1 -1
  30. package/dist/interface/assets/{ConfigTriggerManual-BpBTh3CS.js → ConfigTriggerManual-Bt62nmi8.js} +1 -1
  31. package/dist/interface/assets/{ConfigTriggerSchedule-DZG10NcA.js → ConfigTriggerSchedule-oBDIqAMo.js} +1 -1
  32. package/dist/interface/assets/{ConfigTriggerSensor-CpFa6nUj.js → ConfigTriggerSensor-BlTHlstZ.js} +1 -1
  33. package/dist/interface/assets/{ConfigTriggerSystem-BsQElCAL.js → ConfigTriggerSystem--nEg_iQ0.js} +1 -1
  34. package/dist/interface/assets/{ConfigTriggerWebhook-BKPOGAs3.js → ConfigTriggerWebhook-83rLUOjQ.js} +1 -1
  35. package/dist/interface/assets/{Console-DHlNpv-R.js → Console-2sHljg8h.js} +1 -1
  36. package/dist/interface/assets/{CuiActionButton-BSy6VaJ6.js → CuiActionButton-CNjL01gn.js} +1 -1
  37. package/dist/interface/assets/{CuiAvatar-8AhiddF1.js → CuiAvatar-BDT9c-UI.js} +1 -1
  38. package/dist/interface/assets/{CuiBBoxPlayground-BUPzZ0sI.js → CuiBBoxPlayground-CMnBcPwI.js} +1 -1
  39. package/dist/interface/assets/CuiBottomSheet-WuxFsOrw.js +1 -0
  40. package/dist/interface/assets/{CuiBottomSheet-BNCym-11.js → CuiBottomSheet-h5sjwTf4.js} +1 -1
  41. package/dist/interface/assets/{CuiCameraCard-BGAQxNHs.js → CuiCameraCard-Bip9OIZO.js} +1 -1
  42. package/dist/interface/assets/{CuiCameraPipCard-CUPWEo3r.js → CuiCameraPipCard-DElQvAB0.js} +1 -1
  43. package/dist/interface/assets/{CuiCameraRecordings-B25441dH.js → CuiCameraRecordings-Bf5IG7OM.js} +1 -1
  44. package/dist/interface/assets/{CuiCameraShares-oA3ockIQ.js → CuiCameraShares-Cw-Mffg8.js} +1 -1
  45. package/dist/interface/assets/{CuiCameraSnapshot-BLM8v7rY.js → CuiCameraSnapshot-BS-_U34M.js} +1 -1
  46. package/dist/interface/assets/{CuiCameraTable-3joRnfiM.js → CuiCameraTable-DY_unr4m.js} +1 -1
  47. package/dist/interface/assets/{CuiDataTable-DhnQRMXG.js → CuiDataTable-C5qi7zpz.js} +1 -1
  48. package/dist/interface/assets/{CuiFloatingButton-YIEmTfno.js → CuiFloatingButton-Pmur2pAJ.js} +1 -1
  49. package/dist/interface/assets/{CuiImage-CzYVSrkC.js → CuiImage-DVoYtgtl.js} +1 -1
  50. package/dist/interface/assets/{CuiMenu-CEaJl-MF.js → CuiMenu-B7AhQbgB.js} +1 -1
  51. package/dist/interface/assets/{CuiPluginCard-De_cbBBe.js → CuiPluginCard-B5PhaGoF.js} +2 -2
  52. package/dist/interface/assets/{CuiPluginCategoryChip-BwL2qCTM.js → CuiPluginCategoryChip-nEwIiKiK.js} +1 -1
  53. package/dist/interface/assets/{CuiPluginOAuthButton-BmBW2Pdf.js → CuiPluginOAuthButton-CJGsl-89.js} +2 -2
  54. package/dist/interface/assets/{CuiPluginTrustBadge-KcOasEJH.js → CuiPluginTrustBadge-DExKttfC.js} +1 -1
  55. package/dist/interface/assets/{CuiQRCode-BUSzb3ek.js → CuiQRCode-DkRZ5hfS.js} +1 -1
  56. package/dist/interface/assets/{CuiRecordingsGrid-DHSABYJb.js → CuiRecordingsGrid-mFXSsuG8.js} +1 -1
  57. package/dist/interface/assets/{CuiSchema-D35n-DE5.js → CuiSchema-BSv1nUun.js} +1 -1
  58. package/dist/interface/assets/{CuiTopNavbarItem-DVwItfbG.js → CuiTopNavbarItem-w21c3xtQ.js} +1 -1
  59. package/dist/interface/assets/{CuiTopbarSlot-h5_1cb9v.js → CuiTopbarSlot-CYPUqVPx.js} +1 -1
  60. package/dist/interface/assets/{Faces-BJlHL3gp.js → Faces-DLpVsIkY.js} +1 -1
  61. package/dist/interface/assets/{FirstSteps-D1Zf1CYN.js → FirstSteps-BDDy9x_6.js} +1 -1
  62. package/dist/interface/assets/{Instances-BNasTbK_.js → Instances-FeIAnJix.js} +1 -1
  63. package/dist/interface/assets/{Logs-CW5EqpAL.js → Logs-CIUyuGwj.js} +1 -1
  64. package/dist/interface/assets/{Menu-DTC3IWLc.js → Menu-Gqs2UiSW.js} +1 -1
  65. package/dist/interface/assets/{OAuthDeviceFlow-BOIY4kBt.js → OAuthDeviceFlow-hNJ_UrdG.js} +1 -1
  66. package/dist/interface/assets/{Plugin-DIPGTrNi.js → Plugin-BKItJas0.js} +1 -1
  67. package/dist/interface/assets/{PluginConsole-DwNRvKqW.js → PluginConsole-Bwd5Mktk.js} +1 -1
  68. package/dist/interface/assets/PluginDetail-BBh5y9qU.js +2 -0
  69. package/dist/interface/assets/{PluginSchema-DWnYqRll.js → PluginSchema-D92qcqf9.js} +1 -1
  70. package/dist/interface/assets/PluginSearch-BN3U3NY6.js +2 -0
  71. package/dist/interface/assets/PluginSearch-nHAFKMX0.js +1 -0
  72. package/dist/interface/assets/Plugins-gxyxkQ5i.js +1 -0
  73. package/dist/interface/assets/Recordings-B3Xw23vh.js +1 -0
  74. package/dist/interface/assets/{Settings-CfiNEl95.js → Settings-D2LHMohy2.js} +1 -1
  75. package/dist/interface/assets/SettingsAccount-DUN1oTTn.js +1 -0
  76. package/dist/interface/assets/{SettingsAccount-Ch2SR80X.js → SettingsAccount-b4eVxI3M.js} +1 -1
  77. package/dist/interface/assets/{SettingsAppearance-Ck4VcB-w.js → SettingsAppearance--Eidr-fz.js} +1 -1
  78. package/dist/interface/assets/SettingsAppearance-CLVMmnR_.js +1 -0
  79. package/dist/interface/assets/{SettingsBackup-BigThNuk.js → SettingsBackup-DUYVdKjD.js} +1 -1
  80. package/dist/interface/assets/{SettingsNotifications-XLBjwgwX.js → SettingsNotifications-xYOuLZsr.js} +1 -1
  81. package/dist/interface/assets/SettingsRecordings-BQDf4Zeg.js +1 -0
  82. package/dist/interface/assets/{SettingsRemote-B5jm7MdB.js → SettingsRemote-BXr1HLHm.js} +1 -1
  83. package/dist/interface/assets/SettingsSystem-DAjNwp4I.js +2 -0
  84. package/dist/interface/assets/{SettingsUsers-QuUercEC.js → SettingsUsers-DJ-eMMWe.js} +1 -1
  85. package/dist/interface/assets/{Terminal-BTfYdYuv.js → Terminal-P3yVGG8s.js} +1 -1
  86. package/dist/interface/assets/{VariableInput-DmE1m9Il.js → VariableInput-DdVw4SIR.js} +1 -1
  87. package/dist/interface/assets/{VariableSuggestions-eII-1Bd_.js → VariableSuggestions-SJPd5c8d.js} +1 -1
  88. package/dist/interface/assets/VersionsHandler-DcZ3RtHV.js +7 -0
  89. package/dist/interface/assets/{Workers-Bj_tLx-1.js → Workers-DWSJsRko.js} +1 -1
  90. package/dist/interface/assets/api-CKqAhmSC.css +2 -0
  91. package/dist/interface/assets/{api-CltBUyci.js → api-Cv3vt1pa.js} +1 -1
  92. package/dist/interface/assets/api-Ex2VUVD1.js +2 -0
  93. package/dist/interface/assets/{auth-BiWutL3Z.js → auth-W-JA6GOD.js} +1 -1
  94. package/dist/interface/assets/{automations-hsi5MCtT.js → automations-d_B5L3sO.js} +1 -1
  95. package/dist/interface/assets/{automations-73X55Wxy.js → automations-irBgx9Ew.js} +1 -1
  96. package/dist/interface/assets/{backup-CHJIDGvl.js → backup-BT-P0duG.js} +1 -1
  97. package/dist/interface/assets/{cameras-Crvf7J3t.js → cameras-DxjpuzW1.js} +1 -1
  98. package/dist/interface/assets/{cameras.schema-CQPF4BCK.js → cameras.schema-7ex1wBck.js} +1 -1
  99. package/dist/interface/assets/cloudHandoff-Cu1sRatW.js +1 -0
  100. package/dist/interface/assets/config-DwlSsbcP.js +1 -0
  101. package/dist/interface/assets/{deviceId-BJm8lmpp.js → deviceId-I24oaPe6.js} +1 -1
  102. package/dist/interface/assets/{hasPermission-LUHh3i2_.js → hasPermission-Cz0dkfyp.js} +1 -1
  103. package/dist/interface/assets/{instances-A-hAMJRC.js → instances-D6wHW9_0.js} +1 -1
  104. package/dist/interface/assets/main-C-WCGnJh.js +3 -0
  105. package/dist/interface/assets/{notifications-eX5p2YaS.js → notifications-DLNUbEKC.js} +1 -1
  106. package/dist/interface/assets/{plugins-DfuM5k5Q.js → plugins-D-tGC0mr.js} +1 -1
  107. package/dist/interface/assets/{round-restart-alt-rDMgoX7-.js → round-restart-alt-CaOjUTav.js} +1 -1
  108. package/dist/interface/assets/router-Cbt2STjH.js +2 -0
  109. package/dist/interface/assets/{routerState-DxipV4mY.js → routerState-_BXrxePY.js} +1 -1
  110. package/dist/interface/assets/{server-CoQ6jFok.js → server-ACfR-bOw.js} +1 -1
  111. package/dist/interface/assets/{shares-VWzmq1V-.js → shares-Ut3rTZUx.js} +1 -1
  112. package/dist/interface/assets/{theme-BzXEv5Er.js → theme-CDU91XW5.js} +1 -1
  113. package/dist/interface/assets/{types-CAW47AJe.js → types-Blh_1COV.js} +1 -1
  114. package/dist/interface/assets/{ui-Dg-QtLMl.js → ui-SZnjYe2F.js} +1 -1
  115. package/dist/interface/assets/useAppVersion-Bo_n0OVQ.js +1 -0
  116. package/dist/interface/assets/{useBiometricCredentials-Buylkybs.js → useBiometricCredentials-XyEQbPBo.js} +1 -1
  117. package/dist/interface/assets/{useCameraOptions-GAeXCTvO.js → useCameraOptions-C3-ZVV2A.js} +1 -1
  118. package/dist/interface/assets/{useCuiCameraDrawer-DwlhaZ2i.js → useCuiCameraDrawer-CebQFNWX.js} +1 -1
  119. package/dist/interface/assets/useCuiDialog-ClphnG79.js +2 -0
  120. package/dist/interface/assets/{useFileDownload-Bhdbhy_2.js → useFileDownload-CdDprX3v.js} +1 -1
  121. package/dist/interface/assets/{useLogsSocket-81hTPkwS.js → useLogsSocket-DKU0kKe_.js} +1 -1
  122. package/dist/interface/assets/{useServerRestart-3pT0h7CH.js → useServerRestart-CgCmTap7.js} +1 -1
  123. package/dist/interface/assets/{useSharedComposables-Ki01kFNJ.js → useSharedComposables-BcUR_c17.js} +1 -1
  124. package/dist/interface/assets/{useStreamStatus-CHITNYE7.js → useStreamStatus-BTa-XXZJ.js} +1 -1
  125. package/dist/interface/assets/useUpdateChannel-WznJBL9L.js +1 -0
  126. package/dist/interface/assets/{useUserOptions-CUvE4OL9.js → useUserOptions-DffxmIf7.js} +1 -1
  127. package/dist/interface/assets/{users.schema-DWL8-tZN.js → users.schema-BcwArBbH.js} +1 -1
  128. package/dist/interface/assets/{vendor-cameraui-DhctzD3V.js → vendor-cameraui-zHth8Pa5.js} +2 -2
  129. package/dist/interface/assets/{vendor-chart-BjXPY6za.js → vendor-chart-CTk1N5fJ.js} +1 -1
  130. package/dist/interface/assets/{vendor-core-DnC_SdtD.js → vendor-core-_UeC1YMQ.js} +1 -1
  131. package/dist/interface/assets/{vendor-primevue-OV61z06m.js → vendor-primevue-BXMgwPEu.js} +1 -1
  132. package/dist/interface/assets/{view-grid-_whxsqcn.js → view-grid-CX0RwdlE.js} +1 -1
  133. package/dist/interface/index.html +44 -43
  134. package/dist/interface/sw.js +1 -1
  135. package/dist/server/package.json +26 -16
  136. package/dist/server/requirements.txt +3 -2
  137. package/dist/server/src/_camera_ui_tools/camera_ui_common/common_utils/signal_handler.py +17 -9
  138. package/dist/server/src/_camera_ui_tools/camera_ui_sdk/camera/detection.py +10 -0
  139. package/dist/server/src/_camera_ui_tools/camera_ui_sdk/internal/event_emitter.py +45 -0
  140. package/dist/server/src/_camera_ui_tools/camera_ui_sdk/storage/__init__.py +47 -12
  141. package/dist/server/src/api/controllers/plugins.controller.js +11 -3
  142. package/dist/server/src/api/controllers/plugins.controller.js.map +1 -1
  143. package/dist/server/src/api/schemas/cameras.schema.d.ts +27 -0
  144. package/dist/server/src/api/schemas/cameras.schema.js +6 -0
  145. package/dist/server/src/api/schemas/cameras.schema.js.map +1 -1
  146. package/dist/server/src/api/schemas/config.schema.d.ts +2 -0
  147. package/dist/server/src/api/schemas/config.schema.js +1 -0
  148. package/dist/server/src/api/schemas/config.schema.js.map +1 -1
  149. package/dist/server/src/api/services/plugins.service.js +1 -1
  150. package/dist/server/src/api/services/plugins.service.js.map +1 -1
  151. package/dist/server/src/api.d.ts +1 -0
  152. package/dist/server/src/api.js +11 -3
  153. package/dist/server/src/api.js.map +1 -1
  154. package/dist/server/src/camera/decoder/ptz-autotracker.js +51 -78
  155. package/dist/server/src/camera/decoder/ptz-autotracker.js.map +1 -1
  156. package/dist/server/src/go2rtc/api/routes/streams.js +5 -6
  157. package/dist/server/src/go2rtc/api/routes/streams.js.map +1 -1
  158. package/dist/server/src/go2rtc/index.js +1 -0
  159. package/dist/server/src/go2rtc/index.js.map +1 -1
  160. package/dist/server/src/go2rtc/types.d.ts +3 -0
  161. package/dist/server/src/go2rtc/types.js.map +1 -1
  162. package/dist/server/src/main.js +1 -1
  163. package/dist/server/src/main.js.map +1 -1
  164. package/dist/server/src/manager/deviceManager.js +4 -1
  165. package/dist/server/src/manager/deviceManager.js.map +1 -1
  166. package/dist/server/src/manager/discoveryManager.d.ts +3 -2
  167. package/dist/server/src/manager/discoveryManager.js +58 -29
  168. package/dist/server/src/manager/discoveryManager.js.map +1 -1
  169. package/dist/server/src/plugins/config-store.d.ts +1 -1
  170. package/dist/server/src/plugins/config-store.js +17 -10
  171. package/dist/server/src/plugins/config-store.js.map +1 -1
  172. package/dist/server/src/plugins/index.d.ts +1 -0
  173. package/dist/server/src/plugins/index.js +38 -2
  174. package/dist/server/src/plugins/index.js.map +1 -1
  175. package/dist/server/src/plugins/runtime/base.js +10 -5
  176. package/dist/server/src/plugins/runtime/base.js.map +1 -1
  177. package/dist/server/src/plugins/runtime/go/index.js +1 -0
  178. package/dist/server/src/plugins/runtime/go/index.js.map +1 -1
  179. package/dist/server/src/plugins/runtime/node/child.d.ts +1 -0
  180. package/dist/server/src/plugins/runtime/node/child.js +62 -19
  181. package/dist/server/src/plugins/runtime/node/child.js.map +1 -1
  182. package/dist/server/src/plugins/runtime/node/configDb.d.ts +8 -0
  183. package/dist/server/src/plugins/runtime/node/configDb.js +15 -0
  184. package/dist/server/src/plugins/runtime/node/configDb.js.map +1 -1
  185. package/dist/server/src/plugins/runtime/node/pluginApi.d.ts +4 -0
  186. package/dist/server/src/plugins/runtime/node/pluginApi.js +13 -5
  187. package/dist/server/src/plugins/runtime/node/pluginApi.js.map +1 -1
  188. package/dist/server/src/plugins/runtime/node/proxy/cameraDevice.d.ts +1 -2
  189. package/dist/server/src/plugins/runtime/node/proxy/cameraDevice.js +2 -8
  190. package/dist/server/src/plugins/runtime/node/proxy/cameraDevice.js.map +1 -1
  191. package/dist/server/src/plugins/runtime/node/proxy/coreManager.d.ts +3 -2
  192. package/dist/server/src/plugins/runtime/node/proxy/coreManager.js +9 -13
  193. package/dist/server/src/plugins/runtime/node/proxy/coreManager.js.map +1 -1
  194. package/dist/server/src/plugins/runtime/node/proxy/deviceManager.d.ts +3 -2
  195. package/dist/server/src/plugins/runtime/node/proxy/deviceManager.js +14 -12
  196. package/dist/server/src/plugins/runtime/node/proxy/deviceManager.js.map +1 -1
  197. package/dist/server/src/plugins/runtime/node/proxy/fileServe.js +4 -9
  198. package/dist/server/src/plugins/runtime/node/proxy/fileServe.js.map +1 -1
  199. package/dist/server/src/plugins/runtime/node/proxy/notificationManager.js +3 -0
  200. package/dist/server/src/plugins/runtime/node/proxy/notificationManager.js.map +1 -1
  201. package/dist/server/src/plugins/runtime/node/storage.d.ts +5 -2
  202. package/dist/server/src/plugins/runtime/node/storage.js +92 -67
  203. package/dist/server/src/plugins/runtime/node/storage.js.map +1 -1
  204. package/dist/server/src/plugins/runtime/node/storageController.d.ts +2 -0
  205. package/dist/server/src/plugins/runtime/node/storageController.js +12 -4
  206. package/dist/server/src/plugins/runtime/node/storageController.js.map +1 -1
  207. package/dist/server/src/plugins/runtime/python/child.py +47 -38
  208. package/dist/server/src/plugins/runtime/python/config_db.py +28 -17
  209. package/dist/server/src/plugins/runtime/python/index.js +1 -0
  210. package/dist/server/src/plugins/runtime/python/index.js.map +1 -1
  211. package/dist/server/src/plugins/runtime/python/plugin_api.py +5 -5
  212. package/dist/server/src/plugins/runtime/python/proxy/camera_device.py +14 -13
  213. package/dist/server/src/plugins/runtime/python/proxy/core_manager.py +11 -14
  214. package/dist/server/src/plugins/runtime/python/proxy/device_manager.py +23 -18
  215. package/dist/server/src/plugins/runtime/python/remote_urls.py +3 -7
  216. package/dist/server/src/plugins/runtime/python/storage.py +157 -126
  217. package/dist/server/src/plugins/runtime/python/storage_controller.py +28 -13
  218. package/dist/server/src/plugins/runtime/python/store.py +513 -0
  219. package/dist/server/src/plugins/runtime/python/store_migrate.py +59 -0
  220. package/dist/server/src/plugins/store/coalescingWriter.d.ts +7 -0
  221. package/dist/server/src/plugins/store/coalescingWriter.js +52 -0
  222. package/dist/server/src/plugins/store/coalescingWriter.js.map +1 -0
  223. package/dist/server/src/plugins/store/envelope.d.ts +5 -0
  224. package/dist/server/src/plugins/store/envelope.js +40 -0
  225. package/dist/server/src/plugins/store/envelope.js.map +1 -0
  226. package/dist/server/src/plugins/store/layout.d.ts +2 -0
  227. package/dist/server/src/plugins/store/layout.js +60 -0
  228. package/dist/server/src/plugins/store/layout.js.map +1 -0
  229. package/dist/server/src/plugins/store/location.d.ts +14 -0
  230. package/dist/server/src/plugins/store/location.js +55 -0
  231. package/dist/server/src/plugins/store/location.js.map +1 -0
  232. package/dist/server/src/plugins/store/migrate.d.ts +1 -0
  233. package/dist/server/src/plugins/store/migrate.js +22 -0
  234. package/dist/server/src/plugins/store/migrate.js.map +1 -0
  235. package/dist/server/src/plugins/store/pluginStoreFile.d.ts +14 -0
  236. package/dist/server/src/plugins/store/pluginStoreFile.js +58 -0
  237. package/dist/server/src/plugins/store/pluginStoreFile.js.map +1 -0
  238. package/dist/server/src/plugins/store/storeFile.d.ts +5 -0
  239. package/dist/server/src/plugins/store/storeFile.js +112 -0
  240. package/dist/server/src/plugins/store/storeFile.js.map +1 -0
  241. package/dist/server/src/plugins/store/validate.d.ts +1 -0
  242. package/dist/server/src/plugins/store/validate.js +49 -0
  243. package/dist/server/src/plugins/store/validate.js.map +1 -0
  244. package/dist/server/src/plugins/worker.js +9 -8
  245. package/dist/server/src/plugins/worker.js.map +1 -1
  246. package/dist/server/src/remote/services/cloudflare-managed.js +2 -2
  247. package/dist/server/src/remote/services/cloudflare-managed.js.map +1 -1
  248. package/dist/server/src/remote/services/cloudflare.js +1 -0
  249. package/dist/server/src/remote/services/cloudflare.js.map +1 -1
  250. package/dist/server/src/remote/tunnel/client.js +1 -0
  251. package/dist/server/src/remote/tunnel/client.js.map +1 -1
  252. package/dist/server/src/rpc/server.js +12 -5
  253. package/dist/server/src/rpc/server.js.map +1 -1
  254. package/dist/server/src/services/config/defaults.js +1 -0
  255. package/dist/server/src/services/config/defaults.js.map +1 -1
  256. package/dist/server/src/services/config/types.d.ts +1 -0
  257. package/dist/server/src/utils/npm/index.d.ts +1 -1
  258. package/dist/server/src/utils/npm/index.js +7 -4
  259. package/dist/server/src/utils/npm/index.js.map +1 -1
  260. package/dist/server/src/workers/capabilities/plugin-host.js +1 -1
  261. package/dist/server/src/workers/capabilities/plugin-host.js.map +1 -1
  262. package/package.json +26 -16
  263. package/dist/interface/assets/About-CjQs3rZw.js +0 -1
  264. package/dist/interface/assets/Config-C7H5amm4.js +0 -2
  265. package/dist/interface/assets/CuiBottomSheet-ChG_av5z.js +0 -1
  266. package/dist/interface/assets/PluginDetail-D1TJZJhY.js +0 -2
  267. package/dist/interface/assets/PluginSearch-C5bkQgYA.js +0 -1
  268. package/dist/interface/assets/PluginSearch-DqwgO886.js +0 -2
  269. package/dist/interface/assets/Plugins-o7IfSFjd.js +0 -1
  270. package/dist/interface/assets/Recordings-DTw5IG8W.js +0 -1
  271. package/dist/interface/assets/SettingsAccount-DIwWUyp6.js +0 -1
  272. package/dist/interface/assets/SettingsAppearance-CkF7biyP.js +0 -1
  273. package/dist/interface/assets/SettingsRecordings-Dp9tJdHk.js +0 -1
  274. package/dist/interface/assets/SettingsSystem-Isy5hiDl.js +0 -2
  275. package/dist/interface/assets/VersionsHandler-BYj7-lpw.js +0 -7
  276. package/dist/interface/assets/api-D7tGlI-z.css +0 -2
  277. package/dist/interface/assets/api-DoPgGqHU.js +0 -2
  278. package/dist/interface/assets/cloudHandoff-lLBGqrXl.js +0 -1
  279. package/dist/interface/assets/main-DEbfQmxF.js +0 -3
  280. package/dist/interface/assets/router-D1e_o7wa.js +0 -2
  281. package/dist/interface/assets/useCuiDialog-yRn5B1oO.js +0 -2
@@ -1,26 +1,16 @@
1
+ from __future__ import annotations
2
+
1
3
  import asyncio
2
4
  import inspect
3
- from collections.abc import Awaitable, Callable
4
- from contextlib import suppress
5
5
  from copy import deepcopy
6
- from typing import Any, Literal, cast
6
+ from typing import TYPE_CHECKING, Any, ClassVar, cast
7
7
 
8
8
  from deepdiff.diff import DeepDiff
9
- from plugins.runtime.python.config_db import PluginConfigDb
10
9
 
11
10
  from _camera_ui_tools.camera_ui_common import ObjectPath, merge, merge_with
12
- from _camera_ui_tools.camera_ui_rpc import CloseHandler, RPCClient, RPCMethod
13
- from _camera_ui_tools.camera_ui_sdk import (
14
- API_EVENT,
15
- FormSubmitResponse,
16
- JsonSchema,
17
- PluginAPI,
18
- SchemaConfig,
19
- )
11
+ from _camera_ui_tools.camera_ui_rpc import RPCMethod
20
12
  from _camera_ui_tools.camera_ui_sdk import DeviceStorage as DeviceStorageInterface
21
- from plugins.runtime.python.namespaces import (
22
- NamespaceManager,
23
- )
13
+ from plugins.runtime.python.namespaces import NamespaceManager
24
14
  from plugins.runtime.python.schema import (
25
15
  generate_config_from_schemas,
26
16
  get_value_by_key,
@@ -28,13 +18,28 @@ from plugins.runtime.python.schema import (
28
18
  is_submit_type,
29
19
  remove_callbacks_from_schemas,
30
20
  )
31
- from plugins.runtime.python.typings import PluginInfo
21
+ from plugins.runtime.python.store import (
22
+ PluginLocation,
23
+ SensorLocation,
24
+ delete_location,
25
+ read_location,
26
+ validate_store_value,
27
+ write_location,
28
+ )
32
29
 
33
- ConfigDict = dict[str, Any]
30
+ if TYPE_CHECKING:
31
+ from _camera_ui_tools.camera_ui_rpc import CloseHandler, RPCClient
32
+ from _camera_ui_tools.camera_ui_sdk import (
33
+ FormSubmitResponse,
34
+ JsonSchema,
35
+ PluginAPI,
36
+ SchemaConfig,
37
+ )
38
+ from plugins.runtime.python.config_db import PluginConfigDb
39
+ from plugins.runtime.python.store import StoreLocation
40
+ from plugins.runtime.python.typings import PluginInfo
34
41
 
35
- OnSetCallback = Callable[[Any, Any], Awaitable[None] | None]
36
- OnGetCallback = Callable[[], Awaitable[Any] | Any]
37
- OnClickCallback = Callable[[Any], Awaitable[FormSubmitResponse | None] | FormSubmitResponse | None]
42
+ ConfigDict = dict[str, Any]
38
43
 
39
44
 
40
45
  def merge2(
@@ -53,17 +58,19 @@ def merge2(
53
58
 
54
59
 
55
60
  class DeviceStorage(DeviceStorageInterface):
61
+ # Strong refs to detached onSet tasks: the event loop only holds weak
62
+ # references, so an unreferenced task could be garbage-collected mid-flight.
63
+ __detached_tasks: ClassVar[set[asyncio.Task[None]]] = set()
64
+
56
65
  def __init__(
57
66
  self,
58
67
  api: PluginAPI,
59
68
  proxy: RPCClient,
60
69
  plugin: PluginInfo,
61
70
  plugin_db: PluginConfigDb,
62
- device_id: str,
71
+ location: StoreLocation,
63
72
  schemas: list[JsonSchema] | None = [],
64
- is_plugin_storage: bool = False,
65
73
  sensor_id: str | None = None,
66
- camera_id: str | None = None,
67
74
  ) -> None:
68
75
  if schemas is None:
69
76
  schemas = []
@@ -75,32 +82,31 @@ class DeviceStorage(DeviceStorageInterface):
75
82
  self.__proxy = proxy
76
83
  self.__plugin = plugin
77
84
  self.__plugin_db = plugin_db
78
- self.__device_id = device_id
79
- self.__is_plugin_storage = is_plugin_storage
85
+ self.__location = location
80
86
  self.__sensor_id = sensor_id
81
- self.__camera_id = camera_id
82
87
  self.__close_proxy: CloseHandler | None = None
88
+ self.__dirty = False
83
89
 
84
- if self.__sensor_id is not None and self.__camera_id is not None:
90
+ if isinstance(location, SensorLocation):
91
+ if not self.__sensor_id:
92
+ raise ValueError(f"sensor storage for {plugin['id']} requires a sensor_id")
85
93
  sensor_ns = NamespaceManager.plugin_sensor_namespaces(
86
- plugin["id"], self.__camera_id, self.__sensor_id
94
+ plugin["id"], location.camera_id, self.__sensor_id
87
95
  )
88
96
  self.__storage_namespace = sensor_ns.sensor_storage_rpc
89
- elif self.__is_plugin_storage:
97
+ elif isinstance(location, PluginLocation):
90
98
  plugin_ns = NamespaceManager.plugin_namespaces(plugin["id"])
91
99
  self.__storage_namespace = plugin_ns.plugin_storage_rpc
92
100
  else:
93
- camera_ns = NamespaceManager.plugin_camera_namespaces(plugin["id"], self.__device_id)
101
+ camera_ns = NamespaceManager.plugin_camera_namespaces(plugin["id"], location.camera_id)
94
102
  self.__storage_namespace = camera_ns.camera_storage_rpc
95
103
 
96
- self.__api.once(API_EVENT.SHUTDOWN, self.__close)
97
-
98
104
  @RPCMethod
99
105
  async def getValue(self, key: str, default_value: Any | None = None) -> Any:
100
106
  schema: Any = next((schema for schema in self.schemas if schema["key"] == key), None)
101
107
  config_value = ObjectPath.get(self.values, key)
102
108
  schema_default_value = (
103
- schema["defaultValue"] if "defaultValue" in schema and schema["defaultValue"] else None
109
+ schema["defaultValue"] if schema and "defaultValue" in schema and schema["defaultValue"] else None
104
110
  )
105
111
 
106
112
  if schema:
@@ -132,23 +138,24 @@ class DeviceStorage(DeviceStorageInterface):
132
138
  schema: Any = next((schema for schema in self.schemas if schema["key"] == key), None)
133
139
 
134
140
  if schema:
141
+ validate_store_value(key, new_value)
135
142
  old_value = ObjectPath.get(self.values, key)
136
- ObjectPath.set(self.values, key, new_value)
137
-
138
- on_set_function = schema.get("onSet", None)
139
- if on_set_function:
140
- if inspect.iscoroutinefunction(on_set_function):
141
- await on_set_function(new_value, old_value)
142
- else:
143
- res = on_set_function(new_value, old_value)
144
-
145
- if inspect.iscoroutine(res):
146
- await res
147
- else:
148
- on_set_function(new_value, old_value)
149
-
150
- if self.__contains_storable_schema(schema):
151
- self.save()
143
+ if new_value is None:
144
+ unchanged = old_value is None
145
+ ObjectPath.delete(self.values, key)
146
+ else:
147
+ unchanged = old_value is not None and DeepDiff(old_value, new_value) == {}
148
+ stored = ( # pyright: ignore[reportUnknownVariableType]
149
+ deepcopy(new_value) # pyright: ignore[reportUnknownVariableType, reportUnknownArgumentType]
150
+ if isinstance(new_value, (dict, list))
151
+ else new_value
152
+ )
153
+ ObjectPath.set(self.values, key, stored)
154
+
155
+ if self.__contains_storable_schema(schema) and (not unchanged or self.__dirty):
156
+ await self.save()
157
+
158
+ self.__run_on_set_detached(schema.get("onSet", None), key, new_value, old_value)
152
159
 
153
160
  @RPCMethod
154
161
  async def submitValue(self, key: str, new_value: Any) -> FormSubmitResponse | None:
@@ -170,9 +177,20 @@ class DeviceStorage(DeviceStorageInterface):
170
177
  return None
171
178
 
172
179
  @RPCMethod
173
- def setInternalValue(self, key: str, value: Any) -> None:
174
- ObjectPath.set(self.values, key, value)
175
- self.save()
180
+ async def setInternalValue(self, key: str, value: Any) -> None: # type: ignore[override]
181
+ validate_store_value(key, value)
182
+ old_value = ObjectPath.get(self.values, key)
183
+ if value is None:
184
+ unchanged = old_value is None
185
+ ObjectPath.delete(self.values, key)
186
+ else:
187
+ unchanged = old_value is not None and DeepDiff(old_value, value) == {}
188
+ stored = deepcopy(value) if isinstance(value, (dict, list)) else value # type: ignore
189
+ ObjectPath.set(self.values, key, stored)
190
+
191
+ if unchanged and not self.__dirty:
192
+ return
193
+ await self.save()
176
194
 
177
195
  @RPCMethod
178
196
  def hasValue(self, key: str) -> bool:
@@ -188,52 +206,67 @@ class DeviceStorage(DeviceStorageInterface):
188
206
 
189
207
  @RPCMethod
190
208
  async def setConfig(self, new_config: dict[str, Any]) -> None:
209
+ validate_store_value("config", new_config)
191
210
  old_config = deepcopy(self.values)
192
211
  self.values = merge_with(self.values, new_config, merge)
193
212
 
194
- await self.__trigger_on_set_for_changes(old_config, self.values)
195
- self.save()
213
+ await self.save()
214
+ self.__trigger_on_set_for_changes(old_config, self.values)
196
215
 
197
216
  @RPCMethod
198
217
  async def addSchema(self, schema: JsonSchema) -> None:
199
- should_save = False
200
-
201
- schema_exist = next((schema for schema in self.schemas if schema["key"] == schema["key"]), None)
202
-
203
- if schema_exist:
218
+ if self.hasSchema(schema["key"]):
204
219
  raise Exception(f"Schema with key {schema['key']} already exists")
205
- else:
206
- self.schemas.append(schema)
207
220
 
208
- await self.__resolve_on_get_functions(schema)
221
+ self.schemas.append(schema)
209
222
 
210
- if self.__contains_storable_schema(schema):
211
- should_save = True
223
+ old_value = ObjectPath.get(self.values, schema["key"])
224
+ await self.__resolve_on_get_functions(schema)
212
225
 
213
- if should_save:
214
- self.save()
226
+ new_value = ObjectPath.get(self.values, schema["key"])
227
+ if (
228
+ self.__contains_storable_schema(schema)
229
+ and DeepDiff(old_value, new_value, ignore_order=True) != {}
230
+ ):
231
+ await self.save()
215
232
 
216
233
  @RPCMethod
217
- def removeSchema(self, key: str) -> None:
234
+ async def removeSchema(self, key: str) -> None: # type: ignore[override]
218
235
  schema = next((schema for schema in self.schemas if schema["key"] == key), None)
236
+ had_value = ObjectPath.get(self.values, key) is not None
219
237
  self.schemas = [s for s in self.schemas if s["key"] != key]
220
238
  ObjectPath.delete(self.values, key)
221
239
 
222
- if schema and self.__contains_storable_schema(schema):
223
- self.save()
240
+ if schema and self.__contains_storable_schema(schema) and had_value:
241
+ await self.save()
224
242
 
225
243
  @RPCMethod
226
244
  async def changeSchema(self, key: str, new_schema: dict[str, Any]) -> None:
227
245
  new_schema["key"] = key
228
- schema = next((schema for schema in self.schemas if schema["key"] == new_schema["key"]), None)
246
+ index = next(
247
+ (i for i, schema in enumerate(self.schemas) if schema["key"] == key),
248
+ None,
249
+ )
250
+ if index is None:
251
+ return
229
252
 
230
- if schema:
231
- merge_with(schema, new_schema, merge)
253
+ was_storable = self.__contains_storable_schema(self.schemas[index])
254
+ self.schemas[index] = cast(JsonSchema, new_schema)
232
255
 
233
- await self.__resolve_on_get_functions(schema)
256
+ # Storable-ness flipped: the next write must persist even if the
257
+ # value compares unchanged, otherwise the flip never becomes durable.
258
+ if self.__contains_storable_schema(self.schemas[index]) != was_storable:
259
+ self.__dirty = True
234
260
 
235
- if self.__contains_storable_schema(schema):
236
- self.save()
261
+ old_value = ObjectPath.get(self.values, key)
262
+ await self.__resolve_on_get_functions(self.schemas[index])
263
+
264
+ new_value = ObjectPath.get(self.values, key)
265
+ if (
266
+ self.__contains_storable_schema(self.schemas[index])
267
+ and DeepDiff(old_value, new_value, ignore_order=True) != {}
268
+ ):
269
+ await self.save()
237
270
 
238
271
  @RPCMethod
239
272
  def getSchema(self, key: str) -> JsonSchema | None:
@@ -245,42 +278,50 @@ class DeviceStorage(DeviceStorageInterface):
245
278
 
246
279
  @RPCMethod
247
280
  async def destroy(self) -> None:
248
- self.__api.removeListener(API_EVENT.SHUTDOWN, self.__close)
249
- config: Any = cast(dict[str, Any], self.__plugin_db.get("config") or {})
250
- if self.__device_id in config:
251
- del config[self.__device_id]
252
- self.values = {}
253
- self.__plugin_db.update({"config": config})
281
+ config = self.__plugin_db.get("config") or {}
282
+ delete_location(config, self.__location)
283
+ self.values = {}
284
+ await self.__plugin_db.put("config", config)
254
285
 
255
- def save(self) -> None:
256
- config: Any = cast(dict[str, Any], self.__plugin_db.get("config") or {})
286
+ async def save(self) -> None: # type: ignore[override]
287
+ config = self.__plugin_db.get("config") or {}
257
288
 
258
289
  if len(self.schemas) > 0:
259
290
  storable_config = self.__filter_storable_values(self.schemas)
260
291
  else:
261
292
  storable_config = self.values
262
293
 
263
- config[self.__device_id] = storable_config
264
- self.__plugin_db.update({"config": config})
294
+ write_location(config, self.__location, storable_config)
295
+ try:
296
+ await self.__plugin_db.put("config", config)
297
+ except Exception:
298
+ self.__dirty = True
299
+ raise
300
+ self.__dirty = False
265
301
 
266
302
  def defineSchemas(self, schemas: list[JsonSchema]) -> None:
267
303
  self.schemas = schemas
268
304
  self.__initializeStorage()
269
305
 
270
- # Internal method
271
306
  def update_schema(self, schemas: list[JsonSchema] = []) -> None:
272
307
  self.schemas = schemas
273
308
  self.__initializeStorage()
274
309
 
275
- # Internal method
276
310
  async def register_storage(self) -> None:
277
311
  self.__close_proxy = await self.__proxy.register_handler(self.__storage_namespace, self)
278
312
 
279
- # Internal method
280
313
  async def unregister_storage(self) -> None:
281
314
  if self.__close_proxy:
282
315
  await self.__close_proxy()
283
316
 
317
+ # Internal method to close the storage
318
+ async def close(self) -> None:
319
+ try:
320
+ await self.save()
321
+ await self.unregister_storage()
322
+ except Exception as error:
323
+ cast(Any, self.__api).logger.error(f"store: close save failed: {error}")
324
+
284
325
  async def __resolve_on_get_functions(
285
326
  self,
286
327
  schemas: JsonSchema | list[JsonSchema],
@@ -294,46 +335,40 @@ class DeviceStorage(DeviceStorageInterface):
294
335
  if schema_value is not None:
295
336
  ObjectPath.set(self.values, schema["key"], schema_value)
296
337
 
297
- async def __trigger_on_set_for_changes(
298
- self, old_config: dict[str, Any], new_config: dict[str, Any]
299
- ) -> None:
300
- tasks: list[Awaitable[None | Any]] = []
301
-
338
+ def __trigger_on_set_for_changes(self, old_config: dict[str, Any], new_config: dict[str, Any]) -> None:
302
339
  for config_key in list(new_config.keys()):
303
340
  old_value: Any = get_value_by_key(old_config, config_key) or {}
304
341
  new_value: Any = new_config[config_key]
305
342
 
306
343
  if DeepDiff(old_value, new_value, ignore_order=True) != {}:
307
- schema = next((schema for schema in self.schemas if schema["key"] == config_key), None)
344
+ schema = next(
345
+ (schema for schema in self.schemas if schema["key"] == config_key),
346
+ None,
347
+ )
308
348
  if schema and schema["type"] != "submit" and "onSet" in schema:
309
- on_set_function: Any = schema.get("onSet", None)
310
- if on_set_function:
311
-
312
- async def process_on_set(
313
- func: Callable[[Any, Any], Awaitable[None | Any]]
314
- | Callable[[Any, Any], None | Any],
315
- new_val: Any,
316
- old_val: Any,
317
- ) -> None:
318
- if inspect.iscoroutinefunction(func):
319
- await func(new_val, old_val)
320
- else:
321
- res = func(new_val, old_val)
322
- if inspect.iscoroutine(res):
323
- await res
324
-
325
- tasks.append(process_on_set(on_set_function, new_value, old_value))
326
-
327
- if tasks:
328
- await asyncio.gather(*tasks)
349
+ self.__run_on_set_detached(schema.get("onSet", None), config_key, new_value, old_value)
350
+
351
+ def __run_on_set_detached(self, on_set_function: Any, key: str, new_value: Any, old_value: Any) -> None:
352
+ if not on_set_function:
353
+ return
354
+
355
+ async def _run() -> None:
356
+ try:
357
+ res = on_set_function(new_value, old_value)
358
+ if inspect.iscoroutine(res):
359
+ await res
360
+ except Exception as error:
361
+ cast(Any, self.__api).logger.error(f'onSet handler for "{key}" failed: {error}')
362
+
363
+ task = asyncio.create_task(_run())
364
+ self.__detached_tasks.add(task)
365
+ task.add_done_callback(self.__detached_tasks.discard)
329
366
 
330
367
  def __filter_storable_values(
331
368
  self,
332
369
  schemas: list[JsonSchema],
333
- result: ConfigDict | None = None,
334
370
  ) -> ConfigDict:
335
- if result is None:
336
- result = {}
371
+ result: ConfigDict = {}
337
372
 
338
373
  for schema in schemas:
339
374
  if is_button_type(schema) or is_submit_type(schema):
@@ -343,9 +378,10 @@ class DeviceStorage(DeviceStorageInterface):
343
378
  config_value = get_value_by_key(self.values, schema["key"])
344
379
  ObjectPath.set(result, schema["key"], config_value)
345
380
 
346
- # Preserve internal values (prefixed with '_') that have no schema
381
+ # Keys without any schema are internal values and always persist;
382
+ # schema-covered keys persist only via their store flag above.
347
383
  for key, value in self.values.items():
348
- if key.startswith("_"):
384
+ if not any(schema["key"] == key or schema["key"].startswith(f"{key}.") for schema in schemas):
349
385
  result[key] = value
350
386
 
351
387
  return result
@@ -360,12 +396,7 @@ class DeviceStorage(DeviceStorageInterface):
360
396
  return bool("store" in schema and schema["store"])
361
397
 
362
398
  def __initializeStorage(self) -> None:
363
- config: Any = cast(dict[str, Any], self.__plugin_db.get("config") or {})
364
- device_config = config.get(self.__device_id, {})
399
+ config = self.__plugin_db.get("config") or {}
400
+ device_config = read_location(config, self.__location) or {}
365
401
  schema_config = generate_config_from_schemas(self.schemas)
366
402
  self.values = merge_with(schema_config, device_config, merge2)
367
-
368
- async def __close(self) -> None:
369
- with suppress(Exception):
370
- self.save()
371
- await self.unregister_storage()
@@ -1,18 +1,19 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING, Any, Literal, overload
3
+ from typing import TYPE_CHECKING, Literal, overload
4
4
 
5
- from plugins.runtime.python.config_db import PluginConfigDb
6
-
7
- from _camera_ui_tools.camera_ui_rpc import RPCClient
8
- from _camera_ui_tools.camera_ui_sdk import JsonSchema, SensorType
9
5
  from plugins.runtime.python.storage import DeviceStorage
10
- from plugins.runtime.python.typings import PluginInfo
6
+ from plugins.runtime.python.store import CameraLocation, PluginLocation, SensorLocation
11
7
 
12
8
  if TYPE_CHECKING:
9
+ from _camera_ui_tools.camera_ui_rpc import RPCClient
10
+ from _camera_ui_tools.camera_ui_sdk import JsonSchema, SensorType
11
+ from plugins.runtime.python.config_db import PluginConfigDb
13
12
  from plugins.runtime.python.plugin_api import PluginAPI
13
+ from plugins.runtime.python.typings import PluginInfo
14
14
 
15
15
 
16
+ # Stable storage key for sensor instance storage.
16
17
  def sensor_storage_key(camera_id: str, sensor_type: str, plugin_id: str, sensor_name: str) -> str:
17
18
  return f"{camera_id}:sensor:{sensor_type}:{plugin_id}:{sensor_name}"
18
19
 
@@ -39,7 +40,12 @@ class StorageController:
39
40
 
40
41
  if not camera_storage:
41
42
  camera_storage = DeviceStorage(
42
- self.__api, self.__proxy, self.__plugin, self.__plugin_db, cameraId, schemas, False
43
+ self.__api,
44
+ self.__proxy,
45
+ self.__plugin,
46
+ self.__plugin_db,
47
+ CameraLocation(camera_id=cameraId),
48
+ schemas,
43
49
  )
44
50
  self.__storages[cameraId] = camera_storage
45
51
  else:
@@ -55,7 +61,12 @@ class StorageController:
55
61
 
56
62
  if not plugin_storage:
57
63
  plugin_storage = DeviceStorage(
58
- self.__api, self.__proxy, self.__plugin, self.__plugin_db, "storage", schemas, True
64
+ self.__api,
65
+ self.__proxy,
66
+ self.__plugin,
67
+ self.__plugin_db,
68
+ PluginLocation(),
69
+ schemas,
59
70
  )
60
71
  self.__storages["storage"] = plugin_storage
61
72
  else:
@@ -82,16 +93,17 @@ class StorageController:
82
93
  storage = self.__storages.get(storage_key)
83
94
 
84
95
  if not storage:
96
+ # storage_key is only the in-memory registry key; persistence addresses
97
+ # the canonical sensors.<camId>.<type>.<name> path. sensor_id is the
98
+ # runtime UUID for the RPC namespace.
85
99
  storage = DeviceStorage(
86
100
  self.__api,
87
101
  self.__proxy,
88
102
  self.__plugin,
89
103
  self.__plugin_db,
90
- storage_key,
104
+ SensorLocation(camera_id=camera_id, sensor_type=sensor_type.value, sensor_name=sensor_name),
91
105
  schemas,
92
- False,
93
106
  sensor_id,
94
- camera_id,
95
107
  )
96
108
  self.__storages[storage_key] = storage
97
109
  storage.update_schema(schemas)
@@ -106,7 +118,6 @@ class StorageController:
106
118
  storage_key = sensor_storage_key(camera_id, sensor_type.value, plugin_id, sensor_name)
107
119
  return self.__storages.get(storage_key)
108
120
 
109
- # Internal method
110
121
  @overload
111
122
  async def createStorage(self, type_: Literal["camera"], device_id: str) -> DeviceStorage: ...
112
123
  @overload
@@ -150,7 +161,6 @@ class StorageController:
150
161
  await storage.register_storage()
151
162
  return storage
152
163
 
153
- # Internal method
154
164
  @overload
155
165
  async def removeStorage(self, type_: Literal["camera"], device_id: str) -> None: ...
156
166
  @overload
@@ -194,3 +204,8 @@ class StorageController:
194
204
  await device_storage.unregister_storage()
195
205
  del self.__storages[storage_key]
196
206
  return
207
+
208
+ # Internal method to close all storages
209
+ async def close(self) -> None:
210
+ for storage in self.__storages.values():
211
+ await storage.close()