homesec 0.1.0__tar.gz → 1.0.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 (118) hide show
  1. homesec-1.0.0/.dockerignore +57 -0
  2. {homesec-0.1.0 → homesec-1.0.0}/.env.example +11 -12
  3. homesec-1.0.0/.github/workflows/ci.yml +65 -0
  4. homesec-1.0.0/.github/workflows/release.yaml +67 -0
  5. homesec-1.0.0/.github/workflows/validate-pr-title.yaml +20 -0
  6. homesec-1.0.0/.gitignore +11 -0
  7. homesec-1.0.0/CHANGELOG.md +7 -0
  8. homesec-1.0.0/Dockerfile +96 -0
  9. homesec-1.0.0/Makefile +98 -0
  10. {homesec-0.1.0 → homesec-1.0.0}/PKG-INFO +66 -31
  11. {homesec-0.1.0 → homesec-1.0.0}/README.md +65 -30
  12. {homesec-0.1.0 → homesec-1.0.0}/alembic/env.py +14 -3
  13. homesec-1.0.0/alembic/script.py.mako +26 -0
  14. homesec-1.0.0/alembic/versions/e6f25df0df90_initial.py +67 -0
  15. homesec-1.0.0/config/example.yaml +194 -0
  16. homesec-1.0.0/docker-compose.yml +42 -0
  17. homesec-1.0.0/docker-entrypoint.sh +20 -0
  18. {homesec-0.1.0 → homesec-1.0.0}/pyproject.toml +42 -1
  19. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/__init__.py +1 -1
  20. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/app.py +34 -36
  21. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/cli.py +14 -11
  22. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/config/loader.py +11 -11
  23. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/config/validation.py +2 -5
  24. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/errors.py +2 -4
  25. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/health/server.py +29 -27
  26. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/interfaces.py +11 -6
  27. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/logging_setup.py +9 -5
  28. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/maintenance/cleanup_clips.py +2 -3
  29. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/models/alert.py +2 -0
  30. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/models/clip.py +8 -1
  31. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/models/config.py +9 -13
  32. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/models/events.py +14 -0
  33. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/models/filter.py +1 -3
  34. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/models/vlm.py +1 -2
  35. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/pipeline/core.py +15 -32
  36. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/plugins/alert_policies/__init__.py +3 -4
  37. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/plugins/alert_policies/default.py +3 -2
  38. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/plugins/alert_policies/noop.py +1 -2
  39. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/plugins/analyzers/__init__.py +3 -4
  40. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/plugins/analyzers/openai.py +34 -43
  41. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/plugins/filters/__init__.py +3 -4
  42. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/plugins/filters/yolo.py +27 -29
  43. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/plugins/notifiers/__init__.py +2 -1
  44. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/plugins/notifiers/mqtt.py +16 -17
  45. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/plugins/notifiers/multiplex.py +3 -2
  46. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/plugins/notifiers/sendgrid_email.py +6 -8
  47. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/plugins/storage/__init__.py +3 -4
  48. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/plugins/storage/dropbox.py +20 -17
  49. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/plugins/storage/local.py +3 -1
  50. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/plugins/utils.py +2 -1
  51. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/repository/clip_repository.py +5 -4
  52. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/sources/base.py +2 -2
  53. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/sources/local_folder.py +9 -7
  54. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/sources/rtsp.py +22 -10
  55. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/state/postgres.py +34 -35
  56. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/telemetry/db_log_handler.py +3 -2
  57. {homesec-0.1.0 → homesec-1.0.0}/tests/homesec/conftest.py +6 -4
  58. {homesec-0.1.0 → homesec-1.0.0}/tests/homesec/mocks/filter.py +2 -2
  59. {homesec-0.1.0 → homesec-1.0.0}/tests/homesec/mocks/notifier.py +2 -2
  60. {homesec-0.1.0 → homesec-1.0.0}/tests/homesec/mocks/state_store.py +2 -2
  61. {homesec-0.1.0 → homesec-1.0.0}/tests/homesec/mocks/storage.py +2 -2
  62. {homesec-0.1.0 → homesec-1.0.0}/tests/homesec/mocks/vlm.py +2 -2
  63. {homesec-0.1.0 → homesec-1.0.0}/tests/homesec/test_app.py +3 -4
  64. {homesec-0.1.0 → homesec-1.0.0}/tests/homesec/test_clip_repository.py +10 -5
  65. {homesec-0.1.0 → homesec-1.0.0}/tests/homesec/test_clip_sources.py +41 -65
  66. {homesec-0.1.0 → homesec-1.0.0}/tests/homesec/test_config.py +1 -2
  67. {homesec-0.1.0 → homesec-1.0.0}/tests/homesec/test_dropbox_storage.py +3 -1
  68. {homesec-0.1.0 → homesec-1.0.0}/tests/homesec/test_event_store.py +2 -2
  69. {homesec-0.1.0 → homesec-1.0.0}/tests/homesec/test_health.py +35 -35
  70. {homesec-0.1.0 → homesec-1.0.0}/tests/homesec/test_integration.py +61 -58
  71. {homesec-0.1.0 → homesec-1.0.0}/tests/homesec/test_local_folder_deduplication.py +0 -1
  72. {homesec-0.1.0 → homesec-1.0.0}/tests/homesec/test_mqtt_notifier.py +6 -2
  73. {homesec-0.1.0 → homesec-1.0.0}/tests/homesec/test_openai_vlm.py +15 -5
  74. {homesec-0.1.0 → homesec-1.0.0}/tests/homesec/test_pipeline.py +49 -31
  75. {homesec-0.1.0 → homesec-1.0.0}/tests/homesec/test_plugin_registration.py +2 -6
  76. {homesec-0.1.0 → homesec-1.0.0}/tests/homesec/test_state_store.py +2 -4
  77. {homesec-0.1.0 → homesec-1.0.0}/tests/homesec/test_yolo_filter.py +0 -2
  78. {homesec-0.1.0 → homesec-1.0.0}/uv.lock +2 -2
  79. homesec-0.1.0/.gitignore +0 -4
  80. homesec-0.1.0/Makefile +0 -61
  81. homesec-0.1.0/config/example.yaml +0 -85
  82. homesec-0.1.0/config/local.yaml +0 -68
  83. homesec-0.1.0/config/production.yaml +0 -113
  84. homesec-0.1.0/config/sample.yaml +0 -66
  85. homesec-0.1.0/docker-compose.postgres.yml +0 -17
  86. {homesec-0.1.0 → homesec-1.0.0}/AGENTS.md +0 -0
  87. {homesec-0.1.0 → homesec-1.0.0}/DESIGN.md +0 -0
  88. {homesec-0.1.0 → homesec-1.0.0}/LICENSE +0 -0
  89. {homesec-0.1.0 → homesec-1.0.0}/alembic.ini +0 -0
  90. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/config/__init__.py +0 -0
  91. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/health/__init__.py +0 -0
  92. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/maintenance/__init__.py +0 -0
  93. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/models/__init__.py +1 -1
  94. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/models/source.py +0 -0
  95. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/models/storage.py +0 -0
  96. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/pipeline/__init__.py +0 -0
  97. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/pipeline/alert_policy.py +0 -0
  98. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/plugins/__init__.py +0 -0
  99. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/py.typed +0 -0
  100. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/repository/__init__.py +0 -0
  101. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/sources/__init__.py +0 -0
  102. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/sources/ftp.py +0 -0
  103. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/state/__init__.py +0 -0
  104. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/storage_paths.py +0 -0
  105. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/telemetry/__init__.py +0 -0
  106. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/telemetry/db/__init__.py +0 -0
  107. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/telemetry/db/log_table.py +0 -0
  108. {homesec-0.1.0 → homesec-1.0.0}/src/homesec/telemetry/postgres_settings.py +0 -0
  109. {homesec-0.1.0 → homesec-1.0.0}/tests/__init__.py +0 -0
  110. {homesec-0.1.0 → homesec-1.0.0}/tests/conftest.py +0 -0
  111. {homesec-0.1.0 → homesec-1.0.0}/tests/homesec/__init__.py +0 -0
  112. {homesec-0.1.0 → homesec-1.0.0}/tests/homesec/mocks/__init__.py +0 -0
  113. {homesec-0.1.0 → homesec-1.0.0}/tests/homesec/mocks/event_store.py +0 -0
  114. {homesec-0.1.0 → homesec-1.0.0}/tests/homesec/test_alert_policy.py +0 -0
  115. {homesec-0.1.0 → homesec-1.0.0}/tests/homesec/test_cleanup_clips.py +1 -1
  116. {homesec-0.1.0 → homesec-1.0.0}/tests/homesec/test_notifiers.py +0 -0
  117. {homesec-0.1.0 → homesec-1.0.0}/tests/homesec/test_pipeline_events.py +1 -1
  118. {homesec-0.1.0 → homesec-1.0.0}/tests/homesec/test_rtsp_helpers.py +0 -0
@@ -0,0 +1,57 @@
1
+ # Git
2
+ .git
3
+ .gitignore
4
+
5
+ # Python
6
+ __pycache__
7
+ *.py[cod]
8
+ *$py.class
9
+ *.so
10
+ .Python
11
+ .venv
12
+ venv
13
+ ENV
14
+ env
15
+
16
+ # Development
17
+ .env
18
+ .env.*
19
+ .mypy_cache
20
+ .pytest_cache
21
+ .ruff_cache
22
+ *.egg-info
23
+ dist
24
+ build
25
+
26
+ # IDE
27
+ .idea
28
+ .vscode
29
+ *.swp
30
+ *.swo
31
+
32
+ # Tests
33
+ tests
34
+ pytest.ini
35
+
36
+ # Documentation
37
+ docs
38
+ DESIGN.md
39
+ CLAUDE.md
40
+
41
+ # Local data (should be mounted as volumes)
42
+ recordings
43
+ storage
44
+ *.mp4
45
+ *.avi
46
+ *.mov
47
+ video_cache
48
+ yolo_cache
49
+ ftp_incoming
50
+
51
+ # Notebooks
52
+ notebooks
53
+ *.ipynb
54
+
55
+ # Config examples (actual config mounted at runtime)
56
+ config/*.yaml
57
+ !config/example.yaml
@@ -30,20 +30,19 @@ MQTT_PASSWORD="..."
30
30
  # SendGrid email (if using sendgrid_email notifier)
31
31
  SENDGRID_API_KEY="..."
32
32
 
33
- # Twilio SMS (if using Twilio notifier)
34
- TWILIO_ACCOUNT_SID="..."
35
- TWILIO_AUTH_TOKEN="..."
36
-
37
- # --- Telemetry logging (optional) ---
38
- # If DB_DSN is set, the process will attempt to write structured JSON logs to Postgres.
39
- # Run a local Postgres with: make db-up
40
- POSTGRES_USER="telemetry"
41
- POSTGRES_PASSWORD="telemetry"
42
- POSTGRES_DB="telemetry"
33
+ # --- Database (optional) ---
34
+ # If DB_DSN is set, the process will store clip state/events in Postgres.
35
+ # Run a local Postgres with: make db
36
+ POSTGRES_USER="homesec"
37
+ POSTGRES_PASSWORD="homesec"
38
+ POSTGRES_DB="homesec"
43
39
  POSTGRES_PORT="5432"
44
40
 
45
- # Example (local docker):
46
- DB_DSN="postgresql+asyncpg://telemetry:telemetry@127.0.0.1:5432/telemetry"
41
+ # For local dev (make db + make run):
42
+ DB_DSN="postgresql+asyncpg://homesec:homesec@localhost:5432/homesec"
43
+
44
+ # For docker-compose (make up) - set automatically, but can override:
45
+ # DB_DSN="postgresql+asyncpg://homesec:homesec@postgres:5432/homesec"
47
46
 
48
47
  # Optional tuning:
49
48
  # DB_LOG_LEVEL="INFO" # only logs >= this level go to DB
@@ -0,0 +1,65 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main, master]
6
+ pull_request:
7
+
8
+ jobs:
9
+ ci:
10
+ runs-on: ubuntu-latest
11
+ services:
12
+ postgres:
13
+ image: postgres:16-alpine
14
+ env:
15
+ POSTGRES_DB: homesec
16
+ POSTGRES_USER: homesec
17
+ POSTGRES_PASSWORD: homesec
18
+ ports:
19
+ - "5432:5432"
20
+ # No health check - Postgres boots while lint/typecheck run
21
+
22
+ env:
23
+ DB_DSN: postgresql+asyncpg://homesec:homesec@localhost:5432/homesec
24
+
25
+ steps:
26
+ - name: Checkout
27
+ uses: actions/checkout@v4
28
+
29
+ - name: Setup uv and Python
30
+ uses: astral-sh/setup-uv@v3
31
+ with:
32
+ python-version: "3.14"
33
+ enable-cache: true
34
+ cache-dependency-glob: "uv.lock"
35
+
36
+ - name: Cache venv
37
+ uses: actions/cache@v4
38
+ with:
39
+ path: .venv
40
+ key: venv-${{ runner.os }}-py3.14-${{ hashFiles('uv.lock') }}
41
+
42
+ - name: Sync dependencies
43
+ run: uv sync
44
+
45
+ # Lint and typecheck run while Postgres boots
46
+ - name: Lint
47
+ run: make lint
48
+
49
+ - name: Type check
50
+ run: make typecheck
51
+
52
+ # Wait for Postgres before migrations
53
+ - name: Wait for Postgres
54
+ run: |
55
+ for i in {1..30}; do
56
+ pg_isready -h localhost -p 5432 -U homesec && break
57
+ echo "Waiting for Postgres..."
58
+ sleep 1
59
+ done
60
+
61
+ - name: Run migrations
62
+ run: uv run alembic -c alembic.ini upgrade head
63
+
64
+ - name: Test
65
+ run: make test
@@ -0,0 +1,67 @@
1
+ name: Release
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ dry_run:
7
+ description: 'Dry run (no actual release)'
8
+ required: false
9
+ default: 'false'
10
+ type: boolean
11
+
12
+ jobs:
13
+ release:
14
+ runs-on: ubuntu-latest
15
+ permissions:
16
+ contents: write
17
+ id-token: write
18
+
19
+ steps:
20
+ - name: Checkout
21
+ uses: actions/checkout@v4
22
+ with:
23
+ fetch-depth: 0
24
+
25
+ - name: Setup Python
26
+ uses: actions/setup-python@v5
27
+ with:
28
+ python-version: "3.14"
29
+
30
+ - name: Install python-semantic-release
31
+ run: pip install python-semantic-release
32
+
33
+ - name: Semantic Release (dry run)
34
+ if: ${{ inputs.dry_run == 'true' }}
35
+ run: semantic-release version --noop
36
+
37
+ - name: Semantic Release
38
+ if: ${{ inputs.dry_run != 'true' }}
39
+ env:
40
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
41
+ run: |
42
+ git config user.name "github-actions[bot]"
43
+ git config user.email "github-actions[bot]@users.noreply.github.com"
44
+ semantic-release version
45
+
46
+ - name: Build Package
47
+ if: ${{ inputs.dry_run != 'true' }}
48
+ run: |
49
+ pip install build
50
+ python -m build
51
+
52
+ - name: Publish to PyPI
53
+ if: ${{ inputs.dry_run != 'true' }}
54
+ uses: pypa/gh-action-pypi-publish@release/v1
55
+ with:
56
+ verbose: true
57
+
58
+ - name: Create GitHub Release
59
+ if: ${{ inputs.dry_run != 'true' }}
60
+ env:
61
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
62
+ run: |
63
+ VERSION=$(grep 'version = ' pyproject.toml | head -1 | cut -d'"' -f2)
64
+ gh release create "v$VERSION" \
65
+ --title "v$VERSION" \
66
+ --generate-notes \
67
+ dist/*
@@ -0,0 +1,20 @@
1
+ name: Validate PR Title
2
+
3
+ on:
4
+ pull_request_target:
5
+ types:
6
+ - opened
7
+ - edited
8
+ - synchronize
9
+
10
+ permissions:
11
+ pull-requests: read
12
+
13
+ jobs:
14
+ main:
15
+ name: Validate PR title
16
+ runs-on: ubuntu-latest
17
+ steps:
18
+ - uses: amannn/action-semantic-pull-request@v5
19
+ env:
20
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -0,0 +1,11 @@
1
+ video_cache/
2
+ *.pt
3
+ .env
4
+ __pycache__/
5
+ dist
6
+ .idea
7
+ recordings
8
+ video_cache
9
+ yolo_cache
10
+ ftp_incoming
11
+ config/config.yaml
@@ -0,0 +1,7 @@
1
+ # CHANGELOG
2
+
3
+ <!-- version list -->
4
+
5
+ ## v1.0.0 (2026-01-11)
6
+
7
+ - Initial Release
@@ -0,0 +1,96 @@
1
+ # HomeSec Dockerfile
2
+ # Multi-stage build for minimal image size
3
+ #
4
+ # Build: docker build -t homesec .
5
+ # Run: docker run \
6
+ # -v ./config.yaml:/config/config.yaml \
7
+ # -v ./.env:/config/.env \
8
+ # -v ./recordings:/data/recordings \
9
+ # -v ./storage:/data/storage \
10
+ # -v ./yolo_cache:/app/yolo_cache \
11
+ # -p 8080:8080 homesec
12
+
13
+ # =============================================================================
14
+ # Stage 1: Builder
15
+ # =============================================================================
16
+ FROM python:3.14-slim-bookworm AS builder
17
+
18
+ # Install build dependencies
19
+ RUN apt-get update && apt-get install -y --no-install-recommends \
20
+ build-essential \
21
+ curl \
22
+ && rm -rf /var/lib/apt/lists/*
23
+
24
+ # Install uv for fast dependency management
25
+ COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
26
+
27
+ WORKDIR /app
28
+
29
+ # Copy dependency files first for better caching
30
+ COPY pyproject.toml uv.lock* LICENSE README.md ./
31
+
32
+ # Install dependencies into a virtual environment
33
+ RUN uv venv /app/.venv
34
+ ENV VIRTUAL_ENV=/app/.venv
35
+ ENV PATH="/app/.venv/bin:$PATH"
36
+
37
+ RUN uv sync --frozen --no-dev --no-install-project
38
+
39
+ # Copy source code
40
+ COPY src/ ./src/
41
+ COPY alembic/ ./alembic/
42
+ COPY alembic.ini ./
43
+
44
+ # Install the project
45
+ RUN uv sync --frozen --no-dev
46
+
47
+ # =============================================================================
48
+ # Stage 2: Runtime
49
+ # =============================================================================
50
+ FROM python:3.14-slim-bookworm AS runtime
51
+
52
+ # Install runtime dependencies
53
+ # - ffmpeg: required for RTSP source video processing
54
+ # - libgl1: required by OpenCV
55
+ # - libglib2.0-0: required by OpenCV
56
+ RUN apt-get update && apt-get install -y --no-install-recommends \
57
+ ffmpeg \
58
+ libgl1 \
59
+ libglib2.0-0 \
60
+ && rm -rf /var/lib/apt/lists/*
61
+
62
+ # Create non-root user for security
63
+ RUN useradd --create-home --shell /bin/bash homesec
64
+
65
+ WORKDIR /app
66
+
67
+ # Copy virtual environment from builder
68
+ COPY --from=builder /app/.venv /app/.venv
69
+ COPY --from=builder /app/alembic /app/alembic
70
+ COPY --from=builder /app/alembic.ini /app/alembic.ini
71
+
72
+ # Copy entrypoint script
73
+ COPY docker-entrypoint.sh /app/docker-entrypoint.sh
74
+
75
+ # Set up environment
76
+ ENV VIRTUAL_ENV=/app/.venv
77
+ ENV PATH="/app/.venv/bin:$PATH"
78
+ ENV PYTHONUNBUFFERED=1
79
+
80
+ # Create directories for volume mounts and make entrypoint executable
81
+ RUN chmod +x /app/docker-entrypoint.sh \
82
+ && mkdir -p /config /data/recordings /data/storage /app/yolo_cache \
83
+ && chown -R homesec:homesec /config /data /app
84
+
85
+ # Switch to non-root user
86
+ USER homesec
87
+
88
+ # Health check endpoint
89
+ EXPOSE 8080
90
+ HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
91
+ CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8080/health')" || exit 1
92
+
93
+ # Entrypoint runs migrations then starts app
94
+ # Config and env are expected to be mounted at /config/
95
+ ENTRYPOINT ["/app/docker-entrypoint.sh"]
96
+ CMD ["run", "--config", "/config/config.yaml", "--log_level", "INFO"]
homesec-1.0.0/Makefile ADDED
@@ -0,0 +1,98 @@
1
+ SHELL := /bin/bash
2
+ .SHELLFLAGS := -eu -o pipefail -c
3
+
4
+ .PHONY: help up down docker-build docker-push run db test typecheck lint check db-migrate db-migration publish
5
+
6
+ help:
7
+ @echo "Targets:"
8
+ @echo ""
9
+ @echo " Docker:"
10
+ @echo " make up Start HomeSec + Postgres"
11
+ @echo " make down Stop all services"
12
+ @echo " make docker-build Build Docker image"
13
+ @echo " make docker-push Push to DockerHub"
14
+ @echo ""
15
+ @echo " Local dev:"
16
+ @echo " make run Run HomeSec locally (requires Postgres)"
17
+ @echo " make db Start just Postgres"
18
+ @echo " make test Run tests"
19
+ @echo " make typecheck Run mypy"
20
+ @echo " make lint Run ruff linter"
21
+ @echo " make check Run lint + typecheck + test"
22
+ @echo ""
23
+ @echo " Database:"
24
+ @echo " make db-migrate Run migrations"
25
+ @echo " make db-migration m=\"description\" Generate new migration"
26
+ @echo ""
27
+ @echo " Release:"
28
+ @echo " make publish Build and upload to PyPI"
29
+
30
+ # Config
31
+ HOMESEC_CONFIG ?= config/config.yaml
32
+ HOMESEC_LOG_LEVEL ?= INFO
33
+ DOCKER_IMAGE ?= homesec
34
+ DOCKER_TAG ?= latest
35
+ DOCKERHUB_USER ?= $(shell echo $${DOCKERHUB_USER:-})
36
+
37
+ # Docker
38
+ up:
39
+ docker compose up -d --build
40
+
41
+ down:
42
+ docker compose down
43
+
44
+ docker-build:
45
+ docker build -t $(DOCKER_IMAGE):$(DOCKER_TAG) .
46
+
47
+ docker-push: docker-build
48
+ @if [ -z "$(DOCKERHUB_USER)" ]; then \
49
+ echo "Error: DOCKERHUB_USER not set. Run: export DOCKERHUB_USER=yourusername"; \
50
+ exit 1; \
51
+ fi
52
+ docker tag $(DOCKER_IMAGE):$(DOCKER_TAG) $(DOCKERHUB_USER)/$(DOCKER_IMAGE):$(DOCKER_TAG)
53
+ docker tag $(DOCKER_IMAGE):$(DOCKER_TAG) $(DOCKERHUB_USER)/$(DOCKER_IMAGE):latest
54
+ docker push $(DOCKERHUB_USER)/$(DOCKER_IMAGE):$(DOCKER_TAG)
55
+ docker push $(DOCKERHUB_USER)/$(DOCKER_IMAGE):latest
56
+
57
+ # Local dev
58
+ run:
59
+ @echo "Running database migrations..."
60
+ @uv run alembic -c alembic.ini upgrade head
61
+ uv run python -m homesec.cli run --config $(HOMESEC_CONFIG) --log_level $(HOMESEC_LOG_LEVEL)
62
+
63
+ db:
64
+ docker compose up -d postgres
65
+
66
+ test:
67
+ uv run pytest tests/homesec/ -v
68
+
69
+ typecheck:
70
+ uv run mypy --package homesec --strict
71
+
72
+ lint:
73
+ uv run ruff check src tests
74
+ uv run ruff format --check src tests
75
+
76
+ lint-fix:
77
+ uv run ruff check --fix src tests
78
+ uv run ruff format src tests
79
+
80
+ check: lint typecheck test
81
+
82
+ # Database
83
+ db-migrate:
84
+ uv run --with alembic --with sqlalchemy --with asyncpg --with python-dotenv alembic -c alembic.ini upgrade head
85
+
86
+ db-migration:
87
+ @if [ -z "$(m)" ]; then \
88
+ echo "Error: message required. Run: make db-migration m=\"your description\""; \
89
+ exit 1; \
90
+ fi
91
+ uv run --with alembic --with sqlalchemy --with asyncpg --with python-dotenv alembic -c alembic.ini revision --autogenerate -m "$(m)"
92
+
93
+ # Release
94
+ publish: check
95
+ rm -rf dist build
96
+ uv run --with build python -m build
97
+ uv run --with twine python -m twine check dist/*
98
+ uv run --with twine python -m twine upload dist/*
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: homesec
3
- Version: 0.1.0
3
+ Version: 1.0.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
@@ -247,11 +247,9 @@ Description-Content-Type: text/markdown
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
249
 
250
- HomeSec is a pluggable, async pipeline for home security cameras. It records
251
- short clips, runs object detection, optionally calls a vision-language model
252
- (VLM) for a structured summary, and sends alerts via MQTT or email. The design
253
- leans toward reliability: clips land on disk first, state/event writes are
254
- best-effort, and non-critical stages can fail without losing the alert.
250
+ 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
+ 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.
255
253
 
256
254
  ## Highlights
257
255
 
@@ -261,6 +259,7 @@ best-effort, and non-critical stages can fail without losing the alert.
261
259
  - Policy-driven alerts with per-camera overrides
262
260
  - Fan-out notifiers (MQTT for Home Assistant, SendGrid email)
263
261
  - Postgres-backed state + events with graceful degradation
262
+ - Built around small, stable interfaces so new plugins drop in cleanly
264
263
  - Health endpoint plus optional Postgres telemetry logging
265
264
 
266
265
  ## Pipeline at a glance
@@ -277,32 +276,42 @@ ClipSource -> (Upload + Filter) -> VLM (optional) -> Alert Policy -> Notifier(s)
277
276
 
278
277
  ### Requirements
279
278
 
280
- - Python 3.10+ (newest available is best; 3.14 is fine if your deps support it)
281
- - ffmpeg in PATH (required for RTSP source)
282
- - Postgres for state/events (`make db-up` starts a local instance). The pipeline
283
- continues if the DB is down, but a DSN is still required.
279
+ - Docker and Docker Compose
284
280
  - Optional: MQTT broker, Dropbox credentials, OpenAI-compatible API key
285
281
 
286
282
  ### Setup
287
283
 
288
- 1. Install dependencies:
289
- `uv sync`
290
- 2. Create a config file:
291
- - Start from `config/example.yaml` or `config/sample.yaml`
292
- 3. Set environment variables (use `.env.example` as a template):
293
- `cp .env.example .env`
294
- 4. Start Postgres:
295
- `make db-up`
296
- 5. Validate config:
297
- `uv run python -m homesec.cli validate --config config/example.yaml`
298
- 6. Run:
299
- `uv run python -m homesec.cli run --config config/example.yaml --log_level INFO`
300
-
301
- Tip: `make homesec` loads `.env` and runs `config/production.yaml`.
284
+ 1. Create a config file:
285
+ ```bash
286
+ cp config/example.yaml config/config.yaml
287
+ # Edit config/config.yaml with your settings
288
+ ```
289
+ 2. Set environment variables:
290
+ ```bash
291
+ cp .env.example .env
292
+ # Edit .env with your credentials
293
+ ```
294
+ 3. Start HomeSec + Postgres:
295
+ ```bash
296
+ make up
297
+ ```
298
+ 4. Stop:
299
+ ```bash
300
+ make down
301
+ ```
302
+
303
+ ### Running without Docker
304
+
305
+ If you prefer to run locally:
306
+
307
+ 1. Install Python 3.10+ and ffmpeg
308
+ 2. `uv sync`
309
+ 3. `make db` (starts Postgres)
310
+ 4. `make run`
302
311
 
303
312
  ## Configuration
304
313
 
305
- Configs are YAML and validated with Pydantic. Start with any file in `config/`.
314
+ Configs are YAML and validated with Pydantic. See `config/example.yaml` for all options.
306
315
 
307
316
  Minimal example (RTSP + Dropbox + MQTT):
308
317
 
@@ -362,8 +371,8 @@ per_camera_alert:
362
371
  A few things worth knowing:
363
372
  - Secrets never go in YAML. Use env var names (`*_env`) and set values in your shell or `.env`.
364
373
  - At least one notifier must be enabled (`mqtt` or `sendgrid_email`).
365
- - Built-in YOLO classes: `person`, `bird`, `cat`, `dog`, `horse`, `sheep`, `cow`,
366
- `elephant`, `bear`, `zebra`, `giraffe`.
374
+ - Built-in YOLO classes: `person`, `car`, `truck`, `motorcycle`, `bicycle`,
375
+ `dog`, `cat`, `bird`, `backpack`, `handbag`, `suitcase`.
367
376
  - Local storage for development:
368
377
 
369
378
  ```yaml
@@ -377,14 +386,35 @@ storage:
377
386
  - For a quick local run, pair `local_folder` with `local` storage and drop a clip
378
387
  into `recordings/`.
379
388
 
389
+ ## Extensible by design
390
+
391
+ HomeSec is intentionally modular. Each major capability is an interface
392
+ (`ClipSource`, `StorageBackend`, `ObjectFilter`, `VLMAnalyzer`, `AlertPolicy`,
393
+ `Notifier`) defined in `src/homesec/interfaces.py`, and plugins are discovered at
394
+ runtime via entry points. This keeps the core pipeline small while making it
395
+ easy to add new backends without editing core code.
396
+
397
+ What this means in practice:
398
+ - Swap storage or notifications by changing config, not code.
399
+ - Add a new plugin type as a separate package and register it.
400
+ - Keep config validation strict by pairing each plugin with a Pydantic model.
401
+
402
+ Extension points (all pluggable):
403
+ - Sources: RTSP motion detection, FTP uploads, local folders
404
+ - Storage backends: Dropbox, local disk, or your own
405
+ - Filters: object detection (YOLO or custom models)
406
+ - VLM analyzers: OpenAI-compatible APIs or local models
407
+ - Alert policies: per-camera rules and thresholds
408
+ - Notifiers: MQTT, email, or anything else you can send from Python
409
+
380
410
  ## CLI
381
411
 
382
412
  - Run the pipeline:
383
- `uv run python -m homesec.cli run --config config/example.yaml --log_level INFO`
413
+ `uv run python -m homesec.cli run --config config/config.yaml --log_level INFO`
384
414
  - Validate config:
385
- `uv run python -m homesec.cli validate --config config/example.yaml`
415
+ `uv run python -m homesec.cli validate --config config/config.yaml`
386
416
  - Cleanup (reanalyze and optionally delete empty clips):
387
- `uv run python -m homesec.cli cleanup --config config/example.yaml --older_than_days 7 --dry_run True`
417
+ `uv run python -m homesec.cli cleanup --config config/config.yaml --older_than_days 7 --dry_run True`
388
418
 
389
419
  ## Built-in plugins
390
420
 
@@ -399,6 +429,11 @@ storage:
399
429
  HomeSec discovers plugins via entry points in the `homesec.plugins` group. A plugin
400
430
  module just needs to import and register itself.
401
431
 
432
+ Each plugin provides:
433
+ - A unique name (used in config)
434
+ - A Pydantic config model for validation
435
+ - A factory that builds the concrete implementation
436
+
402
437
  ```python
403
438
  # my_package/filters/custom.py
404
439
  from pydantic import BaseModel
@@ -430,7 +465,7 @@ my_filters = "my_package.filters.custom"
430
465
 
431
466
  - Health endpoint: `GET /health` (configurable in `health.host`/`health.port`)
432
467
  - Optional telemetry logs to Postgres when `DB_DSN` is set:
433
- - Start local DB: `make db-up`
468
+ - Start local DB: `make db`
434
469
  - Run migrations: `make db-migrate`
435
470
 
436
471
  ## Development