Occupancy Manager
A hierarchical occupancy tracking engine with locking and identity logic.
Overview
Occupancy Manager is a pure Python library for managing hierarchical occupancy state. It accepts events from sensors, calculates the state of logical "Locations" (Rooms, Floors, Zones), and maintains a hierarchy where occupancy bubbles up from child to parent locations.
Features
- Hierarchical Location Tracking: Support for parent-child location relationships with upward propagation
- Identity Management: Track active occupants across locations with individual arrival/departure handling
- Locking Logic: Freeze location state when needed (party mode)
- Multiple Occupancy Strategies: Independent locations or locations that follow parent state
- Time-Agnostic: All time operations accept
nowas an argument (no system clock access) - Pure Python: No external dependencies, standard library only
- Event Types: Support for momentary events (motion), holds (presence/radar), and manual overrides
Installation
pip install occupancy-manager
Quick Start
from datetime import datetime, timedelta
from occupancy_manager import (
LocationConfig,
LocationKind,
OccupancyEvent,
EventType,
OccupancyEngine,
)
# Create location configuration
kitchen = LocationConfig(
id="kitchen",
kind=LocationKind.AREA,
timeouts={"motion": 10, "presence": 5}
)
# Initialize engine with list of configs
engine = OccupancyEngine([kitchen])
# Create an event
now = datetime.now()
event = OccupancyEvent(
location_id="kitchen",
event_type=EventType.MOMENTARY,
category="motion",
source_id="binary_sensor.kitchen_motion",
timestamp=now,
)
# Process event
result = engine.handle_event(event, now)
# Check for transitions
for transition in result.transitions:
print(f"{transition.location_id}: {'occupied' if transition.new_state.is_occupied else 'vacant'}")
print(f" Occupants: {transition.new_state.active_occupants}")
print(f" Expires at: {transition.new_state.occupied_until}")
# Check when next timeout check is needed
if result.next_expiration:
print(f"Next timeout check: {result.next_expiration}")
Identity Tracking Example
from datetime import datetime
from occupancy_manager import (
LocationConfig,
LocationKind,
OccupancyEvent,
EventType,
OccupancyEngine,
)
# Setup
kitchen = LocationConfig(id="kitchen", kind=LocationKind.AREA)
engine = OccupancyEngine([kitchen])
now = datetime.now()
# Mike arrives (Bluetooth presence start)
event = OccupancyEvent(
location_id="kitchen",
event_type=EventType.HOLD_START,
category="presence",
source_id="ble_mike",
timestamp=now,
occupant_id="Mike",
)
result = engine.handle_event(event, now)
print(f"Occupants: {engine.state['kitchen'].active_occupants}") # {'Mike'}
# Marla arrives
event = OccupancyEvent(
location_id="kitchen",
event_type=EventType.HOLD_START,
category="presence",
source_id="ble_marla",
timestamp=now,
occupant_id="Marla",
)
result = engine.handle_event(event, now)
print(f"Occupants: {engine.state['kitchen'].active_occupants}") # {'Mike', 'Marla'}
# Mike leaves (Bluetooth presence end)
event = OccupancyEvent(
location_id="kitchen",
event_type=EventType.HOLD_END,
category="presence",
source_id="ble_mike",
timestamp=now,
occupant_id="Mike",
)
result = engine.handle_event(event, now)
print(f"Occupants: {engine.state['kitchen'].active_occupants}") # {'Marla'}
print(f"Still occupied: {engine.state['kitchen'].is_occupied}") # True
Development
This project uses:
- Python 3.11+
rufffor linting and formattingmypyfor type checking (strict mode)pytestfor testing
Setup
# Create virtual environment
python -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
# Install in editable mode
pip install -e .
# Install development dependencies
pip install -e ".[dev]"
Running Tests
pytest
Running Linters
ruff check .
ruff format .
mypy src/
License
MIT License
Release files for occupancy-manager 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| occupancy_manager-0.1.0.tar.gz | 22.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| occupancy_manager-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 31.5 kB
Release files / occupancy_manager-0.1.0.tar.gz
| Download URL | occupancy_manager-0.1.0.tar.gz |
|---|---|
| Size | 22.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ef96fd9f40aaa884d477d6b8dddd8f901d2fa0408f45546de51779c670158fa5
|
|
BLAKE2b-256 checksum How to use checksums |
a1b493c99a2f7fce3a08022fb4e2c0c34a2065b77059674f67126de086cc0c1f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|
Release files / occupancy_manager-0.1.0-py3-none-any.whl
| Download URL | occupancy_manager-0.1.0-py3-none-any.whl |
|---|---|
| Size | 9.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7d541ebc0631d86262915fed6cc0fb31c69c55a06d7d03a38b712920be85a74c
|
|
BLAKE2b-256 checksum How to use checksums |
10f53c51f6109f4a3df2498325a457e691bb4d85e9638d2e92a2097caa062876
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|