Metadata-Version: 2.4
Name: home-topology
Version: 1.0.6
Summary: A platform-agnostic home topology kernel for modeling spaces and attaching behavior modules
Project-URL: Homepage, https://github.com/mjcumming/home-topology
Project-URL: Repository, https://github.com/mjcumming/home-topology
Project-URL: Documentation, https://github.com/mjcumming/home-topology#readme
Author-email: Michael Cumming <mjcumming@users.noreply.github.com>
License: MIT
License-File: LICENSE
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Requires-Python: >=3.12
Provides-Extra: dev
Requires-Dist: black>=23.0; extra == 'dev'
Requires-Dist: isort>=5.12.0; extra == 'dev'
Requires-Dist: mypy>=1.0; extra == 'dev'
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: ruff>=0.1.0; extra == 'dev'
Description-Content-Type: text/markdown

# home-topology

[![CI](https://github.com/mjcumming/home-topology/actions/workflows/ci.yml/badge.svg)](https://github.com/mjcumming/home-topology/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Python 3.12+](https://img.shields.io/badge/python-3.12+-blue.svg)](https://www.python.org/downloads/)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)

> A platform-agnostic **home topology kernel** for modeling spaces (Locations), attaching behavior (Modules), and wiring everything together with a location-aware **Event Bus**.

`home-topology` is the structural backbone for smart homes:

- It models **where things are** (rooms, floors, zones, virtual spaces).
- It lets you attach **modules** like Occupancy, Automation, Ambient, Comfort, Energy.
- It routes **events** through this topology so modules can react cleanly.
- It stays **independent of Home Assistant** or any specific platform.

Think of it as a tiny "operating system" for your home's spatial model.  
Occupancy, automations, energy logic, etc. are apps running on top.

---

## Why Home Topology?

If every room had a perfect presence sensor, occupancy detection would be trivial. But reality is different:

- **Sensors are expensive** – $30-50 per room adds up fast for a whole house
- **Batteries die** – Motion sensors need constant maintenance
- **Devices go offline** – More devices = more failure points
- **Coverage gaps** – Even good sensors miss corners and edges

**Home Topology lets you use what you already have:**

| Device | Occupancy Signal |
|--------|------------------|
| 💡 Light switch turned on | Someone's in the room |
| 🚪 Door opened | Someone entered |
| 📺 TV playing | Living room is occupied |
| 🌡️ Thermostat adjusted | Someone's home |
| 🔊 Speaker volume changed | Activity detected |

Combine signals from motion sensors, switches, doors, media players, and more into reliable occupancy detection – **without buying more hardware**.

Your storage room doesn't need a motion sensor. Turn on the light → room is occupied → lights turn off after timeout. Simple.

---

## Features

- 🧱 **Location graph (topology)**  
  Structured model of your home: house → floors → rooms → zones, with optional links to Home Assistant Areas.

- 🧠 **Modules as plug-ins**  
  Occupancy, Automation, Ambient, Comfort, Energy, etc. are independent modules that attach to Locations and react to events.

- 🔁 **Location-aware Event Bus**  
  Simple, synchronous event pipeline with filters for type / location / ancestors / descendants.
  Location mutations can publish core events (`location.created`,
  `location.renamed`, `location.parent_changed`, `location.deleted`,
  `location.reordered`) when `LocationManager` is attached to an event bus.

- 🧩 **Schema-driven configuration**  
  Each module exposes a config schema; UIs can render dynamic forms per location without custom frontend code.

- 🧪 **Platform agnostic**  
  Core library has **no** dependency on Home Assistant. HA support is a thin adapter layer.

- 💾 **Config & state evolution**  
  Modules can version and migrate their configs, and optionally dump/restore runtime state via the host platform.

---

## Installation

```bash
pip install home-topology
```

Or pin a specific version:

```bash
pip install home-topology==1.0.1
```

Install from source (development):

```bash
git clone https://github.com/mjcumming/home-topology.git
cd home-topology
pip install -e ".[dev]"
```

---

## Used by Topomation

The current Topomation Home Assistant integration uses these `home-topology` surfaces at runtime:

- Core: `LocationManager`, `EventBus`, `Event`, `EventFilter`
- Modules: `OccupancyModule`, `AutomationModule`, `AmbientLightModule`

Topomation relies on:

- Location tree and entity-to-location mapping
- Topology and semantic event routing (`occupancy.signal`, `occupancy.changed`, etc.)
- Occupancy runtime commands/state APIs (trigger/clear/vacate/lock family + timeout/state queries)
- Per-location automation module config/state persistence
- Ambient light lookups for location-level light state entities

---

## Core Concepts

### Location

A `Location` is a logical space: a room, floor, area, or virtual zone.

```python
from dataclasses import dataclass
from typing import Optional, Dict, List

@dataclass
class Location:
    id: str
    name: str
    parent_id: Optional[str]
    ha_area_id: Optional[str]           # optional link to a HA Area
    entity_ids: List[str]               # platform entity IDs mapped here
    modules: Dict[str, Dict]            # per-module config blobs
```

Locations form a **hierarchy** (e.g. `house → main_floor → kitchen → kitchen_table_zone`).

### LocationManager

`LocationManager` owns the **topology and config**, not the behavior.

Responsibilities:

* Store the location tree.
* Provide graph queries: `parent_of`, `children_of`, `ancestors_of`, `descendants_of`.
* Maintain canonical sibling ordering (`Location.order`) and indexed reorder operations.
* Maintain entity → location mappings.
* Store per-location module config:

  ```python
  location.modules["occupancy"]  # config for the Occupancy module on this location
  ```

It does **not** implement occupancy, energy, or automation logic.

### Event Bus

The **Event Bus** is a simple, synchronous dispatcher for domain events:

```python
from dataclasses import dataclass
from datetime import datetime
from typing import Optional, Dict, Any

@dataclass
class Event:
    type: str                  # "sensor.state_changed", "occupancy.changed", ...
    source: str                # "ha", "occupancy", "automation", ...
    location_id: Optional[str]
    entity_id: Optional[str]
    payload: Dict[str, Any]
    timestamp: datetime
```

* `publish(event)` synchronously delivers events to subscribers.
* Handlers are wrapped in `try/except` so one bad module cannot crash the kernel.
* Modules treat handlers as **fast and CPU-bound**.
* For I/O-heavy work, the host integration should offload asynchronously.

### Modules

Modules are plug-ins that add behavior to the topology:

* **OccupancyModule** – computes binary `occupied` / `vacant` per Location.
* **AutomationModule** – runs automations in response to semantic events.
* **AmbientLightModule** – resolves ambient light using local/ancestor sensors with fallback strategies.
* **ComfortModule** (future) – room comfort metrics.
* **EnergyModule** (future) – room-level energy and power.

A module:

* Receives events from the Event Bus.
* Uses the `LocationManager` to understand hierarchy.
* Maintains its own runtime state.
* Emits **semantic events** that other modules can consume.

Example interface (simplified):

```python
class LocationModule:
    id: str
    CURRENT_CONFIG_VERSION: int

    def attach(self, bus, loc_manager) -> None:
        """Register event subscriptions and capture references."""

    def default_config(self) -> dict:
        """Default per-location config."""

    def location_config_schema(self) -> dict:
        """JSON-schema-like definition for UI configuration."""

    def migrate_config(self, config: dict) -> dict:
        """Upgrade older config versions to CURRENT_CONFIG_VERSION."""

    def on_location_config_changed(self, location_id: str, config: dict) -> None:
        """React to config updates for a given location."""

    def dump_state(self) -> dict:
        """Optional: serialize runtime state (host is responsible for storage)."""

    def restore_state(self, state: dict) -> None:
        """Optional: restore runtime state from serialized form."""
```

---

## Quick Example

> **Note:** This is illustrative, not a final API.

```python
from datetime import UTC, datetime
from home_topology import Event, EventBus, LocationManager
from home_topology.modules.occupancy import OccupancyModule

# 1. Kernel components
loc_mgr = LocationManager()
bus = EventBus()
bus.set_location_manager(loc_mgr)
loc_mgr.set_event_bus(bus)

# 2. Create a simple topology
loc_mgr.create_location(
    id="main_floor",
    name="Main Floor",
    parent_id=None,
)

kitchen = loc_mgr.create_location(
    id="kitchen",
    name="Kitchen",
    parent_id="main_floor",
    ha_area_id="area.kitchen",
)

# Map a motion sensor entity to the kitchen
loc_mgr.add_entity_to_location("binary_sensor.kitchen_motion", "kitchen")

# 3. Attach the Occupancy module
occupancy = OccupancyModule()
occupancy.attach(bus, loc_mgr)

# Optionally override per-location config
loc_mgr.set_module_config(
    location_id="kitchen",
    module_id="occupancy",
    config={
        "version": occupancy.CURRENT_CONFIG_VERSION,
        "default_timeout": 300,
        "default_trailing_timeout": 120,
        "occupancy_strategy": "independent",
        "contributes_to_parent": True,
    },
)

# 4. Feed a normalized occupancy signal into the kernel
bus.publish(
    Event(
        type="occupancy.signal",
        source="ha_adapter",
        location_id="kitchen",
        entity_id="binary_sensor.kitchen_motion",
        payload={
            "event_type": "trigger",
            "source_id": "binary_sensor.kitchen_motion",
            "timeout": 300,
        },
        timestamp=datetime.now(UTC),
    )
)

# 5. Query occupancy state (implementation-dependent)
state = occupancy.get_location_state("kitchen")
print(state["occupied"] if state else None)
```

In a Home Assistant integration, you'd:

* Translate HA state changes → `Event`s.
* Expose module state back as HA entities.
* Optionally provide a UI to configure modules per location.

> **See the [Integration Guide](./docs/integration/integration-guide.md) for a complete, production-ready Home Assistant integration example.**

---

## Relationship to Home Assistant

`home-topology` is **not** a Home Assistant custom component.
It's a pure Python library that can *back* a HA integration (and other platforms).

A typical HA setup would add a custom integration (for example `custom_components/topomation/`) that:

* Builds a location graph from HA Areas / devices / entities.
* Feeds HA events into the Event Bus.
* Exposes module state (e.g., occupancy sensors) back to HA.
* Provides a UI for Locations and their modules (with an "Unassigned/Inbox" view for entities).

**Building an integration?** See the complete **[Integration Guide](./docs/integration/integration-guide.md)** for step-by-step instructions, patterns, and a full Home Assistant example.

---

## Project Status

This is a **work-in-progress** architecture focused on:

* Clean separation between topology, events, and behavior.
* Extensibility via modules.
* Strong testability in pure Python (without spinning up HA).

Expect breaking changes while the core stabilizes.

---

## Development

### Quick Start

```bash
# Clone and setup
git clone https://github.com/mjcumming/home-topology.git
cd home-topology
python3 -m venv .venv
source .venv/bin/activate

# Install in development mode
make dev-install

# Run tests
make test

# Run example
make example

# Run all checks
make check
```

### Documentation

**📖 Start Here**:
- **[README.md](./README.md)** - This file (project overview)
- **[docs/project-status.md](./docs/project-status.md)** ⭐ - Current sprint status, task dashboard

**🔌 Integration**:
- **[docs/integration/integration-guide.md](./docs/integration/integration-guide.md)** ⭐ - Complete guide for platform integrators

**📊 Project**:
- **[docs/decisions-pending.md](./docs/decisions-pending.md)** - Decisions log (decided and deferred)
- **[SECURITY.md](./SECURITY.md)** - Security policy and vulnerability reporting

**🏗️ Architecture**:
- **[docs/architecture.md](./docs/architecture.md)** ⭐ - Complete kernel architecture specification
- **[docs/adr-log.md](./docs/adr-log.md)** - Architecture decision records with rationale
- **[docs/coding-standards.md](./docs/coding-standards.md)** - Code style and patterns
- **[CONTRIBUTING.md](./CONTRIBUTING.md)** - How to contribute

**📦 Modules**:
- **[docs/modules/occupancy-integration.md](./docs/modules/occupancy-integration.md)** ⭐ - Occupancy integration contract and mappings
- **[docs/modules/occupancy-presence-interaction.md](./docs/modules/occupancy-presence-interaction.md)** - Occupancy and presence interaction model
- **[docs/modules/automation-architecture.md](./docs/modules/automation-architecture.md)** - Automation module architecture
- **[docs/modules/ambient-module-design.md](./docs/modules/ambient-module-design.md)** - Ambient module design

**🧪 Testing**:
- **[docs/testing/guide.md](./docs/testing/guide.md)** - Full testing documentation
- **[docs/testing/commands.md](./docs/testing/commands.md)** - Quick test reference card

**📚 Reference**:
- **[docs/project-overview.md](./docs/project-overview.md)** - Detailed project guide
- **[docs/ai-development-guide.md](./docs/ai-development-guide.md)** - AI-assisted development guide
- **[CHANGELOG.md](./CHANGELOG.md)** - Version history

### Development Commands

```bash
make help          # Show all available commands
make test-cov      # Run tests with coverage
make format        # Format code with black
make lint          # Run ruff linter
make typecheck     # Run mypy type checker
make check         # Run all quality checks
```

---

## Contributing

Contributions are welcome! Please read [CONTRIBUTING.md](./CONTRIBUTING.md) before submitting PRs.

Key guidelines:
- Follow [docs/coding-standards.md](./docs/coding-standards.md)
- Add tests for new functionality
- Update documentation
- Run `make check` before committing

---

## License

MIT License - see [LICENSE](./LICENSE) for details.

---

## Links

- **Documentation**: [docs/](./docs/)
- **Issues**: [GitHub Issues](https://github.com/mjcumming/home-topology/issues)
- **Discussions**: [GitHub Discussions](https://github.com/mjcumming/home-topology/discussions)
