homesec 1.0.1__tar.gz → 1.1.0__tar.gz

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 (122) hide show
  1. {homesec-1.0.1 → homesec-1.1.0}/.github/workflows/ci.yml +14 -3
  2. {homesec-1.0.1 → homesec-1.1.0}/AGENTS.md +16 -8
  3. homesec-1.1.0/CHANGELOG.md +45 -0
  4. {homesec-1.0.1 → homesec-1.1.0}/Makefile +8 -3
  5. {homesec-1.0.1 → homesec-1.1.0}/PKG-INFO +45 -4
  6. {homesec-1.0.1 → homesec-1.1.0}/README.md +44 -3
  7. homesec-1.1.0/TESTING.md +456 -0
  8. {homesec-1.0.1 → homesec-1.1.0}/pyproject.toml +2 -1
  9. homesec-1.1.0/tests/homesec/test_cli.py +348 -0
  10. homesec-1.1.0/tests/homesec/test_dropbox_storage.py +668 -0
  11. homesec-1.1.0/tests/homesec/test_ftp_source.py +297 -0
  12. homesec-1.1.0/tests/homesec/test_local_storage.py +318 -0
  13. homesec-1.1.0/tests/homesec/test_logging_setup.py +220 -0
  14. homesec-1.1.0/tests/homesec/test_mqtt_notifier.py +435 -0
  15. homesec-1.1.0/tests/homesec/test_openai_vlm.py +413 -0
  16. homesec-1.1.0/tests/homesec/test_plugin_utils.py +197 -0
  17. homesec-1.1.0/tests/homesec/test_sendgrid_notifier.py +441 -0
  18. {homesec-1.0.1 → homesec-1.1.0}/uv.lock +122 -2
  19. homesec-1.0.1/CHANGELOG.md +0 -16
  20. homesec-1.0.1/tests/homesec/test_dropbox_storage.py +0 -295
  21. homesec-1.0.1/tests/homesec/test_mqtt_notifier.py +0 -126
  22. homesec-1.0.1/tests/homesec/test_openai_vlm.py +0 -161
  23. {homesec-1.0.1 → homesec-1.1.0}/.dockerignore +0 -0
  24. {homesec-1.0.1 → homesec-1.1.0}/.env.example +0 -0
  25. {homesec-1.0.1 → homesec-1.1.0}/.github/workflows/release.yaml +0 -0
  26. {homesec-1.0.1 → homesec-1.1.0}/.github/workflows/validate-pr-title.yaml +0 -0
  27. {homesec-1.0.1 → homesec-1.1.0}/.gitignore +0 -0
  28. {homesec-1.0.1 → homesec-1.1.0}/DESIGN.md +0 -0
  29. {homesec-1.0.1 → homesec-1.1.0}/Dockerfile +0 -0
  30. {homesec-1.0.1 → homesec-1.1.0}/LICENSE +0 -0
  31. {homesec-1.0.1 → homesec-1.1.0}/alembic/env.py +0 -0
  32. {homesec-1.0.1 → homesec-1.1.0}/alembic/script.py.mako +0 -0
  33. {homesec-1.0.1 → homesec-1.1.0}/alembic/versions/e6f25df0df90_initial.py +0 -0
  34. {homesec-1.0.1 → homesec-1.1.0}/alembic.ini +0 -0
  35. {homesec-1.0.1 → homesec-1.1.0}/config/example.yaml +0 -0
  36. {homesec-1.0.1 → homesec-1.1.0}/docker-compose.yml +0 -0
  37. {homesec-1.0.1 → homesec-1.1.0}/docker-entrypoint.sh +0 -0
  38. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/__init__.py +0 -0
  39. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/app.py +0 -0
  40. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/cli.py +0 -0
  41. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/config/__init__.py +0 -0
  42. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/config/loader.py +0 -0
  43. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/config/validation.py +0 -0
  44. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/errors.py +0 -0
  45. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/health/__init__.py +0 -0
  46. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/health/server.py +0 -0
  47. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/interfaces.py +0 -0
  48. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/logging_setup.py +0 -0
  49. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/maintenance/__init__.py +0 -0
  50. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/maintenance/cleanup_clips.py +0 -0
  51. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/models/__init__.py +0 -0
  52. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/models/alert.py +0 -0
  53. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/models/clip.py +0 -0
  54. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/models/config.py +0 -0
  55. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/models/events.py +0 -0
  56. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/models/filter.py +0 -0
  57. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/models/source.py +0 -0
  58. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/models/storage.py +0 -0
  59. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/models/vlm.py +0 -0
  60. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/pipeline/__init__.py +0 -0
  61. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/pipeline/alert_policy.py +0 -0
  62. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/pipeline/core.py +0 -0
  63. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/plugins/__init__.py +0 -0
  64. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/plugins/alert_policies/__init__.py +0 -0
  65. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/plugins/alert_policies/default.py +0 -0
  66. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/plugins/alert_policies/noop.py +0 -0
  67. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/plugins/analyzers/__init__.py +0 -0
  68. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/plugins/analyzers/openai.py +0 -0
  69. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/plugins/filters/__init__.py +0 -0
  70. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/plugins/filters/yolo.py +0 -0
  71. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/plugins/notifiers/__init__.py +0 -0
  72. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/plugins/notifiers/mqtt.py +0 -0
  73. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/plugins/notifiers/multiplex.py +0 -0
  74. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/plugins/notifiers/sendgrid_email.py +0 -0
  75. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/plugins/storage/__init__.py +0 -0
  76. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/plugins/storage/dropbox.py +0 -0
  77. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/plugins/storage/local.py +0 -0
  78. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/plugins/utils.py +0 -0
  79. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/py.typed +0 -0
  80. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/repository/__init__.py +0 -0
  81. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/repository/clip_repository.py +0 -0
  82. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/sources/__init__.py +0 -0
  83. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/sources/base.py +0 -0
  84. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/sources/ftp.py +0 -0
  85. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/sources/local_folder.py +0 -0
  86. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/sources/rtsp.py +0 -0
  87. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/state/__init__.py +0 -0
  88. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/state/postgres.py +0 -0
  89. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/storage_paths.py +0 -0
  90. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/telemetry/__init__.py +0 -0
  91. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/telemetry/db/__init__.py +0 -0
  92. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/telemetry/db/log_table.py +0 -0
  93. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/telemetry/db_log_handler.py +0 -0
  94. {homesec-1.0.1 → homesec-1.1.0}/src/homesec/telemetry/postgres_settings.py +0 -0
  95. {homesec-1.0.1 → homesec-1.1.0}/tests/__init__.py +0 -0
  96. {homesec-1.0.1 → homesec-1.1.0}/tests/conftest.py +0 -0
  97. {homesec-1.0.1 → homesec-1.1.0}/tests/homesec/__init__.py +0 -0
  98. {homesec-1.0.1 → homesec-1.1.0}/tests/homesec/conftest.py +0 -0
  99. {homesec-1.0.1 → homesec-1.1.0}/tests/homesec/mocks/__init__.py +0 -0
  100. {homesec-1.0.1 → homesec-1.1.0}/tests/homesec/mocks/event_store.py +0 -0
  101. {homesec-1.0.1 → homesec-1.1.0}/tests/homesec/mocks/filter.py +0 -0
  102. {homesec-1.0.1 → homesec-1.1.0}/tests/homesec/mocks/notifier.py +0 -0
  103. {homesec-1.0.1 → homesec-1.1.0}/tests/homesec/mocks/state_store.py +0 -0
  104. {homesec-1.0.1 → homesec-1.1.0}/tests/homesec/mocks/storage.py +0 -0
  105. {homesec-1.0.1 → homesec-1.1.0}/tests/homesec/mocks/vlm.py +0 -0
  106. {homesec-1.0.1 → homesec-1.1.0}/tests/homesec/test_alert_policy.py +0 -0
  107. {homesec-1.0.1 → homesec-1.1.0}/tests/homesec/test_app.py +0 -0
  108. {homesec-1.0.1 → homesec-1.1.0}/tests/homesec/test_cleanup_clips.py +0 -0
  109. {homesec-1.0.1 → homesec-1.1.0}/tests/homesec/test_clip_repository.py +0 -0
  110. {homesec-1.0.1 → homesec-1.1.0}/tests/homesec/test_clip_sources.py +0 -0
  111. {homesec-1.0.1 → homesec-1.1.0}/tests/homesec/test_config.py +0 -0
  112. {homesec-1.0.1 → homesec-1.1.0}/tests/homesec/test_event_store.py +0 -0
  113. {homesec-1.0.1 → homesec-1.1.0}/tests/homesec/test_health.py +0 -0
  114. {homesec-1.0.1 → homesec-1.1.0}/tests/homesec/test_integration.py +0 -0
  115. {homesec-1.0.1 → homesec-1.1.0}/tests/homesec/test_local_folder_deduplication.py +0 -0
  116. {homesec-1.0.1 → homesec-1.1.0}/tests/homesec/test_notifiers.py +0 -0
  117. {homesec-1.0.1 → homesec-1.1.0}/tests/homesec/test_pipeline.py +0 -0
  118. {homesec-1.0.1 → homesec-1.1.0}/tests/homesec/test_pipeline_events.py +0 -0
  119. {homesec-1.0.1 → homesec-1.1.0}/tests/homesec/test_plugin_registration.py +0 -0
  120. {homesec-1.0.1 → homesec-1.1.0}/tests/homesec/test_rtsp_helpers.py +0 -0
  121. {homesec-1.0.1 → homesec-1.1.0}/tests/homesec/test_state_store.py +0 -0
  122. {homesec-1.0.1 → homesec-1.1.0}/tests/homesec/test_yolo_filter.py +0 -0
@@ -5,6 +5,10 @@ on:
5
5
  branches: [main, master]
6
6
  pull_request:
7
7
 
8
+ concurrency:
9
+ group: ${{ github.workflow }}-${{ github.ref }}
10
+ cancel-in-progress: true
11
+
8
12
  jobs:
9
13
  ci:
10
14
  runs-on: ubuntu-latest
@@ -40,7 +44,7 @@ jobs:
40
44
  key: venv-${{ runner.os }}-py3.14-${{ hashFiles('uv.lock') }}
41
45
 
42
46
  - name: Sync dependencies
43
- run: uv sync
47
+ run: uv sync --group dev
44
48
 
45
49
  # Lint and typecheck run while Postgres boots
46
50
  - name: Lint
@@ -61,5 +65,12 @@ jobs:
61
65
  - name: Run migrations
62
66
  run: uv run alembic -c alembic.ini upgrade head
63
67
 
64
- - name: Test
65
- run: make test
68
+ - name: Test with coverage
69
+ run: make coverage
70
+
71
+ - name: Upload coverage to Codecov
72
+ uses: codecov/codecov-action@v5
73
+ with:
74
+ token: ${{ secrets.CODECOV_TOKEN }}
75
+ files: coverage.xml
76
+ fail_ci_if_error: false
@@ -1,6 +1,6 @@
1
1
  # HomeSec Development Guidelines
2
2
 
3
- **Last reviewed:** 2025-01-04
3
+ **Last reviewed:** 2025-01-11
4
4
  **Purpose:** Critical patterns to prevent runtime bugs when extending HomeSec. For architecture overview, see `DESIGN.md`.
5
5
 
6
6
  ---
@@ -11,7 +11,7 @@
11
11
  - **Program to interfaces**: Use factory/registry helpers (e.g., `load_filter_plugin()`). Avoid direct instantiation of plugins.
12
12
  - **Repository pattern**: Use `ClipRepository` for all state/event writes. Never touch `StateStore`/`EventStore` directly.
13
13
  - **Preserve stack traces**: Custom errors must set `self.__cause__ = cause` to preserve original exception.
14
- - **Tests must use Given/When/Then comments**: Every test case must include these comments (new or edited).
14
+ - **Tests must use Given/When/Then comments**: Every test must include these comments and follow behavioral testing principles (see `TESTING.md`).
15
15
  - **Postgres for state**: Use `clip_states` table with `clip_id` (primary key) + `data` (jsonb) for evolvable schema.
16
16
  - **Pydantic everywhere**: Validate config, DB payloads, VLM outputs, and MQTT payloads with Pydantic models.
17
17
  - **Clarify before complexity**: Ask user for clarification when simpler design may exist. Don't proceed with complex workarounds.
@@ -277,13 +277,20 @@ def register():
277
277
 
278
278
  ### 5. Testing Requirements
279
279
 
280
- **Rule:** All tests must use Given/When/Then comments. Use mocks from `tests/homesec/mocks/`.
280
+ **Rule:** All tests must use Given/When/Then comments. Follow principles in `TESTING.md` for writing high-quality tests.
281
+
282
+ **Key Principles (see `TESTING.md` for full details):**
283
+ - **Test observable behavior, not internal state** - Never assert on `obj._private_attr`
284
+ - **Mock at the boundary** - Mock HTTP/network, not internal methods
285
+ - **Use contract testing** - Capture and verify API request structure
286
+ - **Use real implementations where cheap** - Filesystem via `tmp_path`, pure functions
287
+ - **Track API calls, not state flags** - Use `api_calls: list[str]` not `was_called: bool`
281
288
 
282
289
  **✅ Template: Test with Given/When/Then**
283
290
 
284
291
  ```python
285
292
  async def test_filter_stage_success():
286
- # Given: A clip with person detected
293
+ # Given: A clip and a filter configured to detect person
287
294
  clip = Clip(clip_id="test-001", camera_name="front_door", ...)
288
295
  mock_filter = MockFilter(result=FilterResult(detected_classes=["person"], confidence=0.9))
289
296
  pipeline = ClipPipeline(filter_plugin=mock_filter, ...)
@@ -294,7 +301,7 @@ async def test_filter_stage_success():
294
301
  # Then: Should return FilterResult with detected person
295
302
  assert isinstance(result, FilterResult)
296
303
  assert "person" in result.detected_classes
297
- assert mock_filter.detect_count == 1
304
+ assert result.confidence == 0.9
298
305
 
299
306
  async def test_filter_stage_failure():
300
307
  # Given: A filter that simulates failure
@@ -304,14 +311,14 @@ async def test_filter_stage_failure():
304
311
  # When: Processing clip through filter stage
305
312
  result = await pipeline._filter_stage(clip)
306
313
 
307
- # Then: Should return FilterError with cause preserved
314
+ # Then: Should return FilterError (error-as-value pattern)
308
315
  assert isinstance(result, FilterError)
309
- assert result.cause is not None
310
316
  assert result.clip_id == clip.clip_id
311
317
  ```
312
318
 
313
319
  **Available Mocks:** `MockFilter`, `MockVLM`, `MockStorage`, `MockNotifier`, `MockStateStore`, `MockEventStore`
314
- All mocks support `simulate_failure=True` and track call counts.
320
+
321
+ **Reference:** See `TESTING.md` for comprehensive testing guidelines and anti-patterns to avoid.
315
322
 
316
323
  ---
317
324
 
@@ -404,6 +411,7 @@ token: "sl.ABC123..." # Don't do this!
404
411
 
405
412
  **Architecture & Design:**
406
413
  - See `DESIGN.md` for full architecture overview and design decisions
414
+ - See `TESTING.md` for comprehensive testing guidelines and principles
407
415
  - See `src/homesec/interfaces.py` for complete interface definitions
408
416
 
409
417
  **Plugin Development:**
@@ -0,0 +1,45 @@
1
+ # CHANGELOG
2
+
3
+ <!-- version list -->
4
+
5
+ ## v1.1.0 (2026-01-12)
6
+
7
+ ### Bug Fixes
8
+
9
+ - Add Codecov token to CI workflow ([#5](https://github.com/lan17/homesec/pull/5),
10
+ [`6657cf2`](https://github.com/lan17/homesec/commit/6657cf2e94fe7bf8d0eaed39cfa98242f9766188))
11
+
12
+ - Correct Codecov badge URL case (HomeSec) ([#6](https://github.com/lan17/homesec/pull/6),
13
+ [`74019fe`](https://github.com/lan17/homesec/commit/74019fe4b313d588a72e96ff86a7b647e69c8150))
14
+
15
+ ### Features
16
+
17
+ - Add code coverage and improve documentation ([#4](https://github.com/lan17/homesec/pull/4),
18
+ [`b1d9490`](https://github.com/lan17/homesec/commit/b1d949057db675ed20f2623f06cef1fd58c4dcc3))
19
+
20
+ ### Testing
21
+
22
+ - Improve code coverage from 64% to 70% ([#7](https://github.com/lan17/homesec/pull/7),
23
+ [`7b93468`](https://github.com/lan17/homesec/commit/7b934685e8389bb24c8cc8878cb43b5570f5371e))
24
+
25
+
26
+ ## v1.0.2 (2026-01-11)
27
+
28
+ ### Bug Fixes
29
+
30
+ - CI and release workflow improvements ([#3](https://github.com/lan17/homesec/pull/3),
31
+ [`ab61e47`](https://github.com/lan17/homesec/commit/ab61e47c7c16c79e8472807c07e4974e31a13c29))
32
+
33
+
34
+ ## v1.0.1 (2026-01-11)
35
+
36
+ ### Bug Fixes
37
+
38
+ - Improve release workflow dry run and prevent major version jumps
39
+ ([#2](https://github.com/lan17/HomeSec/pull/2),
40
+ [`ba02512`](https://github.com/lan17/HomeSec/commit/ba025129de5caa984552807e4e0f376666db485e))
41
+
42
+
43
+ ## v1.0.0 (2026-01-11)
44
+
45
+ - Initial Release
@@ -1,7 +1,7 @@
1
1
  SHELL := /bin/bash
2
2
  .SHELLFLAGS := -eu -o pipefail -c
3
3
 
4
- .PHONY: help up down docker-build docker-push run db test typecheck lint check db-migrate db-migration publish
4
+ .PHONY: help up down docker-build docker-push run db test coverage typecheck lint check db-migrate db-migration publish
5
5
 
6
6
  help:
7
7
  @echo "Targets:"
@@ -15,7 +15,8 @@ help:
15
15
  @echo " Local dev:"
16
16
  @echo " make run Run HomeSec locally (requires Postgres)"
17
17
  @echo " make db Start just Postgres"
18
- @echo " make test Run tests"
18
+ @echo " make test Run tests with coverage"
19
+ @echo " make coverage Run tests and generate HTML coverage report"
19
20
  @echo " make typecheck Run mypy"
20
21
  @echo " make lint Run ruff linter"
21
22
  @echo " make check Run lint + typecheck + test"
@@ -64,7 +65,11 @@ db:
64
65
  docker compose up -d postgres
65
66
 
66
67
  test:
67
- uv run pytest tests/homesec/ -v
68
+ uv run pytest tests/homesec/ -v --cov=homesec --cov-report=term-missing
69
+
70
+ coverage:
71
+ uv run pytest tests/homesec/ -v --cov=homesec --cov-report=html --cov-report=xml
72
+ @echo "Coverage report: htmlcov/index.html"
68
73
 
69
74
  typecheck:
70
75
  uv run mypy --package homesec --strict
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: homesec
3
- Version: 1.0.1
3
+ Version: 1.1.0
4
4
  Summary: Pluggable async home security camera pipeline with detection, VLM analysis, and alerts.
5
5
  Project-URL: Homepage, https://github.com/lan17/homesec
6
6
  Project-URL: Source, https://github.com/lan17/homesec
@@ -246,15 +246,31 @@ Description-Content-Type: text/markdown
246
246
  [![License: Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE)
247
247
  [![Python: 3.10+](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/)
248
248
  [![Typing: Typed](https://img.shields.io/badge/typing-typed-2b825b)](https://peps.python.org/pep-0561/)
249
+ [![codecov](https://codecov.io/gh/lan17/HomeSec/branch/main/graph/badge.svg)](https://codecov.io/gh/lan17/HomeSec)
249
250
 
250
251
  HomeSec is a self-hosted, extensible network video recorder that puts you in control. Store clips wherever you want, analyze them with AI, and get smart notifications—all while keeping your footage private and off third-party clouds.
251
252
 
252
- Under the hood, it's a pluggable async pipeline for home security cameras. It records short clips, runs object detection, optionally calls a vision-language model (VLM) for a structured summary, and sends alerts via MQTT or email. The design leans toward reliability: clips land on disk first, state/event writes are best-effort, and non-critical stages can fail without losing the alert.
253
+ Under the hood, it's a pluggable async pipeline for home security cameras. It records short clips, runs object detection, optionally calls a vision-language model ([VLM](https://en.wikipedia.org/wiki/Vision%E2%80%93language_model)) for a structured summary, and sends alerts via [MQTT](https://en.wikipedia.org/wiki/MQTT) or email. The design prioritizes reliability and extensibility.
254
+
255
+ ## Table of Contents
256
+
257
+ - [Highlights](#highlights)
258
+ - [Pipeline at a glance](#pipeline-at-a-glance)
259
+ - [Quickstart](#quickstart)
260
+ - [Configuration](#configuration)
261
+ - [Extensible by design](#extensible-by-design)
262
+ - [CLI](#cli)
263
+ - [Built-in plugins](#built-in-plugins)
264
+ - [Writing a plugin](#writing-a-plugin)
265
+ - [Observability](#observability)
266
+ - [Development](#development)
267
+ - [Contributing](#contributing)
268
+ - [License](#license)
253
269
 
254
270
  ## Highlights
255
271
 
256
- - Bring your own input: RTSP motion detection, FTP uploads, or a watched folder
257
- - Parallel upload + filter (YOLOv8) with frame sampling and early exit
272
+ - Multiple pluggable video clip sources: [RTSP](https://en.wikipedia.org/wiki/Real-Time_Streaming_Protocol) motion detection, [FTP](https://en.wikipedia.org/wiki/File_Transfer_Protocol) uploads, or a watched folder
273
+ - Parallel upload + filter ([YOLOv8](https://en.wikipedia.org/wiki/You_Only_Look_Once)) with frame sampling and early exit
258
274
  - OpenAI-compatible VLM analysis with structured output
259
275
  - Policy-driven alerts with per-camera overrides
260
276
  - Fan-out notifiers (MQTT for Home Assistant, SendGrid email)
@@ -276,6 +292,7 @@ ClipSource -> (Upload + Filter) -> VLM (optional) -> Alert Policy -> Notifier(s)
276
292
 
277
293
  ### Requirements
278
294
 
295
+ - Raspberry Pi 4 (or equivalent) or higher; any x86_64 system works as well
279
296
  - Docker and Docker Compose
280
297
  - Optional: MQTT broker, Dropbox credentials, OpenAI-compatible API key
281
298
 
@@ -476,6 +493,30 @@ my_filters = "my_package.filters.custom"
476
493
  - Tests must include Given/When/Then comments.
477
494
  - Architecture notes: `DESIGN.md`
478
495
 
496
+ ## Contributing
497
+
498
+ Contributions are welcome! Here's how to get started:
499
+
500
+ 1. **Fork and clone** the repository
501
+ 2. **Create a branch** for your feature or fix: `git checkout -b my-feature`
502
+ 3. **Install dependencies**: `uv sync`
503
+ 4. **Make your changes** and ensure tests pass: `make check`
504
+ 5. **Submit a pull request** with a clear description of your changes
505
+
506
+ ### Guidelines
507
+
508
+ - All code must pass CI checks: `make check`
509
+ - Tests should include Given/When/Then comments explaining the test scenario
510
+ - New plugins should follow the existing patterns in `src/homesec/plugins/`
511
+ - Keep PRs focused on a single change for easier review
512
+
513
+ ### Reporting Issues
514
+
515
+ Found a bug or have a feature request? Please [open an issue](../../issues) with:
516
+ - A clear description of the problem or suggestion
517
+ - Steps to reproduce (for bugs)
518
+ - Your environment (OS, Python version, HomeSec version)
519
+
479
520
  ## License
480
521
 
481
522
  Apache 2.0. See `LICENSE`.
@@ -3,15 +3,31 @@
3
3
  [![License: Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE)
4
4
  [![Python: 3.10+](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/)
5
5
  [![Typing: Typed](https://img.shields.io/badge/typing-typed-2b825b)](https://peps.python.org/pep-0561/)
6
+ [![codecov](https://codecov.io/gh/lan17/HomeSec/branch/main/graph/badge.svg)](https://codecov.io/gh/lan17/HomeSec)
6
7
 
7
8
  HomeSec is a self-hosted, extensible network video recorder that puts you in control. Store clips wherever you want, analyze them with AI, and get smart notifications—all while keeping your footage private and off third-party clouds.
8
9
 
9
- Under the hood, it's a pluggable async pipeline for home security cameras. It records short clips, runs object detection, optionally calls a vision-language model (VLM) for a structured summary, and sends alerts via MQTT or email. The design leans toward reliability: clips land on disk first, state/event writes are best-effort, and non-critical stages can fail without losing the alert.
10
+ Under the hood, it's a pluggable async pipeline for home security cameras. It records short clips, runs object detection, optionally calls a vision-language model ([VLM](https://en.wikipedia.org/wiki/Vision%E2%80%93language_model)) for a structured summary, and sends alerts via [MQTT](https://en.wikipedia.org/wiki/MQTT) or email. The design prioritizes reliability and extensibility.
11
+
12
+ ## Table of Contents
13
+
14
+ - [Highlights](#highlights)
15
+ - [Pipeline at a glance](#pipeline-at-a-glance)
16
+ - [Quickstart](#quickstart)
17
+ - [Configuration](#configuration)
18
+ - [Extensible by design](#extensible-by-design)
19
+ - [CLI](#cli)
20
+ - [Built-in plugins](#built-in-plugins)
21
+ - [Writing a plugin](#writing-a-plugin)
22
+ - [Observability](#observability)
23
+ - [Development](#development)
24
+ - [Contributing](#contributing)
25
+ - [License](#license)
10
26
 
11
27
  ## Highlights
12
28
 
13
- - Bring your own input: RTSP motion detection, FTP uploads, or a watched folder
14
- - Parallel upload + filter (YOLOv8) with frame sampling and early exit
29
+ - Multiple pluggable video clip sources: [RTSP](https://en.wikipedia.org/wiki/Real-Time_Streaming_Protocol) motion detection, [FTP](https://en.wikipedia.org/wiki/File_Transfer_Protocol) uploads, or a watched folder
30
+ - Parallel upload + filter ([YOLOv8](https://en.wikipedia.org/wiki/You_Only_Look_Once)) with frame sampling and early exit
15
31
  - OpenAI-compatible VLM analysis with structured output
16
32
  - Policy-driven alerts with per-camera overrides
17
33
  - Fan-out notifiers (MQTT for Home Assistant, SendGrid email)
@@ -33,6 +49,7 @@ ClipSource -> (Upload + Filter) -> VLM (optional) -> Alert Policy -> Notifier(s)
33
49
 
34
50
  ### Requirements
35
51
 
52
+ - Raspberry Pi 4 (or equivalent) or higher; any x86_64 system works as well
36
53
  - Docker and Docker Compose
37
54
  - Optional: MQTT broker, Dropbox credentials, OpenAI-compatible API key
38
55
 
@@ -233,6 +250,30 @@ my_filters = "my_package.filters.custom"
233
250
  - Tests must include Given/When/Then comments.
234
251
  - Architecture notes: `DESIGN.md`
235
252
 
253
+ ## Contributing
254
+
255
+ Contributions are welcome! Here's how to get started:
256
+
257
+ 1. **Fork and clone** the repository
258
+ 2. **Create a branch** for your feature or fix: `git checkout -b my-feature`
259
+ 3. **Install dependencies**: `uv sync`
260
+ 4. **Make your changes** and ensure tests pass: `make check`
261
+ 5. **Submit a pull request** with a clear description of your changes
262
+
263
+ ### Guidelines
264
+
265
+ - All code must pass CI checks: `make check`
266
+ - Tests should include Given/When/Then comments explaining the test scenario
267
+ - New plugins should follow the existing patterns in `src/homesec/plugins/`
268
+ - Keep PRs focused on a single change for easier review
269
+
270
+ ### Reporting Issues
271
+
272
+ Found a bug or have a feature request? Please [open an issue](../../issues) with:
273
+ - A clear description of the problem or suggestion
274
+ - Steps to reproduce (for bugs)
275
+ - Your environment (OS, Python version, HomeSec version)
276
+
236
277
  ## License
237
278
 
238
279
  Apache 2.0. See `LICENSE`.