homesec 0.1.1__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 (125) hide show
  1. homesec-1.0.0/.dockerignore +57 -0
  2. {homesec-0.1.1 → 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.1 → homesec-1.0.0}/PKG-INFO +39 -31
  11. {homesec-0.1.1 → homesec-1.0.0}/README.md +38 -30
  12. {homesec-0.1.1 → 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.1 → homesec-1.0.0}/pyproject.toml +42 -1
  19. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/app.py +34 -36
  20. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/cli.py +14 -11
  21. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/config/loader.py +11 -11
  22. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/config/validation.py +2 -5
  23. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/errors.py +2 -4
  24. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/health/server.py +29 -27
  25. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/interfaces.py +11 -6
  26. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/logging_setup.py +9 -5
  27. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/maintenance/cleanup_clips.py +2 -3
  28. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/models/alert.py +2 -0
  29. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/models/clip.py +8 -1
  30. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/models/config.py +9 -13
  31. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/models/events.py +14 -0
  32. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/models/filter.py +1 -3
  33. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/models/vlm.py +1 -2
  34. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/pipeline/core.py +15 -32
  35. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/plugins/alert_policies/__init__.py +3 -4
  36. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/plugins/alert_policies/default.py +3 -2
  37. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/plugins/alert_policies/noop.py +1 -2
  38. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/plugins/analyzers/__init__.py +3 -4
  39. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/plugins/analyzers/openai.py +34 -43
  40. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/plugins/filters/__init__.py +3 -4
  41. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/plugins/filters/yolo.py +27 -29
  42. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/plugins/notifiers/__init__.py +2 -1
  43. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/plugins/notifiers/mqtt.py +16 -17
  44. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/plugins/notifiers/multiplex.py +3 -2
  45. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/plugins/notifiers/sendgrid_email.py +6 -8
  46. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/plugins/storage/__init__.py +3 -4
  47. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/plugins/storage/dropbox.py +20 -17
  48. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/plugins/storage/local.py +3 -1
  49. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/plugins/utils.py +2 -1
  50. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/repository/clip_repository.py +5 -4
  51. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/sources/base.py +2 -2
  52. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/sources/local_folder.py +9 -7
  53. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/sources/rtsp.py +22 -10
  54. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/state/postgres.py +34 -35
  55. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/telemetry/db_log_handler.py +3 -2
  56. {homesec-0.1.1 → homesec-1.0.0}/tests/homesec/conftest.py +6 -4
  57. {homesec-0.1.1 → homesec-1.0.0}/tests/homesec/mocks/filter.py +2 -2
  58. {homesec-0.1.1 → homesec-1.0.0}/tests/homesec/mocks/notifier.py +2 -2
  59. {homesec-0.1.1 → homesec-1.0.0}/tests/homesec/mocks/state_store.py +2 -2
  60. {homesec-0.1.1 → homesec-1.0.0}/tests/homesec/mocks/storage.py +2 -2
  61. {homesec-0.1.1 → homesec-1.0.0}/tests/homesec/mocks/vlm.py +2 -2
  62. {homesec-0.1.1 → homesec-1.0.0}/tests/homesec/test_app.py +3 -4
  63. {homesec-0.1.1 → homesec-1.0.0}/tests/homesec/test_clip_repository.py +10 -5
  64. {homesec-0.1.1 → homesec-1.0.0}/tests/homesec/test_clip_sources.py +41 -65
  65. {homesec-0.1.1 → homesec-1.0.0}/tests/homesec/test_config.py +1 -2
  66. {homesec-0.1.1 → homesec-1.0.0}/tests/homesec/test_dropbox_storage.py +3 -1
  67. {homesec-0.1.1 → homesec-1.0.0}/tests/homesec/test_event_store.py +2 -2
  68. {homesec-0.1.1 → homesec-1.0.0}/tests/homesec/test_health.py +35 -35
  69. {homesec-0.1.1 → homesec-1.0.0}/tests/homesec/test_integration.py +61 -58
  70. {homesec-0.1.1 → homesec-1.0.0}/tests/homesec/test_local_folder_deduplication.py +0 -1
  71. {homesec-0.1.1 → homesec-1.0.0}/tests/homesec/test_mqtt_notifier.py +6 -2
  72. {homesec-0.1.1 → homesec-1.0.0}/tests/homesec/test_openai_vlm.py +15 -5
  73. {homesec-0.1.1 → homesec-1.0.0}/tests/homesec/test_pipeline.py +49 -31
  74. {homesec-0.1.1 → homesec-1.0.0}/tests/homesec/test_plugin_registration.py +2 -6
  75. {homesec-0.1.1 → homesec-1.0.0}/tests/homesec/test_state_store.py +2 -4
  76. {homesec-0.1.1 → homesec-1.0.0}/tests/homesec/test_yolo_filter.py +0 -2
  77. homesec-0.1.1/.gitignore +0 -5
  78. homesec-0.1.1/.idea/.gitignore +0 -3
  79. homesec-0.1.1/.idea/homesec.iml +0 -9
  80. homesec-0.1.1/.idea/libraries/my_test_package.xml +0 -9
  81. homesec-0.1.1/.idea/misc.xml +0 -6
  82. homesec-0.1.1/.idea/modules.xml +0 -8
  83. homesec-0.1.1/.idea/vcs.xml +0 -6
  84. homesec-0.1.1/.idea/workspace.xml +0 -57
  85. homesec-0.1.1/Makefile +0 -65
  86. homesec-0.1.1/config/example.yaml +0 -85
  87. homesec-0.1.1/config/local.yaml +0 -68
  88. homesec-0.1.1/config/production.yaml +0 -113
  89. homesec-0.1.1/config/sample.yaml +0 -66
  90. homesec-0.1.1/docker-compose.postgres.yml +0 -17
  91. {homesec-0.1.1 → homesec-1.0.0}/AGENTS.md +0 -0
  92. {homesec-0.1.1 → homesec-1.0.0}/DESIGN.md +0 -0
  93. {homesec-0.1.1 → homesec-1.0.0}/LICENSE +0 -0
  94. {homesec-0.1.1 → homesec-1.0.0}/alembic.ini +0 -0
  95. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/__init__.py +0 -0
  96. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/config/__init__.py +0 -0
  97. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/health/__init__.py +0 -0
  98. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/maintenance/__init__.py +0 -0
  99. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/models/__init__.py +1 -1
  100. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/models/source.py +0 -0
  101. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/models/storage.py +0 -0
  102. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/pipeline/__init__.py +0 -0
  103. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/pipeline/alert_policy.py +0 -0
  104. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/plugins/__init__.py +0 -0
  105. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/py.typed +0 -0
  106. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/repository/__init__.py +0 -0
  107. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/sources/__init__.py +0 -0
  108. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/sources/ftp.py +0 -0
  109. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/state/__init__.py +0 -0
  110. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/storage_paths.py +0 -0
  111. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/telemetry/__init__.py +0 -0
  112. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/telemetry/db/__init__.py +0 -0
  113. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/telemetry/db/log_table.py +0 -0
  114. {homesec-0.1.1 → homesec-1.0.0}/src/homesec/telemetry/postgres_settings.py +0 -0
  115. {homesec-0.1.1 → homesec-1.0.0}/tests/__init__.py +0 -0
  116. {homesec-0.1.1 → homesec-1.0.0}/tests/conftest.py +0 -0
  117. {homesec-0.1.1 → homesec-1.0.0}/tests/homesec/__init__.py +0 -0
  118. {homesec-0.1.1 → homesec-1.0.0}/tests/homesec/mocks/__init__.py +0 -0
  119. {homesec-0.1.1 → homesec-1.0.0}/tests/homesec/mocks/event_store.py +0 -0
  120. {homesec-0.1.1 → homesec-1.0.0}/tests/homesec/test_alert_policy.py +0 -0
  121. {homesec-0.1.1 → homesec-1.0.0}/tests/homesec/test_cleanup_clips.py +1 -1
  122. {homesec-0.1.1 → homesec-1.0.0}/tests/homesec/test_notifiers.py +0 -0
  123. {homesec-0.1.1 → homesec-1.0.0}/tests/homesec/test_pipeline_events.py +1 -1
  124. {homesec-0.1.1 → homesec-1.0.0}/tests/homesec/test_rtsp_helpers.py +0 -0
  125. {homesec-0.1.1 → homesec-1.0.0}/uv.lock +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.1
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
 
@@ -278,32 +276,42 @@ ClipSource -> (Upload + Filter) -> VLM (optional) -> Alert Policy -> Notifier(s)
278
276
 
279
277
  ### Requirements
280
278
 
281
- - Python 3.10+ (newest available is best; 3.14 is fine if your deps support it)
282
- - ffmpeg in PATH (required for RTSP source)
283
- - Postgres for state/events (`make db-up` starts a local instance). The pipeline
284
- continues if the DB is down, but a DSN is still required.
279
+ - Docker and Docker Compose
285
280
  - Optional: MQTT broker, Dropbox credentials, OpenAI-compatible API key
286
281
 
287
282
  ### Setup
288
283
 
289
- 1. Install dependencies:
290
- `uv sync`
291
- 2. Create a config file:
292
- - Start from `config/example.yaml` or `config/sample.yaml`
293
- 3. Set environment variables (use `.env.example` as a template):
294
- `cp .env.example .env`
295
- 4. Start Postgres:
296
- `make db-up`
297
- 5. Validate config:
298
- `uv run python -m homesec.cli validate --config config/example.yaml`
299
- 6. Run:
300
- `uv run python -m homesec.cli run --config config/example.yaml --log_level INFO`
301
-
302
- 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`
303
311
 
304
312
  ## Configuration
305
313
 
306
- 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.
307
315
 
308
316
  Minimal example (RTSP + Dropbox + MQTT):
309
317
 
@@ -363,8 +371,8 @@ per_camera_alert:
363
371
  A few things worth knowing:
364
372
  - Secrets never go in YAML. Use env var names (`*_env`) and set values in your shell or `.env`.
365
373
  - At least one notifier must be enabled (`mqtt` or `sendgrid_email`).
366
- - Built-in YOLO classes: `person`, `bird`, `cat`, `dog`, `horse`, `sheep`, `cow`,
367
- `elephant`, `bear`, `zebra`, `giraffe`.
374
+ - Built-in YOLO classes: `person`, `car`, `truck`, `motorcycle`, `bicycle`,
375
+ `dog`, `cat`, `bird`, `backpack`, `handbag`, `suitcase`.
368
376
  - Local storage for development:
369
377
 
370
378
  ```yaml
@@ -402,11 +410,11 @@ Extension points (all pluggable):
402
410
  ## CLI
403
411
 
404
412
  - Run the pipeline:
405
- `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`
406
414
  - Validate config:
407
- `uv run python -m homesec.cli validate --config config/example.yaml`
415
+ `uv run python -m homesec.cli validate --config config/config.yaml`
408
416
  - Cleanup (reanalyze and optionally delete empty clips):
409
- `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`
410
418
 
411
419
  ## Built-in plugins
412
420
 
@@ -457,7 +465,7 @@ my_filters = "my_package.filters.custom"
457
465
 
458
466
  - Health endpoint: `GET /health` (configurable in `health.host`/`health.port`)
459
467
  - Optional telemetry logs to Postgres when `DB_DSN` is set:
460
- - Start local DB: `make db-up`
468
+ - Start local DB: `make db`
461
469
  - Run migrations: `make db-migrate`
462
470
 
463
471
  ## Development