homesec 1.2.1__py3-none-any.whl → 1.2.3__py3-none-any.whl
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.
- homesec/app.py +5 -14
- homesec/cli.py +5 -4
- homesec/config/__init__.py +8 -1
- homesec/config/loader.py +17 -2
- homesec/config/validation.py +99 -6
- homesec/interfaces.py +2 -2
- homesec/maintenance/cleanup_clips.py +17 -4
- homesec/models/__init__.py +3 -23
- homesec/models/clip.py +1 -1
- homesec/models/config.py +10 -259
- homesec/models/enums.py +8 -0
- homesec/models/events.py +1 -1
- homesec/models/filter.py +3 -21
- homesec/models/vlm.py +11 -20
- homesec/pipeline/__init__.py +1 -2
- homesec/pipeline/core.py +9 -10
- homesec/plugins/alert_policies/__init__.py +5 -5
- homesec/plugins/alert_policies/default.py +21 -2
- homesec/plugins/analyzers/__init__.py +1 -3
- homesec/plugins/analyzers/openai.py +20 -13
- homesec/plugins/filters/__init__.py +1 -2
- homesec/plugins/filters/yolo.py +25 -5
- homesec/plugins/notifiers/__init__.py +1 -6
- homesec/plugins/notifiers/mqtt.py +21 -1
- homesec/plugins/notifiers/sendgrid_email.py +52 -1
- homesec/plugins/registry.py +27 -0
- homesec/plugins/sources/__init__.py +4 -4
- homesec/plugins/sources/ftp.py +1 -1
- homesec/plugins/sources/local_folder.py +1 -1
- homesec/plugins/sources/rtsp.py +2 -2
- homesec/plugins/storage/__init__.py +1 -9
- homesec/plugins/storage/dropbox.py +13 -1
- homesec/plugins/storage/local.py +8 -1
- homesec/repository/clip_repository.py +1 -1
- homesec/sources/__init__.py +3 -4
- homesec/sources/ftp.py +95 -2
- homesec/sources/local_folder.py +27 -2
- homesec/sources/rtsp/__init__.py +5 -0
- homesec/sources/rtsp/clock.py +18 -0
- homesec/sources/rtsp/core.py +1424 -0
- homesec/sources/rtsp/frame_pipeline.py +325 -0
- homesec/sources/rtsp/hardware.py +143 -0
- homesec/sources/rtsp/motion.py +94 -0
- homesec/sources/rtsp/recorder.py +180 -0
- homesec/sources/rtsp/utils.py +35 -0
- {homesec-1.2.1.dist-info → homesec-1.2.3.dist-info}/METADATA +13 -16
- homesec-1.2.3.dist-info/RECORD +73 -0
- homesec/models/source.py +0 -81
- homesec/pipeline/alert_policy.py +0 -5
- homesec/sources/rtsp.py +0 -1304
- homesec-1.2.1.dist-info/RECORD +0 -68
- /homesec/{plugins/notifiers → notifiers}/multiplex.py +0 -0
- {homesec-1.2.1.dist-info → homesec-1.2.3.dist-info}/WHEEL +0 -0
- {homesec-1.2.1.dist-info → homesec-1.2.3.dist-info}/entry_points.txt +0 -0
- {homesec-1.2.1.dist-info → homesec-1.2.3.dist-info}/licenses/LICENSE +0 -0
homesec/app.py
CHANGED
|
@@ -8,23 +8,15 @@ import signal
|
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
from typing import TYPE_CHECKING
|
|
10
10
|
|
|
11
|
-
from homesec.config import
|
|
12
|
-
load_config,
|
|
13
|
-
resolve_env_var,
|
|
14
|
-
validate_camera_references,
|
|
15
|
-
validate_plugin_names,
|
|
16
|
-
)
|
|
11
|
+
from homesec.config import load_config, resolve_env_var, validate_config, validate_plugin_names
|
|
17
12
|
from homesec.health import HealthServer
|
|
18
13
|
from homesec.interfaces import EventStore
|
|
14
|
+
from homesec.notifiers.multiplex import MultiplexNotifier, NotifierEntry
|
|
19
15
|
from homesec.pipeline import ClipPipeline
|
|
20
16
|
from homesec.plugins.alert_policies import load_alert_policy
|
|
21
17
|
from homesec.plugins.analyzers import load_analyzer
|
|
22
18
|
from homesec.plugins.filters import load_filter
|
|
23
|
-
from homesec.plugins.notifiers import
|
|
24
|
-
MultiplexNotifier,
|
|
25
|
-
NotifierEntry,
|
|
26
|
-
load_notifier_plugin,
|
|
27
|
-
)
|
|
19
|
+
from homesec.plugins.notifiers import load_notifier_plugin
|
|
28
20
|
from homesec.plugins.registry import PluginType, get_plugin_names
|
|
29
21
|
from homesec.plugins.sources import load_source_plugin
|
|
30
22
|
from homesec.plugins.storage import load_storage_plugin
|
|
@@ -252,7 +244,6 @@ class Application:
|
|
|
252
244
|
"""Create alert policy using the plugin registry."""
|
|
253
245
|
return load_alert_policy(
|
|
254
246
|
config.alert_policy,
|
|
255
|
-
per_camera_overrides=config.per_camera_alert,
|
|
256
247
|
trigger_classes=config.vlm.trigger_classes,
|
|
257
248
|
)
|
|
258
249
|
|
|
@@ -263,7 +254,7 @@ class Application:
|
|
|
263
254
|
for camera in config.cameras:
|
|
264
255
|
source_cfg = camera.source
|
|
265
256
|
source = load_source_plugin(
|
|
266
|
-
|
|
257
|
+
source_backend=source_cfg.backend,
|
|
267
258
|
config=source_cfg.config,
|
|
268
259
|
camera_name=camera.name,
|
|
269
260
|
)
|
|
@@ -277,7 +268,6 @@ class Application:
|
|
|
277
268
|
return self._config
|
|
278
269
|
|
|
279
270
|
def _validate_config(self, config: Config) -> None:
|
|
280
|
-
validate_camera_references(config)
|
|
281
271
|
validate_plugin_names(
|
|
282
272
|
config,
|
|
283
273
|
valid_filters=get_plugin_names(PluginType.FILTER),
|
|
@@ -287,6 +277,7 @@ class Application:
|
|
|
287
277
|
valid_alert_policies=get_plugin_names(PluginType.ALERT_POLICY),
|
|
288
278
|
valid_sources=get_plugin_names(PluginType.SOURCE),
|
|
289
279
|
)
|
|
280
|
+
validate_config(config)
|
|
290
281
|
|
|
291
282
|
def _setup_signal_handlers(self) -> None:
|
|
292
283
|
"""Set up signal handlers for graceful shutdown."""
|
homesec/cli.py
CHANGED
|
@@ -13,8 +13,8 @@ load_dotenv()
|
|
|
13
13
|
import fire # type: ignore[import-untyped]
|
|
14
14
|
|
|
15
15
|
from homesec.app import Application
|
|
16
|
-
from homesec.config import ConfigError, load_config
|
|
17
|
-
from homesec.config.validation import
|
|
16
|
+
from homesec.config import ConfigError, load_config, validate_config
|
|
17
|
+
from homesec.config.validation import validate_plugin_names
|
|
18
18
|
from homesec.logging_setup import configure_logging
|
|
19
19
|
from homesec.maintenance.cleanup_clips import CleanupOptions, run_cleanup
|
|
20
20
|
from homesec.plugins.registry import PluginType, get_plugin_names
|
|
@@ -66,7 +66,6 @@ class HomeSec:
|
|
|
66
66
|
discover_all_plugins()
|
|
67
67
|
|
|
68
68
|
# Additional validation checks
|
|
69
|
-
validate_camera_references(cfg)
|
|
70
69
|
validate_plugin_names(
|
|
71
70
|
cfg,
|
|
72
71
|
sorted(get_plugin_names(PluginType.FILTER)),
|
|
@@ -74,7 +73,9 @@ class HomeSec:
|
|
|
74
73
|
valid_storage=sorted(get_plugin_names(PluginType.STORAGE)),
|
|
75
74
|
valid_notifiers=sorted(get_plugin_names(PluginType.NOTIFIER)),
|
|
76
75
|
valid_alert_policies=sorted(get_plugin_names(PluginType.ALERT_POLICY)),
|
|
76
|
+
valid_sources=sorted(get_plugin_names(PluginType.SOURCE)),
|
|
77
77
|
)
|
|
78
|
+
validate_config(cfg)
|
|
78
79
|
|
|
79
80
|
print(f"✓ Config valid: {config_path}")
|
|
80
81
|
camera_names = [camera.name for camera in cfg.cameras]
|
|
@@ -84,7 +85,7 @@ class HomeSec:
|
|
|
84
85
|
]
|
|
85
86
|
print(f" Storage backend: {cfg.storage.backend}")
|
|
86
87
|
print(f" Notifiers: {notifier_backends}")
|
|
87
|
-
print(f" Filter
|
|
88
|
+
print(f" Filter backend: {cfg.filter.backend}")
|
|
88
89
|
print(f" VLM backend: {cfg.vlm.backend}")
|
|
89
90
|
print(f" VLM trigger classes: {cfg.vlm.trigger_classes}")
|
|
90
91
|
print(f" Alert policy backend: {cfg.alert_policy.backend}")
|
homesec/config/__init__.py
CHANGED
|
@@ -6,7 +6,12 @@ from homesec.config.loader import (
|
|
|
6
6
|
load_config_from_dict,
|
|
7
7
|
resolve_env_var,
|
|
8
8
|
)
|
|
9
|
-
from homesec.config.validation import
|
|
9
|
+
from homesec.config.validation import (
|
|
10
|
+
validate_camera_references,
|
|
11
|
+
validate_config,
|
|
12
|
+
validate_plugin_configs,
|
|
13
|
+
validate_plugin_names,
|
|
14
|
+
)
|
|
10
15
|
|
|
11
16
|
__all__ = [
|
|
12
17
|
"ConfigError",
|
|
@@ -14,5 +19,7 @@ __all__ = [
|
|
|
14
19
|
"load_config_from_dict",
|
|
15
20
|
"resolve_env_var",
|
|
16
21
|
"validate_camera_references",
|
|
22
|
+
"validate_config",
|
|
23
|
+
"validate_plugin_configs",
|
|
17
24
|
"validate_plugin_names",
|
|
18
25
|
]
|
homesec/config/loader.py
CHANGED
|
@@ -48,10 +48,18 @@ def load_config(path: Path) -> Config:
|
|
|
48
48
|
raise ConfigError(f"Config must be a YAML mapping, got {type(raw).__name__}", path=path)
|
|
49
49
|
|
|
50
50
|
try:
|
|
51
|
-
|
|
51
|
+
config = Config.model_validate(raw)
|
|
52
52
|
except ValidationError as e:
|
|
53
53
|
raise ConfigError(format_validation_error(e, path), path=path) from e
|
|
54
54
|
|
|
55
|
+
# Discover plugins and validate plugin-specific config
|
|
56
|
+
from homesec.config.validation import validate_config
|
|
57
|
+
from homesec.plugins import discover_all_plugins
|
|
58
|
+
|
|
59
|
+
discover_all_plugins()
|
|
60
|
+
validate_config(config)
|
|
61
|
+
return config
|
|
62
|
+
|
|
55
63
|
|
|
56
64
|
def load_config_from_dict(data: dict[str, Any]) -> Config:
|
|
57
65
|
"""Load and validate configuration from a dict (useful for testing).
|
|
@@ -66,10 +74,17 @@ def load_config_from_dict(data: dict[str, Any]) -> Config:
|
|
|
66
74
|
ConfigError: If validation fails
|
|
67
75
|
"""
|
|
68
76
|
try:
|
|
69
|
-
|
|
77
|
+
config = Config.model_validate(data)
|
|
70
78
|
except ValidationError as e:
|
|
71
79
|
raise ConfigError(format_validation_error(e, path=None)) from e
|
|
72
80
|
|
|
81
|
+
from homesec.config.validation import validate_config
|
|
82
|
+
from homesec.plugins import discover_all_plugins
|
|
83
|
+
|
|
84
|
+
discover_all_plugins()
|
|
85
|
+
validate_config(config)
|
|
86
|
+
return config
|
|
87
|
+
|
|
73
88
|
|
|
74
89
|
def resolve_env_var(env_var_name: str, required: bool = True) -> str | None:
|
|
75
90
|
"""Resolve environment variable by name.
|
homesec/config/validation.py
CHANGED
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
from pydantic import BaseModel, ValidationError
|
|
6
|
+
|
|
5
7
|
from homesec.config.loader import ConfigError
|
|
6
8
|
from homesec.models.config import Config
|
|
9
|
+
from homesec.plugins.registry import PluginType, validate_plugin
|
|
7
10
|
|
|
8
11
|
|
|
9
12
|
def validate_camera_references(config: Config, camera_names: list[str] | None = None) -> None:
|
|
@@ -22,9 +25,17 @@ def validate_camera_references(config: Config, camera_names: list[str] | None =
|
|
|
22
25
|
camera_set = set(camera_names)
|
|
23
26
|
errors = []
|
|
24
27
|
|
|
25
|
-
|
|
28
|
+
overrides: dict[str, object] = {}
|
|
29
|
+
if config.alert_policy.backend == "default":
|
|
30
|
+
raw = config.alert_policy.config
|
|
31
|
+
if isinstance(raw, BaseModel):
|
|
32
|
+
overrides = getattr(raw, "overrides", {}) or {}
|
|
33
|
+
elif isinstance(raw, dict):
|
|
34
|
+
overrides = raw.get("overrides", {}) or {}
|
|
35
|
+
|
|
36
|
+
for camera in overrides:
|
|
26
37
|
if camera not in camera_set:
|
|
27
|
-
errors.append(f"
|
|
38
|
+
errors.append(f"alert_policy.overrides references unknown camera: {camera}")
|
|
28
39
|
|
|
29
40
|
if errors:
|
|
30
41
|
raise ConfigError("Invalid camera references:\n " + "\n ".join(errors))
|
|
@@ -56,9 +67,9 @@ def validate_plugin_names(
|
|
|
56
67
|
errors = []
|
|
57
68
|
|
|
58
69
|
valid_filters_lower = {name.lower() for name in valid_filters}
|
|
59
|
-
if config.filter.
|
|
70
|
+
if config.filter.backend.lower() not in valid_filters_lower:
|
|
60
71
|
errors.append(
|
|
61
|
-
f"Unknown filter plugin: {config.filter.
|
|
72
|
+
f"Unknown filter plugin: {config.filter.backend} (valid: {sorted(valid_filters_lower)})"
|
|
62
73
|
)
|
|
63
74
|
|
|
64
75
|
valid_vlms_lower = {name.lower() for name in valid_vlms}
|
|
@@ -95,11 +106,93 @@ def validate_plugin_names(
|
|
|
95
106
|
if valid_sources is not None:
|
|
96
107
|
valid_sources_lower = {name.lower() for name in valid_sources}
|
|
97
108
|
for camera in config.cameras:
|
|
98
|
-
if camera.source.
|
|
109
|
+
if camera.source.backend.lower() not in valid_sources_lower:
|
|
99
110
|
errors.append(
|
|
100
111
|
f"Unknown source type for camera '{camera.name}': "
|
|
101
|
-
f"{camera.source.
|
|
112
|
+
f"{camera.source.backend} (valid: {sorted(valid_sources_lower)})"
|
|
102
113
|
)
|
|
103
114
|
|
|
104
115
|
if errors:
|
|
105
116
|
raise ConfigError("Invalid plugin configuration:\n " + "\n ".join(errors))
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def validate_plugin_configs(config: Config) -> None:
|
|
120
|
+
"""Validate plugin configs against registered plugin config models."""
|
|
121
|
+
errors: list[str] = []
|
|
122
|
+
|
|
123
|
+
def _add_error(prefix: str, err: Exception) -> None:
|
|
124
|
+
if isinstance(err, ValidationError):
|
|
125
|
+
errors.append(f"{prefix}: {err}")
|
|
126
|
+
else:
|
|
127
|
+
errors.append(f"{prefix}: {err}")
|
|
128
|
+
|
|
129
|
+
try:
|
|
130
|
+
validate_plugin(
|
|
131
|
+
PluginType.STORAGE,
|
|
132
|
+
config.storage.backend,
|
|
133
|
+
config.storage.config,
|
|
134
|
+
)
|
|
135
|
+
except Exception as exc:
|
|
136
|
+
_add_error(f"storage[{config.storage.backend}]", exc)
|
|
137
|
+
|
|
138
|
+
try:
|
|
139
|
+
validate_plugin(
|
|
140
|
+
PluginType.FILTER,
|
|
141
|
+
config.filter.backend,
|
|
142
|
+
config.filter.config,
|
|
143
|
+
)
|
|
144
|
+
except Exception as exc:
|
|
145
|
+
_add_error(f"filter[{config.filter.backend}]", exc)
|
|
146
|
+
|
|
147
|
+
try:
|
|
148
|
+
validate_plugin(
|
|
149
|
+
PluginType.ANALYZER,
|
|
150
|
+
config.vlm.backend,
|
|
151
|
+
config.vlm.config,
|
|
152
|
+
)
|
|
153
|
+
except Exception as exc:
|
|
154
|
+
_add_error(f"vlm[{config.vlm.backend}]", exc)
|
|
155
|
+
|
|
156
|
+
if config.alert_policy.enabled:
|
|
157
|
+
try:
|
|
158
|
+
runtime_context = {}
|
|
159
|
+
if config.alert_policy.backend == "default":
|
|
160
|
+
runtime_context["trigger_classes"] = list(config.vlm.trigger_classes)
|
|
161
|
+
validate_plugin(
|
|
162
|
+
PluginType.ALERT_POLICY,
|
|
163
|
+
config.alert_policy.backend,
|
|
164
|
+
config.alert_policy.config,
|
|
165
|
+
**runtime_context,
|
|
166
|
+
)
|
|
167
|
+
except Exception as exc:
|
|
168
|
+
_add_error(f"alert_policy[{config.alert_policy.backend}]", exc)
|
|
169
|
+
|
|
170
|
+
for index, notifier in enumerate(config.notifiers):
|
|
171
|
+
try:
|
|
172
|
+
validate_plugin(
|
|
173
|
+
PluginType.NOTIFIER,
|
|
174
|
+
notifier.backend,
|
|
175
|
+
notifier.config,
|
|
176
|
+
)
|
|
177
|
+
except Exception as exc:
|
|
178
|
+
_add_error(f"notifier[{index}:{notifier.backend}]", exc)
|
|
179
|
+
|
|
180
|
+
for camera in config.cameras:
|
|
181
|
+
try:
|
|
182
|
+
validate_plugin(
|
|
183
|
+
PluginType.SOURCE,
|
|
184
|
+
camera.source.backend,
|
|
185
|
+
camera.source.config,
|
|
186
|
+
camera_name=camera.name,
|
|
187
|
+
)
|
|
188
|
+
except Exception as exc:
|
|
189
|
+
_add_error(f"source[{camera.name}:{camera.source.backend}]", exc)
|
|
190
|
+
|
|
191
|
+
if errors:
|
|
192
|
+
raise ConfigError("Invalid plugin config:\n " + "\n ".join(errors))
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def validate_config(config: Config, camera_names: list[str] | None = None) -> None:
|
|
196
|
+
"""Validate config boundaries and plugin configs."""
|
|
197
|
+
validate_camera_references(config, camera_names)
|
|
198
|
+
validate_plugin_configs(config)
|
homesec/interfaces.py
CHANGED
|
@@ -248,7 +248,7 @@ class ObjectFilter(Shutdownable, ABC):
|
|
|
248
248
|
- MUST be async (use asyncio.to_thread or run_in_executor for blocking code)
|
|
249
249
|
- CPU/GPU-bound plugins should manage their own ProcessPoolExecutor internally
|
|
250
250
|
- I/O-bound plugins can use async HTTP clients directly
|
|
251
|
-
-
|
|
251
|
+
- If managing a worker pool, use concurrency settings from the plugin's config model
|
|
252
252
|
- Should support early exit on first detection for efficiency
|
|
253
253
|
- overrides apply per-call (model path cannot be overridden)
|
|
254
254
|
|
|
@@ -282,7 +282,7 @@ class VLMAnalyzer(Shutdownable, ABC):
|
|
|
282
282
|
- MUST be async (use asyncio.to_thread or run_in_executor for blocking code)
|
|
283
283
|
- Local models: manage ProcessPoolExecutor internally
|
|
284
284
|
- API-based: use async HTTP clients (aiohttp, httpx)
|
|
285
|
-
-
|
|
285
|
+
- If managing a worker pool, use concurrency settings from the plugin's config model
|
|
286
286
|
- Should use filter_result to focus analysis (e.g., detected person at timestamp X)
|
|
287
287
|
|
|
288
288
|
Returns:
|
|
@@ -19,9 +19,10 @@ from pydantic import BaseModel, Field
|
|
|
19
19
|
from homesec.config import load_config, resolve_env_var
|
|
20
20
|
from homesec.interfaces import ObjectFilter, StorageBackend
|
|
21
21
|
from homesec.models.clip import ClipStateData
|
|
22
|
-
from homesec.models.filter import FilterConfig
|
|
22
|
+
from homesec.models.filter import FilterConfig
|
|
23
23
|
from homesec.plugins import discover_all_plugins
|
|
24
24
|
from homesec.plugins.filters import load_filter
|
|
25
|
+
from homesec.plugins.filters.yolo import YoloFilterConfig
|
|
25
26
|
from homesec.plugins.storage import load_storage_plugin
|
|
26
27
|
from homesec.repository.clip_repository import ClipRepository
|
|
27
28
|
from homesec.state.postgres import PostgresStateStore
|
|
@@ -115,7 +116,17 @@ def _base_payload(
|
|
|
115
116
|
|
|
116
117
|
def _recheck_settings(config: FilterConfig) -> dict[str, object]:
|
|
117
118
|
match config.config:
|
|
118
|
-
case
|
|
119
|
+
case YoloFilterConfig() as settings:
|
|
120
|
+
return {
|
|
121
|
+
"model_path": str(settings.model_path),
|
|
122
|
+
"min_confidence": float(settings.min_confidence),
|
|
123
|
+
"sample_fps": int(settings.sample_fps),
|
|
124
|
+
"min_box_h_ratio": float(settings.min_box_h_ratio),
|
|
125
|
+
"min_hits": int(settings.min_hits),
|
|
126
|
+
"classes": list(settings.classes),
|
|
127
|
+
}
|
|
128
|
+
case dict() as raw:
|
|
129
|
+
settings = YoloFilterConfig.model_validate(raw)
|
|
119
130
|
return {
|
|
120
131
|
"model_path": str(settings.model_path),
|
|
121
132
|
"min_confidence": float(settings.min_confidence),
|
|
@@ -130,8 +141,10 @@ def _recheck_settings(config: FilterConfig) -> dict[str, object]:
|
|
|
130
141
|
|
|
131
142
|
def _build_recheck_filter_config(base: FilterConfig, opts: CleanupOptions) -> FilterConfig:
|
|
132
143
|
match base.config:
|
|
133
|
-
case
|
|
144
|
+
case YoloFilterConfig() as yolo:
|
|
134
145
|
settings = yolo.model_copy(deep=True)
|
|
146
|
+
case dict() as raw:
|
|
147
|
+
settings = YoloFilterConfig.model_validate(raw)
|
|
135
148
|
case _:
|
|
136
149
|
raise ValueError(f"Unsupported filter config type: {type(base.config).__name__}")
|
|
137
150
|
|
|
@@ -144,9 +157,9 @@ def _build_recheck_filter_config(base: FilterConfig, opts: CleanupOptions) -> Fi
|
|
|
144
157
|
settings.min_box_h_ratio = opts.recheck_min_box_h_ratio
|
|
145
158
|
if opts.recheck_min_hits is not None:
|
|
146
159
|
settings.min_hits = opts.recheck_min_hits
|
|
160
|
+
settings.max_workers = int(opts.workers)
|
|
147
161
|
|
|
148
162
|
merged = base.model_copy(deep=True)
|
|
149
|
-
merged.max_workers = int(opts.workers)
|
|
150
163
|
merged.config = settings
|
|
151
164
|
return merged
|
|
152
165
|
|
homesec/models/__init__.py
CHANGED
|
@@ -4,32 +4,23 @@ from homesec.models.alert import Alert, AlertDecision
|
|
|
4
4
|
from homesec.models.clip import Clip, ClipStateData, _resolve_forward_refs
|
|
5
5
|
from homesec.models.config import (
|
|
6
6
|
AlertPolicyConfig,
|
|
7
|
-
AlertPolicyOverrides,
|
|
8
7
|
CameraConfig,
|
|
9
8
|
CameraSourceConfig,
|
|
10
9
|
ConcurrencyConfig,
|
|
11
10
|
Config,
|
|
12
|
-
DefaultAlertPolicySettings,
|
|
13
|
-
DropboxStorageConfig,
|
|
14
11
|
HealthConfig,
|
|
15
|
-
LocalStorageConfig,
|
|
16
|
-
MQTTAuthConfig,
|
|
17
|
-
MQTTConfig,
|
|
18
12
|
NotifierConfig,
|
|
19
13
|
RetentionConfig,
|
|
20
14
|
RetryConfig,
|
|
21
|
-
SendGridEmailConfig,
|
|
22
15
|
StateStoreConfig,
|
|
23
16
|
StorageConfig,
|
|
24
17
|
StoragePathsConfig,
|
|
25
18
|
)
|
|
26
|
-
from homesec.models.enums import RiskLevel, RiskLevelField
|
|
27
|
-
from homesec.models.filter import FilterConfig, FilterOverrides, FilterResult
|
|
28
|
-
from homesec.models.source import FtpSourceConfig, LocalFolderSourceConfig, RTSPSourceConfig
|
|
19
|
+
from homesec.models.enums import RiskLevel, RiskLevelField, VLMRunMode
|
|
20
|
+
from homesec.models.filter import FilterConfig, FilterOverrides, FilterResult
|
|
29
21
|
from homesec.models.vlm import (
|
|
30
22
|
AnalysisResult,
|
|
31
23
|
EntityTimeline,
|
|
32
|
-
OpenAILLMConfig,
|
|
33
24
|
SequenceAnalysis,
|
|
34
25
|
VLMConfig,
|
|
35
26
|
VLMPreprocessConfig,
|
|
@@ -42,7 +33,6 @@ __all__ = [
|
|
|
42
33
|
"Alert",
|
|
43
34
|
"AlertDecision",
|
|
44
35
|
"AlertPolicyConfig",
|
|
45
|
-
"AlertPolicyOverrides",
|
|
46
36
|
"AnalysisResult",
|
|
47
37
|
"CameraConfig",
|
|
48
38
|
"CameraSourceConfig",
|
|
@@ -50,31 +40,21 @@ __all__ = [
|
|
|
50
40
|
"ClipStateData",
|
|
51
41
|
"ConcurrencyConfig",
|
|
52
42
|
"Config",
|
|
53
|
-
"DefaultAlertPolicySettings",
|
|
54
|
-
"DropboxStorageConfig",
|
|
55
43
|
"EntityTimeline",
|
|
56
44
|
"FilterConfig",
|
|
57
45
|
"FilterOverrides",
|
|
58
46
|
"FilterResult",
|
|
59
|
-
"FtpSourceConfig",
|
|
60
47
|
"HealthConfig",
|
|
61
|
-
"LocalFolderSourceConfig",
|
|
62
|
-
"LocalStorageConfig",
|
|
63
|
-
"MQTTAuthConfig",
|
|
64
|
-
"MQTTConfig",
|
|
65
48
|
"NotifierConfig",
|
|
66
|
-
"OpenAILLMConfig",
|
|
67
|
-
"RTSPSourceConfig",
|
|
68
49
|
"RetentionConfig",
|
|
69
50
|
"RetryConfig",
|
|
70
51
|
"RiskLevel",
|
|
71
52
|
"RiskLevelField",
|
|
72
|
-
"SendGridEmailConfig",
|
|
73
53
|
"SequenceAnalysis",
|
|
74
54
|
"StateStoreConfig",
|
|
75
55
|
"StorageConfig",
|
|
76
56
|
"StoragePathsConfig",
|
|
77
57
|
"VLMConfig",
|
|
78
58
|
"VLMPreprocessConfig",
|
|
79
|
-
"
|
|
59
|
+
"VLMRunMode",
|
|
80
60
|
]
|