@camera.ui/server 0.0.10 → 0.0.11-alpha.0

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 (160) hide show
  1. package/dist/interface/assets/@mdi-B8Roxr1z.js +1 -0
  2. package/dist/interface/assets/{@tanstack-B-9PO-kT.js → @tanstack-BvYNCsQh.js} +1 -1
  3. package/dist/interface/assets/{@vee-validate-D_G0ehOl.js → @vee-validate-BY5H7fGS.js} +1 -1
  4. package/dist/interface/assets/@vue-DMh6qW6s.js +17 -0
  5. package/dist/interface/assets/{@vueform-Bj73kRPS.js → @vueform-B62o9zLe.js} +1 -1
  6. package/dist/interface/assets/@vueuse-D76s02Ot.js +1 -0
  7. package/dist/interface/assets/Account-DlOUWknb.css +1 -0
  8. package/dist/interface/assets/Account-UkBkrzMK.js +1 -0
  9. package/dist/interface/assets/Adminpanel-Duo20795.js +2 -0
  10. package/dist/interface/assets/Appearance-rMWpzqff.js +1 -0
  11. package/dist/interface/assets/Backup-DhjSkqF7.js +1 -0
  12. package/dist/interface/assets/{Camera-BOu5dUel.js → Camera-CZ9JeXob.js} +1 -1
  13. package/dist/interface/assets/{Cameras-C7eUXdZk.js → Cameras-1zl5Xo1h.js} +1 -1
  14. package/dist/interface/assets/Cameras-Du4Q1fbV.css +1 -0
  15. package/dist/interface/assets/{Camview-CEgwGCGR.js → Camview-Bd7my9N3.js} +1 -1
  16. package/dist/interface/assets/{Config-Dt-etQ3h.js → Config-DB7gvh-P.js} +1 -1
  17. package/dist/interface/assets/{Console-FCve5CKy.js → Console-DCF1GwRU.js} +1 -1
  18. package/dist/interface/assets/FirstSteps-CGEDVu4n.css +1 -0
  19. package/dist/interface/assets/FirstSteps-DMQGcOZU.js +1 -0
  20. package/dist/interface/assets/{NewCamera-Cc1X79Ns.js → NewCamera-qLp7x0cL.js} +2 -2
  21. package/dist/interface/assets/{Plugin-CnQjafMC.js → Plugin-OP2-xz9S.js} +1 -1
  22. package/dist/interface/assets/{Plugins-CwyUdMlv.js → Plugins-qABzLjjT.js} +1 -1
  23. package/dist/interface/assets/Recordings-CLxIZLl0.js +1 -0
  24. package/dist/interface/assets/Recordings-Ctfkr-5E.js +1 -0
  25. package/dist/interface/assets/Settings-1CGQDosw.js +1 -0
  26. package/dist/interface/assets/System-DScEkzIf.js +1 -0
  27. package/dist/interface/assets/User-BQg52K_d.js +1 -0
  28. package/dist/interface/assets/User-DBVny1Dv.css +1 -0
  29. package/dist/interface/assets/VConfigEditor.vue_vue_type_script_setup_true_lang-BZltc68s.js +2 -0
  30. package/dist/interface/assets/{VConsole-BHy92Ws0.js → VConsole-BCO6q8J5.js} +1 -1
  31. package/dist/interface/assets/VPlayer-B_e1Qv8r.js +1 -0
  32. package/dist/interface/assets/{VPlayer-D3KhRfx-.css → VPlayer-DJjwfULk.css} +1 -1
  33. package/dist/interface/assets/VPluginCard-BL86NY9l.js +4 -0
  34. package/dist/interface/assets/VPluginCard-gBag9k-r.css +1 -0
  35. package/dist/interface/assets/main-2jkpYOC_.css +1 -0
  36. package/dist/interface/assets/main-Dv8zMGzJ.js +9 -0
  37. package/dist/interface/assets/{pinia-CitQ5b66.js → pinia-BQbsPkIS.js} +1 -1
  38. package/dist/interface/assets/{plugins-D80RNVmI.js → plugins-CS-GWC5l.js} +1 -1
  39. package/dist/interface/assets/{rxjs-B53o3e_J.js → rxjs-CQrF441D.js} +1 -1
  40. package/dist/interface/assets/{system-CS_ZOs3_.js → system-D7XWalnh.js} +1 -1
  41. package/dist/interface/assets/{ui-CHKoHLl-.js → ui-CyEgSsGE.js} +1 -1
  42. package/dist/interface/assets/{v-calendar-C-8W4FeM.js → v-calendar-CVkp7hgG.js} +1 -1
  43. package/dist/interface/assets/{vee-validate-Ck3I2guh.js → vee-validate-9vrYaQFq.js} +1 -1
  44. package/dist/interface/assets/{vue-i18n-DuCDz9TJ.js → vue-i18n-DqKZtX1N.js} +1 -1
  45. package/dist/interface/assets/{vue-inline-svg-C2gR6nRD.js → vue-inline-svg-D1BV6A5r.js} +1 -1
  46. package/dist/interface/assets/{vue-router-OPGDMnws.js → vue-router-Bje-Muh5.js} +1 -1
  47. package/dist/interface/assets/vue3-apexcharts-B8Nznyy3.js +703 -0
  48. package/dist/interface/assets/{vue3-dnd-Hf295yge.js → vue3-dnd-CvPSCHQb.js} +1 -1
  49. package/dist/interface/assets/{vue3-toastify-DQr2dOCx.js → vue3-toastify-D8Ru9hkX.js} +1 -1
  50. package/dist/interface/assets/{vuetify-B__3Y-at.css → vuetify-CoMF_JkX.css} +2 -2
  51. package/dist/interface/assets/vuetify-DYU2dOLI.js +5 -0
  52. package/dist/interface/index.html +17 -17
  53. package/dist/interface/sw.js +1 -1
  54. package/dist/server/bin/cui-service.js +4 -8
  55. package/dist/server/bin/cui-service.js.map +1 -1
  56. package/dist/server/package.json +3 -3
  57. package/dist/server/requirements.txt +1 -1
  58. package/dist/server/src/api/controllers/api.controller.d.ts +1 -0
  59. package/dist/server/src/api/controllers/api.controller.js +3 -0
  60. package/dist/server/src/api/controllers/api.controller.js.map +1 -1
  61. package/dist/server/src/api/controllers/cameras.controller.d.ts +2 -1
  62. package/dist/server/src/api/controllers/cameras.controller.js +27 -0
  63. package/dist/server/src/api/controllers/cameras.controller.js.map +1 -1
  64. package/dist/server/src/api/controllers/system.controller.d.ts +3 -0
  65. package/dist/server/src/api/controllers/system.controller.js +39 -0
  66. package/dist/server/src/api/controllers/system.controller.js.map +1 -1
  67. package/dist/server/src/api/controllers/users.controller.js +21 -2
  68. package/dist/server/src/api/controllers/users.controller.js.map +1 -1
  69. package/dist/server/src/api/database/types.d.ts +6 -6
  70. package/dist/server/src/api/index.js +8 -12
  71. package/dist/server/src/api/index.js.map +1 -1
  72. package/dist/server/src/api/routes/api.routes.js +1 -0
  73. package/dist/server/src/api/routes/api.routes.js.map +1 -1
  74. package/dist/server/src/api/routes/cameras.routes.js +19 -1
  75. package/dist/server/src/api/routes/cameras.routes.js.map +1 -1
  76. package/dist/server/src/api/routes/system.routes.js +6 -0
  77. package/dist/server/src/api/routes/system.routes.js.map +1 -1
  78. package/dist/server/src/api/schemas/cameras.schema.d.ts +61 -54
  79. package/dist/server/src/api/schemas/cameras.schema.js +7 -6
  80. package/dist/server/src/api/schemas/cameras.schema.js.map +1 -1
  81. package/dist/server/src/api/schemas/users.schema.d.ts +5 -0
  82. package/dist/server/src/api/schemas/users.schema.js +2 -1
  83. package/dist/server/src/api/schemas/users.schema.js.map +1 -1
  84. package/dist/server/src/api/types/index.d.ts +12 -2
  85. package/dist/server/src/api/types/index.js.map +1 -1
  86. package/dist/server/src/api/websocket/nsp/metrics.d.ts +4 -22
  87. package/dist/server/src/api/websocket/nsp/metrics.js +74 -214
  88. package/dist/server/src/api/websocket/nsp/metrics.js.map +1 -1
  89. package/dist/server/src/api/websocket/nsp/status.d.ts +4 -15
  90. package/dist/server/src/api/websocket/nsp/status.js +10 -16
  91. package/dist/server/src/api/websocket/nsp/status.js.map +1 -1
  92. package/dist/server/src/api/websocket/types.d.ts +25 -64
  93. package/dist/server/src/decoder/worker.d.ts +1 -2
  94. package/dist/server/src/decoder/worker.js +5 -11
  95. package/dist/server/src/decoder/worker.js.map +1 -1
  96. package/dist/server/src/go2rtc/index.js +4 -6
  97. package/dist/server/src/go2rtc/index.js.map +1 -1
  98. package/dist/server/src/index.d.ts +2 -2
  99. package/dist/server/src/index.js.map +1 -1
  100. package/dist/server/src/nats/index.d.ts +1 -1
  101. package/dist/server/src/nats/index.js +3 -3
  102. package/dist/server/src/nats/index.js.map +1 -1
  103. package/dist/server/src/nats/proxy/cameraDevice.d.ts +1 -1
  104. package/dist/server/src/nats/proxy/cameraDevice.js +1 -1
  105. package/dist/server/src/nats/proxy/cameraDevice.js.map +1 -1
  106. package/dist/server/src/nats/proxy/deviceManager.d.ts +1 -1
  107. package/dist/server/src/nats/proxy/deviceManager.js +1 -1
  108. package/dist/server/src/nats/proxy/deviceManager.js.map +1 -1
  109. package/dist/server/src/nats/server.js +4 -6
  110. package/dist/server/src/nats/server.js.map +1 -1
  111. package/dist/server/src/nats/websocket.js +2 -2
  112. package/dist/server/src/nats/websocket.js.map +1 -1
  113. package/dist/server/src/plugins/types.d.ts +10 -41
  114. package/dist/server/src/plugins/worker.js +4 -7
  115. package/dist/server/src/plugins/worker.js.map +1 -1
  116. package/dist/server/src/polyglot/node/plugins/cameraStorage.d.ts +9 -10
  117. package/dist/server/src/polyglot/node/plugins/cameraStorage.js +51 -55
  118. package/dist/server/src/polyglot/node/plugins/cameraStorage.js.map +1 -1
  119. package/dist/server/src/polyglot/node/plugins/child.js +18 -4
  120. package/dist/server/src/polyglot/node/plugins/child.js.map +1 -1
  121. package/dist/server/src/polyglot/node/plugins/proxy/cameraDevice.js +18 -4
  122. package/dist/server/src/polyglot/node/plugins/proxy/cameraDevice.js.map +1 -1
  123. package/dist/server/src/polyglot/node/plugins/schema.d.ts +22 -9
  124. package/dist/server/src/polyglot/node/plugins/schema.js +22 -2
  125. package/dist/server/src/polyglot/node/plugins/schema.js.map +1 -1
  126. package/dist/server/src/polyglot/node/plugins/storageController.d.ts +3 -3
  127. package/dist/server/src/polyglot/node/plugins/storageController.js.map +1 -1
  128. package/dist/server/src/polyglot/python/camera_ui_types/__init__.py +59 -93
  129. package/dist/server/src/polyglot/python/plugins/camera_storage.py +75 -80
  130. package/dist/server/src/polyglot/python/plugins/child.py +12 -4
  131. package/dist/server/src/polyglot/python/plugins/constants.py +0 -1
  132. package/dist/server/src/polyglot/python/plugins/proxy/camera_device.py +12 -4
  133. package/dist/server/src/polyglot/python/plugins/schema.py +19 -230
  134. package/dist/server/src/polyglot/python/plugins/storage_controller.py +2 -2
  135. package/dist/server/src/services/config/types.d.ts +1 -1
  136. package/package.json +3 -3
  137. package/dist/interface/assets/@mdi-C-QA1nE3.js +0 -1
  138. package/dist/interface/assets/@vue-C10tA4b9.js +0 -17
  139. package/dist/interface/assets/@vueuse-BPeqNGoM.js +0 -1
  140. package/dist/interface/assets/Account-B_tHnXoX.css +0 -1
  141. package/dist/interface/assets/Account-MEaGtu1D.js +0 -1
  142. package/dist/interface/assets/Adminpanel-D1ycltxf.js +0 -1
  143. package/dist/interface/assets/Appearance-Jlzp_P21.js +0 -1
  144. package/dist/interface/assets/Backup-1-ju9NDo.js +0 -1
  145. package/dist/interface/assets/Cameras-tmTzi1M6.css +0 -1
  146. package/dist/interface/assets/FirstSteps-DLkKDSe9.js +0 -1
  147. package/dist/interface/assets/FirstSteps-DNHhyDSw.css +0 -1
  148. package/dist/interface/assets/Recordings-Boeev1lV.js +0 -1
  149. package/dist/interface/assets/Recordings-D5ta7AYS.js +0 -1
  150. package/dist/interface/assets/Settings-Cvm2jnBr.js +0 -1
  151. package/dist/interface/assets/System-fHQ7XKri.js +0 -1
  152. package/dist/interface/assets/User-5o1VKizc.js +0 -1
  153. package/dist/interface/assets/User-BXColIbU.css +0 -1
  154. package/dist/interface/assets/VConfigEditor.vue_vue_type_script_setup_true_lang-C_95ELTZ.js +0 -2
  155. package/dist/interface/assets/VPlayer-LX5HGLrY.js +0 -1
  156. package/dist/interface/assets/VPluginCard-CFpg0LM4.js +0 -4
  157. package/dist/interface/assets/VPluginCard-Fl0AYmnF.css +0 -1
  158. package/dist/interface/assets/main-BCWSiwnj.js +0 -9
  159. package/dist/interface/assets/main-CQNVVncO.css +0 -1
  160. package/dist/interface/assets/vuetify-CrcT8VHB.js +0 -5
@@ -1,6 +1,6 @@
1
1
  from abc import ABCMeta, abstractmethod
2
2
  from collections.abc import AsyncGenerator, Awaitable, Coroutine
3
- from typing import Any, Callable, Generic, Optional, TypeVar, Union, overload, runtime_checkable
3
+ from typing import Any, Callable, Generic, Optional, Union, overload, runtime_checkable
4
4
 
5
5
  from PIL import Image
6
6
  from reactivex import Observable
@@ -9,6 +9,7 @@ from typing_extensions import (
9
9
  NotRequired,
10
10
  Protocol,
11
11
  TypedDict,
12
+ TypeVar,
12
13
  )
13
14
 
14
15
  Callback = Union[
@@ -18,7 +19,7 @@ Callback = Union[
18
19
 
19
20
 
20
21
  # schema
21
- JsonSchemaType = Literal["string", "number", "boolean", "object", "array"]
22
+ JsonSchemaType = Literal["string", "number", "boolean", "object", "array", "button"]
22
23
 
23
24
 
24
25
  class JsonBaseSchema(TypedDict):
@@ -32,59 +33,12 @@ class JsonBaseSchema(TypedDict):
32
33
  group: NotRequired[Optional[str]]
33
34
  defaultValue: NotRequired[Optional[Union[str, int, bool]]]
34
35
  store: NotRequired[Optional[bool]]
35
-
36
-
37
- class PluginJsonBaseSchema(JsonBaseSchema):
38
36
  onSet: NotRequired[
39
37
  Union[Callable[[Any, Any], Awaitable[Union[None, Any]]], Callable[[Any, Any], Union[None, Any]]]
40
- ]
41
- onGet: NotRequired[Union[Callable[[], Awaitable[Union[Any, None]]], Callable[[], Union[Any, None]]]]
42
-
43
-
44
- class PluginJsonSchemaString(PluginJsonBaseSchema):
45
- type: Literal["string"]
46
- format: NotRequired[str]
47
- minLength: NotRequired[int]
48
- maxLength: NotRequired[int]
49
-
50
-
51
- class PluginJsonSchemaNumber(PluginJsonBaseSchema):
52
- type: Literal["number"]
53
- minimum: NotRequired[int]
54
- maximum: NotRequired[int]
55
- step: NotRequired[float]
56
-
57
-
58
- class PluginJsonSchemaBoolean(PluginJsonBaseSchema):
59
- type: Literal["boolean"]
60
-
61
-
62
- class PluginJsonSchemaEnum(PluginJsonBaseSchema):
63
- type: Literal["string"]
64
- enum: list[str]
65
- multiple: NotRequired[bool]
66
-
67
-
68
- class PluginJsonSchemaObject(PluginJsonBaseSchema):
69
- type: Literal["object"]
70
- opened: NotRequired[bool]
71
- properties: NotRequired["PluginJsonSchemaForm"]
72
-
73
-
74
- PluginJsonSchema = Union[
75
- PluginJsonSchemaString,
76
- PluginJsonSchemaNumber,
77
- PluginJsonSchemaBoolean,
78
- PluginJsonSchemaEnum,
79
- PluginJsonSchemaObject,
80
- ]
81
-
82
-
83
- PluginJsonSchemaForm = dict[str, PluginJsonSchema]
84
-
85
-
86
- class PluginRootSchema(TypedDict):
87
- schema: PluginJsonSchemaForm
38
+ ] # only for plugins
39
+ onGet: NotRequired[
40
+ Union[Callable[[], Awaitable[Union[Any, None]]], Callable[[], Union[Any, None]]]
41
+ ] # only for plugins
88
42
 
89
43
 
90
44
  class JsonSchemaString(JsonBaseSchema):
@@ -113,25 +67,29 @@ class JsonSchemaEnum(JsonBaseSchema):
113
67
  multiple: NotRequired[bool]
114
68
 
115
69
 
70
+ class JsonSchemaButton(JsonBaseSchema):
71
+ type: Literal["button"]
72
+
73
+
116
74
  class JsonSchemaObject(JsonBaseSchema):
117
75
  type: Literal["object"]
118
76
  opened: NotRequired[bool]
119
77
  properties: NotRequired["JsonSchemaForm"]
120
78
 
121
79
 
122
- class JsonSchemaButton(TypedDict):
80
+ class JsonSchemaArray(JsonBaseSchema):
81
+ type: Literal["array"]
82
+ opened: NotRequired[bool]
83
+ items: NotRequired["JsonSchema"]
84
+
85
+
86
+ class JsonSchemaObjectButton(TypedDict):
123
87
  label: str
124
88
  onSubmit: str
125
89
 
126
90
 
127
91
  class JsonSchemaObjectWithButtons(JsonSchemaObject):
128
- buttons: tuple[JsonSchemaButton, NotRequired[JsonSchemaButton]]
129
-
130
-
131
- class JsonSchemaArray(JsonBaseSchema):
132
- type: Literal["array"]
133
- opened: NotRequired[bool]
134
- items: NotRequired["JsonSchema"]
92
+ buttons: list[JsonSchemaObjectButton]
135
93
 
136
94
 
137
95
  JsonSchema = Union[
@@ -142,6 +100,7 @@ JsonSchema = Union[
142
100
  JsonSchemaObject,
143
101
  JsonSchemaObjectWithButtons,
144
102
  JsonSchemaArray,
103
+ JsonSchemaButton,
145
104
  ]
146
105
 
147
106
 
@@ -153,8 +112,8 @@ class RootSchema(TypedDict):
153
112
 
154
113
 
155
114
  class SchemaConfig(TypedDict):
156
- rootSchema: PluginRootSchema
157
- config: dict[str, Union[str, int, bool]]
115
+ rootSchema: RootSchema
116
+ config: dict[str, Any]
158
117
 
159
118
 
160
119
  # camera object
@@ -240,12 +199,12 @@ class DetectionZone(TypedDict):
240
199
 
241
200
 
242
201
  class CameraInformation(TypedDict):
243
- model: str
244
- manufacturer: str
245
- hardware: str
246
- serialNumber: str
247
- firmwareVersion: str
248
- supportUrl: str
202
+ model: NotRequired[str]
203
+ manufacturer: NotRequired[str]
204
+ hardware: NotRequired[str]
205
+ serialNumber: NotRequired[str]
206
+ firmwareVersion: NotRequired[str]
207
+ supportUrl: NotRequired[str]
249
208
 
250
209
 
251
210
  class MotionDetectionSettings(TypedDict):
@@ -1037,47 +996,52 @@ class DeviceManager(Protocol):
1037
996
 
1038
997
  # camera storage
1039
998
  P = TypeVar("P")
999
+ CS = TypeVar("CS", default=dict[str, Any])
1040
1000
 
1041
1001
 
1042
1002
  @runtime_checkable
1043
- class CameraStorage(Protocol):
1044
- values: dict[str, Any]
1003
+ class CameraStorage(Protocol, Generic[CS]):
1004
+ values: CS
1005
+ schema: JsonSchemaForm
1045
1006
 
1046
- async def initialize_storage(self) -> None: ...
1007
+ async def initializeStorage(self) -> None: ...
1047
1008
  @overload
1048
- def get_value(self, path: str) -> Union[Awaitable[P], P, None]: ...
1009
+ def getValue(self, path: str) -> Union[Awaitable[P], P, None]: ...
1049
1010
  @overload
1050
- def get_value(self, path: str, default_value: P) -> Union[Awaitable[P], P]: ...
1051
- def get_value(self, path: str, default_value: Optional[P] = None) -> Union[Awaitable[P], P, None]: ...
1052
- async def set_value(self, path: str, new_value: Any) -> None: ...
1053
- def has_value(self, path: str) -> bool: ...
1011
+ def getValue(self, path: str, default_value: P) -> Union[Awaitable[P], P]: ...
1012
+ def getValue(self, path: str, default_value: Optional[P] = None) -> Union[Awaitable[P], P, None]: ...
1013
+ async def setValue(self, path: str, new_value: Any) -> None: ...
1014
+ def hasValue(self, path: str) -> bool: ...
1054
1015
  async def getConfig(self) -> SchemaConfig: ...
1055
- async def setConfig(self, new_config: dict[str, Any]) -> None: ...
1016
+ async def setConfig(self, new_config: CS) -> None: ...
1056
1017
  @overload
1057
- async def add_schema(self, schema_or_path: PluginJsonSchemaForm) -> None: ...
1018
+ async def addSchema(self, schema_or_path: JsonSchemaForm) -> None: ...
1058
1019
  @overload
1059
- async def add_schema(self, schema_or_path: str, schema: PluginJsonSchema) -> None: ...
1060
- async def add_schema(
1020
+ async def addSchema(self, schema_or_path: str, schema: JsonSchema) -> None: ...
1021
+ async def addSchema(
1061
1022
  self,
1062
- schema_or_path: Union[PluginJsonSchemaForm, str],
1063
- schema: Optional[PluginJsonSchema] = None,
1023
+ schema_or_path: Union[JsonSchemaForm, str],
1024
+ schema: Optional[JsonSchema] = None,
1064
1025
  ) -> None: ...
1065
- def remove_schema(self, path: str) -> None: ...
1066
- async def change_schema(self, path: str, new_schema: dict[str, Any]) -> None: ...
1067
- def get_schema(self, path: str) -> Optional[PluginJsonSchema]: ...
1068
- def has_schema(self, path: str) -> bool: ...
1026
+ def removeSchema(self, path: str) -> None: ...
1027
+ async def changeSchema(self, path: str, new_schema: dict[str, Any]) -> None: ...
1028
+ def getSchema(self, path: str) -> Optional[JsonSchema]: ...
1029
+ def hasSchema(self, path: str) -> bool: ...
1030
+
1031
+
1032
+ SC = TypeVar("SC", default=CameraStorage[dict[str, Any]], covariant=True)
1069
1033
 
1070
1034
 
1071
1035
  # StorageController
1072
1036
  @runtime_checkable
1073
- class StorageController(Protocol):
1037
+ class StorageController(Protocol[SC]):
1074
1038
  def create_camera_storage(
1075
1039
  self,
1076
1040
  instance: Any,
1077
1041
  camera_id: str,
1078
- schema: Optional[PluginJsonSchemaForm] = None,
1079
- ) -> CameraStorage: ...
1080
- def get_camera_storage(self, camera_id: str) -> Optional[CameraStorage]: ...
1042
+ schema: Optional[JsonSchemaForm] = None,
1043
+ ) -> SC: ...
1044
+ def get_camera_storage(self, camera_id: str) -> Optional[SC]: ...
1081
1045
  def remove_camera_storage(self, camera_id: str) -> None: ...
1082
1046
 
1083
1047
 
@@ -1123,13 +1087,15 @@ class PluginConfigService(Protocol):
1123
1087
  # Plugin API
1124
1088
  APIEventType = Literal["finishLaunching", "shutdown"]
1125
1089
 
1090
+ PA = TypeVar("PA", bound=CameraStorage[Any], default=CameraStorage[Any])
1091
+
1126
1092
 
1127
1093
  @runtime_checkable
1128
- class PluginAPI(Protocol):
1094
+ class PluginAPI(Protocol[PA]):
1129
1095
  storage_path: str
1130
1096
  config_file: str
1131
1097
  config_service: PluginConfigService
1132
- storage_controller: StorageController
1098
+ storage_controller: StorageController[PA]
1133
1099
  device_manager: DeviceManager
1134
1100
 
1135
1101
  def on(self, event: APIEventType, listener: Callback) -> "PluginAPI": ...
@@ -6,10 +6,10 @@ from pathlib import Path
6
6
  from typing import Any, Optional, TypeVar, Union, cast
7
7
 
8
8
  from camera_ui_types import (
9
- PluginJsonBaseSchema,
10
- PluginJsonSchema,
11
- PluginJsonSchemaForm,
12
- PluginRootSchema,
9
+ JsonBaseSchema,
10
+ JsonSchema,
11
+ JsonSchemaForm,
12
+ RootSchema,
13
13
  SchemaConfig,
14
14
  )
15
15
  from deepdiff import DeepDiff
@@ -62,15 +62,15 @@ class CameraStorage:
62
62
  api: PluginAPI,
63
63
  instance: Any,
64
64
  camera_id: str,
65
- schema: Optional[PluginJsonSchemaForm] = None,
65
+ schema: Optional[JsonSchemaForm] = None,
66
66
  ) -> None:
67
67
  if schema is None:
68
68
  schema = {}
69
69
  self.__api = api
70
70
  self.__instance = instance
71
71
  self.__camera_id = camera_id
72
- self.__schema: PluginJsonSchemaForm = schema or {}
73
- self.__values: dict[str, Any] = {}
72
+ self.schema: JsonSchemaForm = schema or {}
73
+ self.values: dict[str, Any] = {}
74
74
 
75
75
  db_path = Path(api.storage_path) / "volume"
76
76
  self.__db: Lmdb[Literal["config"], dict[str, Any]] = JsonLmdb.open(file=str(db_path), flag="c")
@@ -78,11 +78,7 @@ class CameraStorage:
78
78
  self.__api.device_manager.once("cameraDeselected", self.__deselected)
79
79
  self.__api.once("shutdown", self.__close)
80
80
 
81
- @property
82
- def values(self) -> dict[str, Any]:
83
- return self.__values
84
-
85
- def initialize_storage(self) -> None:
81
+ def initializeStorage(self) -> None:
86
82
  """
87
83
  Initialize the storage.
88
84
  This will load the configuration from the database.
@@ -96,17 +92,17 @@ class CameraStorage:
96
92
  except: # noqa: E722
97
93
  pass
98
94
 
99
- schema_config = generate_json_from_root_schema({"schema": self.__schema})
95
+ schema_config = generate_json_from_root_schema({"schema": self.schema})
100
96
 
101
- self.__values = merge_with(schema_config, camera_config, merge, [])
97
+ self.values = merge_with(schema_config, camera_config, merge, [])
102
98
 
103
99
  self.__save_db()
104
100
 
105
101
  @overload
106
- async def get_value(self, path: str) -> Union[Any, None]: ...
102
+ async def getValue(self, path: str) -> Union[Any, None]: ...
107
103
  @overload
108
- async def get_value(self, path: str, default_value: P) -> P: ...
109
- async def get_value(self, path: str, default_value: Optional[P] = None) -> Union[P, None]:
104
+ async def getValue(self, path: str, default_value: P) -> P: ...
105
+ async def getValue(self, path: str, default_value: Optional[P] = None) -> Union[P, None]:
110
106
  """
111
107
  Get a value from the configuration by its key/path.
112
108
  If the schema has an onGet function, the onGet function will be called.
@@ -119,8 +115,8 @@ class CameraStorage:
119
115
  """
120
116
 
121
117
  schema_path = config_path_to_schema_path(path)
122
- schema: PluginJsonBaseSchema = ObjectPath.get(self.__schema, schema_path)
123
- config_value = ObjectPath.get(self.__values, path)
118
+ schema: JsonBaseSchema = ObjectPath.get(self.schema, schema_path)
119
+ config_value = ObjectPath.get(self.values, path)
124
120
 
125
121
  if schema:
126
122
  on_get_function = schema.get("onGet", None)
@@ -136,7 +132,7 @@ class CameraStorage:
136
132
  else:
137
133
  return default_value
138
134
 
139
- async def set_value(self, path: str, new_value: Any) -> None:
135
+ async def setValue(self, path: str, new_value: Any) -> None:
140
136
  """
141
137
  Set a new value for a given key/path in the configuration.
142
138
  If the schema of the value has an onSet function, and the value has changed, the onSet function will be called.
@@ -147,18 +143,18 @@ class CameraStorage:
147
143
  """
148
144
 
149
145
  schema_path = config_path_to_schema_path(path)
150
- schema: PluginJsonSchema = ObjectPath.get(self.__schema, schema_path)
146
+ schema: JsonSchema = ObjectPath.get(self.schema, schema_path)
151
147
 
152
148
  if schema:
153
- old_config = deepcopy(self.__values)
154
- ObjectPath.set(self.__values, path, new_value)
149
+ old_config = deepcopy(self.values)
150
+ ObjectPath.set(self.values, path, new_value)
155
151
 
156
- await self.__trigger_on_set_for_changes(old_config, self.__values)
152
+ await self.__trigger_on_set_for_changes(old_config, self.values)
157
153
 
158
154
  if self.__contains_storable_schema(schema):
159
155
  self.__save_db()
160
156
 
161
- def has_value(self, path: str) -> bool:
157
+ def hasValue(self, path: str) -> bool:
162
158
  """
163
159
  Check if a value exists by its config path/key.
164
160
 
@@ -166,7 +162,7 @@ class CameraStorage:
166
162
  :return: True if the value exists, false otherwise.
167
163
  """
168
164
 
169
- config_value = ObjectPath.get(self.__values, path)
165
+ config_value = ObjectPath.get(self.values, path)
170
166
  return config_value is not None
171
167
 
172
168
  async def getConfig(self) -> SchemaConfig:
@@ -177,12 +173,12 @@ class CameraStorage:
177
173
  :return: The root schema and configuration.
178
174
  """
179
175
 
180
- filtered_schema = remove_functions_from_schema_form(self.__schema)
181
- root_schema: PluginRootSchema = {"schema": filtered_schema}
176
+ filtered_schema = remove_functions_from_schema_form(self.schema)
177
+ root_schema: RootSchema = {"schema": filtered_schema}
182
178
 
183
- await self.__resolve_on_get_functions(self.__schema)
179
+ await self.__resolve_on_get_functions(self.schema)
184
180
 
185
- return {"rootSchema": root_schema, "config": self.__values}
181
+ return {"rootSchema": root_schema, "config": self.values}
186
182
 
187
183
  async def setConfig(self, new_config: dict[str, Any]) -> None:
188
184
  """
@@ -192,20 +188,20 @@ class CameraStorage:
192
188
  :param new_config: The new configuration to set.
193
189
  """
194
190
 
195
- old_config = deepcopy(self.__values)
196
- self.__values = new_config
191
+ old_config = deepcopy(self.values)
192
+ self.values = new_config
197
193
 
198
- await self.__trigger_on_set_for_changes(old_config, self.__values)
194
+ await self.__trigger_on_set_for_changes(old_config, self.values)
199
195
  self.__save_db()
200
196
 
201
197
  @overload
202
- async def add_schema(self, schema_or_path: PluginJsonSchemaForm) -> None: ...
198
+ async def addSchema(self, schema_or_path: JsonSchemaForm) -> None: ...
203
199
  @overload
204
- async def add_schema(self, schema_or_path: str, schema: PluginJsonSchema) -> None: ...
205
- async def add_schema(
200
+ async def addSchema(self, schema_or_path: str, schema: JsonSchema) -> None: ...
201
+ async def addSchema(
206
202
  self,
207
- schema_or_path: Union[PluginJsonSchemaForm, str],
208
- schema: Optional[PluginJsonSchema] = None,
203
+ schema_or_path: Union[JsonSchemaForm, str],
204
+ schema: Optional[JsonSchema] = None,
209
205
  ) -> None:
210
206
  """
211
207
  Add a new schema to the existing schema.
@@ -222,7 +218,7 @@ class CameraStorage:
222
218
  path = schema_or_path
223
219
  schema_path = config_path_to_schema_path(path)
224
220
 
225
- ObjectPath.set(self.__schema, schema_path, schema)
221
+ ObjectPath.set(self.schema, schema_path, schema)
226
222
 
227
223
  await self.__resolve_on_get_functions(schema, schema_path)
228
224
 
@@ -232,7 +228,7 @@ class CameraStorage:
232
228
  elif isinstance(schema_or_path, dict) and schema is None:
233
229
  new_schema = schema_or_path
234
230
 
235
- self.__schema = merge_with(self.__schema, new_schema, merge, [])
231
+ self.schema = merge_with(self.schema, new_schema, merge, [])
236
232
 
237
233
  await self.__resolve_on_get_functions(new_schema)
238
234
 
@@ -240,12 +236,12 @@ class CameraStorage:
240
236
  should_save = True
241
237
 
242
238
  else:
243
- raise Exception("Invalid arguments for add_schema")
239
+ raise Exception("Invalid arguments for addSchema")
244
240
 
245
241
  if should_save:
246
242
  self.__save_db()
247
243
 
248
- def remove_schema(self, path: str) -> None:
244
+ def removeSchema(self, path: str) -> None:
249
245
  """
250
246
  Remove a schema by its config path/key from the existing schema.
251
247
  It will also remove the value from the configuration.
@@ -255,16 +251,16 @@ class CameraStorage:
255
251
  """
256
252
 
257
253
  schema_path = config_path_to_schema_path(path)
258
- schema: Optional[PluginJsonSchema] = ObjectPath.get(self.__schema, schema_path) or None
254
+ schema: Optional[JsonSchema] = ObjectPath.get(self.schema, schema_path) or None
259
255
 
260
256
  if schema:
261
- ObjectPath.delete(self.__schema, schema_path)
262
- ObjectPath.delete(self.__values, path)
257
+ ObjectPath.delete(self.schema, schema_path)
258
+ ObjectPath.delete(self.values, path)
263
259
 
264
260
  if self.__contains_storable_schema(schema):
265
261
  self.__remove_db()
266
262
 
267
- async def change_schema(self, path: str, new_schema: dict[str, Any]) -> None:
263
+ async def changeSchema(self, path: str, new_schema: dict[str, Any]) -> None:
268
264
  """
269
265
  Change a schema by its config path/key from the existing schema.
270
266
  It will also change the value from the configuration.
@@ -275,18 +271,18 @@ class CameraStorage:
275
271
  """
276
272
 
277
273
  schema_path = config_path_to_schema_path(path)
278
- schema: Optional[PluginJsonSchema] = ObjectPath.get(self.__schema, schema_path) or None
274
+ schema: Optional[JsonSchema] = ObjectPath.get(self.schema, schema_path) or None
279
275
 
280
276
  if schema:
281
277
  merge_with(schema, new_schema, merge, [])
282
- ObjectPath.set(self.__schema, schema_path, schema)
278
+ ObjectPath.set(self.schema, schema_path, schema)
283
279
 
284
280
  await self.__resolve_on_get_functions(schema, schema_path)
285
281
 
286
282
  if self.__contains_storable_schema(schema):
287
283
  self.__save_db()
288
284
 
289
- def get_schema(self, path: str) -> Optional[PluginJsonSchema]:
285
+ def getSchema(self, path: str) -> Optional[JsonSchema]:
290
286
  """
291
287
  Get a schema by its config path/key.
292
288
 
@@ -295,11 +291,11 @@ class CameraStorage:
295
291
  """
296
292
 
297
293
  schema_path = config_path_to_schema_path(path)
298
- schema: Optional[PluginJsonSchema] = ObjectPath.get(self.__schema, schema_path) or None
294
+ schema: Optional[JsonSchema] = ObjectPath.get(self.schema, schema_path) or None
299
295
 
300
296
  return schema
301
297
 
302
- def has_schema(self, path: str) -> bool:
298
+ def hasSchema(self, path: str) -> bool:
303
299
  """
304
300
  Check if a schema exists by its config path/key.
305
301
 
@@ -308,13 +304,13 @@ class CameraStorage:
308
304
  """
309
305
 
310
306
  schema_path = config_path_to_schema_path(path)
311
- schema: Optional[PluginJsonSchema] = ObjectPath.get(self.__schema, schema_path) or None
307
+ schema: Optional[JsonSchema] = ObjectPath.get(self.schema, schema_path) or None
312
308
 
313
309
  return schema is not None
314
310
 
315
311
  async def __resolve_on_get_functions(
316
312
  self,
317
- schema: Union[PluginJsonSchemaForm, PluginJsonSchema],
313
+ schema: Union[JsonSchemaForm, JsonSchema],
318
314
  base_schema_path: str = "",
319
315
  ) -> None:
320
316
  if "onGet" in schema or "onSet" in schema:
@@ -326,7 +322,7 @@ class CameraStorage:
326
322
  base_schema_path,
327
323
  )
328
324
  else:
329
- _schema = cast(PluginJsonSchemaForm, schema)
325
+ _schema = cast(JsonSchemaForm, schema)
330
326
 
331
327
  for key, value in _schema.items():
332
328
  schema_path = f"{base_schema_path}.{key}" if base_schema_path else key
@@ -340,7 +336,7 @@ class CameraStorage:
340
336
 
341
337
  async def ___resolve_on_get_functions_for_object(
342
338
  self,
343
- schema: PluginJsonSchemaForm,
339
+ schema: JsonSchemaForm,
344
340
  base_schema_path: str,
345
341
  ) -> None:
346
342
  for key, _ in schema.items():
@@ -349,10 +345,10 @@ class CameraStorage:
349
345
 
350
346
  async def __resolve_on_get_functions_for_schema(self, schema_path: str) -> None:
351
347
  config_path = schema_path_to_config_path(schema_path)
352
- schema_value = await self.get_value(config_path)
348
+ schema_value = await self.getValue(config_path)
353
349
 
354
350
  if schema_value is not None:
355
- ObjectPath.set(self.__values, config_path, schema_value)
351
+ ObjectPath.set(self.values, config_path, schema_value)
356
352
 
357
353
  async def __trigger_on_set_for_changes(
358
354
  self, old_config: dict[str, Any], new_config: dict[str, Any], path: str = ""
@@ -364,7 +360,7 @@ class CameraStorage:
364
360
 
365
361
  if DeepDiff(old_value, new_value, ignore_order=True) != {}:
366
362
  schema_path = config_path_to_schema_path(new_path)
367
- schema_item: Optional[PluginJsonBaseSchema] = get_value_by_path(self.__schema, schema_path)
363
+ schema_item: Optional[JsonBaseSchema] = get_value_by_path(self.schema, schema_path)
368
364
 
369
365
  if schema_item:
370
366
  on_set_function = schema_item.get("onSet", None)
@@ -380,12 +376,15 @@ class CameraStorage:
380
376
 
381
377
  def __filter_storable_values(
382
378
  self,
383
- schema: PluginJsonSchemaForm,
379
+ schema: JsonSchemaForm,
384
380
  base_schema_path: str = "",
385
381
  result: dict[str, Any] = {},
386
382
  ) -> dict[str, Any]:
387
383
  for key, value in schema.items():
388
- if "type" in value and value["type"] == "object":
384
+ if value["type"] == "button":
385
+ continue
386
+
387
+ if value["type"] == "object":
389
388
  if "properties" in value:
390
389
  s: Any = value["properties"]
391
390
  path = f"{base_schema_path}.{key}" if base_schema_path else key
@@ -393,7 +392,7 @@ class CameraStorage:
393
392
  elif "store" in value and value["store"]:
394
393
  schema_path = f"{base_schema_path}.{key}" if base_schema_path else key
395
394
  config_path = schema_path_to_config_path(schema_path)
396
- config_value = get_value_by_path(self.__values, config_path)
395
+ config_value = get_value_by_path(self.values, config_path)
397
396
 
398
397
  ObjectPath.set(result, config_path, config_value)
399
398
 
@@ -401,30 +400,25 @@ class CameraStorage:
401
400
 
402
401
  def __contains_storable_schema(
403
402
  self,
404
- schema: Union[PluginJsonSchemaForm, PluginJsonSchema],
403
+ schema: Union[JsonSchemaForm, JsonSchema],
405
404
  ) -> bool:
406
- if "type" in schema and schema["type"] == "object":
407
- if "type" in schema and schema["type"] == "object":
408
- if "store" in schema and schema["store"]:
409
- return True
410
- if "properties" in schema and schema["properties"]:
411
- for sub_schema in schema["properties"].values():
412
- sub: Any = sub_schema
413
- if self.__contains_storable_schema(sub):
414
- return True
415
-
416
- return False
417
-
405
+ if schema["type"] == "boolean":
418
406
  return False
419
- else:
420
- return any(
421
- self.__contains_storable_schema(sub_schema) # type: ignore
422
- for sub_schema in schema.values()
423
- )
407
+
408
+ if "store" in schema and schema["store"]:
409
+ return True
410
+
411
+ if schema["type"] == "object" and "properties" in schema:
412
+ schema_values: Any = schema["properties"].values()
413
+ return any(self.__contains_storable_schema(sub_schema) for sub_schema in schema_values)
414
+ elif schema["type"] == "array" and "items" in schema:
415
+ return self.__contains_storable_schema(schema["items"])
416
+
417
+ return False
424
418
 
425
419
  def __save_db(self) -> None:
426
420
  config: Any = self.__db.get("config") or {}
427
- storable_config = self.__filter_storable_values(self.__schema)
421
+ storable_config = self.__filter_storable_values(self.schema)
428
422
 
429
423
  config[self.__camera_id] = storable_config
430
424
 
@@ -443,4 +437,5 @@ class CameraStorage:
443
437
 
444
438
  def __close(self) -> None:
445
439
  with suppress(Exception):
440
+ self.__save_db()
446
441
  self.__db.close()
@@ -143,6 +143,9 @@ class PluginChild:
143
143
  if not self.message_queue:
144
144
  raise Exception("Message queue not initialized")
145
145
 
146
+ reply = message.copy()
147
+ reply["type"] = "reply"
148
+
146
149
  try:
147
150
  plugin = None
148
151
 
@@ -165,7 +168,7 @@ class PluginChild:
165
168
  else:
166
169
  response = method_to_call(*args)
167
170
 
168
- await self.message_queue.enqueue({**message, "type": "reply", "response": response})
171
+ reply["response"] = response or {}
169
172
  elif camera_storage and hasattr(camera_storage, fn) and callable(getattr(camera_storage, fn)):
170
173
  method_to_call = getattr(camera_storage, fn)
171
174
 
@@ -174,9 +177,9 @@ class PluginChild:
174
177
  else:
175
178
  response = method_to_call(*args)
176
179
 
177
- await self.message_queue.enqueue({**message, "type": "reply", "response": response})
180
+ reply["response"] = response or {}
178
181
  else:
179
- await self.message_queue.enqueue({**message, "type": "reply"})
182
+ raise Exception(f"Unknown function: {fn}")
180
183
  except Exception as e:
181
184
  deserialized_error: DeserializedError = {
182
185
  "name": type(e).__name__,
@@ -184,7 +187,12 @@ class PluginChild:
184
187
  "stack": traceback.format_exc(),
185
188
  }
186
189
 
187
- await self.message_queue.enqueue({**message, "type": "reply", "error": deserialized_error})
190
+ reply["error"] = deserialized_error
191
+
192
+ if message["timeout"] == 0:
193
+ return
194
+ else:
195
+ await self.message_queue.enqueue(reply)
188
196
 
189
197
  async def on_worker_message(self, msg: Msg) -> None:
190
198
  if self.stopped: