Feature Branch: 001-project-aether
Date: 2026-02-03
This document defines the core entities, their relationships, and state transitions for Project Aether. The model mirrors Home Assistant's internal architecture to expose all HA primitives to the agents.
The following table maps spec entities to their implementation status:
| Entity | Status | ORM Model |
|---|---|---|
| Floor | Not implemented | Area has nullable floor_id only |
| Area | Implemented | src/storage/entities/area.py |
| Label | Not implemented | — |
| Category | Not implemented | — |
| Device | Implemented | src/storage/entities/device.py |
| Entity (HAEntity) | Implemented | src/storage/entities/ha_entity.py |
| ConfigEntry | Not implemented | — |
| Integration | Not implemented | — |
| Helper | Not implemented | — |
| Scene | Implemented (partial) | src/storage/entities/ha_scene.py (entity_states null until MCP supports) |
| Script | Implemented (partial) | src/storage/entities/ha_script.py (sequence null until MCP supports) |
| Service | Implemented | src/storage/entities/ha_service.py |
| Event | Not implemented | — |
| HAAutomation | Implemented | src/storage/entities/ha_automation.py |
| DiscoverySession | Implemented | src/storage/entities/discovery_session.py |
| Agent | Implemented (expanded) | src/storage/entities/agent.py — includes config/prompt versioning (Feature 23) |
| Conversation | Implemented | src/storage/entities/conversation.py |
| Message | Implemented | src/storage/entities/message.py |
| AutomationProposal | Implemented | src/storage/entities/automation_proposal.py |
| Insight | Implemented | src/storage/entities/insight.py |
| Dashboard | Not implemented | — |
| Checkpoint | Implemented | src/storage/checkpoints.py (LangGraph managed) |
Additional entities in codebase (not in original spec):
| Entity | ORM Model | Purpose |
|---|---|---|
| UserProfile | src/storage/entities/user_profile.py |
User accounts (password, Google OAuth) |
| PasskeyCredential | src/storage/entities/passkey_credential.py |
WebAuthn passkey storage |
| SystemConfig | src/storage/entities/system_config.py |
HA URL/token (encrypted), setup state |
| HAZone | src/storage/entities/ha_zone.py |
HA zone connection configs |
| InsightSchedule | src/storage/entities/insight_schedule.py |
Cron/webhook insight triggers |
| LLMUsage | src/storage/entities/llm_usage.py |
Token counts, costs, latency per LLM call |
| FlowGrade | src/storage/entities/flow_grade.py |
Conversation quality grades |
┌──────────────────────────────────────────────────────────────────────────────┐
│ HOME ASSISTANT CORE │
├──────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ ORGANIZATIONAL REGISTRIES │ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ Floor │───>│ Area │ │ Label │ │ Category │ │ │
│ │ └──────────┘ └────┬─────┘ └──────────┘ └──────────┘ │ │
│ │ │ │ tags │ │ │
│ └────────────────────────│───────────────│────────────────│──────────┘ │
│ │ │ │ │
│ ┌────────────────────────▼───────────────▼────────────────▼──────────┐ │
│ │ DEVICE REGISTRY │ │
│ │ │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ Device │ │ Device │ │ Device │ ... │ │
│ │ │ (Hub/GW) │ │ (Thermostat) │ │ (Sensor) │ │ │
│ │ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │ │
│ │ │ │ │ │ │
│ └──────────│───────────────────│───────────────────│──────────────────┘ │
│ │ │ │ │
│ ┌──────────▼───────────────────▼───────────────────▼──────────────────┐ │
│ │ ENTITY REGISTRY │ │
│ │ │ │
│ │ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐│ │
│ │ │ light. │ │switch. │ │sensor. │ │binary_ │ │climate.│ │ cover. ││ │
│ │ │ │ │ │ │ │ │sensor. │ │ │ │ ││ │
│ │ └────────┘ └────────┘ └────────┘ └────────┘ └────────┘ └────────┘│ │
│ │ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐│ │
│ │ │ fan. │ │ lock. │ │ media_ │ │vacuum. │ │ camera.│ │ alarm_ ││ │
│ │ │ │ │ │ │ player.│ │ │ │ │ │control_││ │
│ │ └────────┘ └────────┘ └────────┘ └────────┘ └────────┘ └────────┘│ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ HELPER ENTITIES │ │
│ │ │ │
│ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │
│ │ │input_ │ │input_ │ │input_ │ │input_ │ │ │
│ │ │boolean │ │number │ │text │ │select │ │ │
│ │ └────────────┘ └────────────┘ └────────────┘ └────────────┘ │ │
│ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │
│ │ │input_ │ │input_ │ │counter │ │timer │ │ │
│ │ │datetime │ │button │ │ │ │ │ │ │
│ │ └────────────┘ └────────────┘ └────────────┘ └────────────┘ │ │
│ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │
│ │ │schedule │ │group │ │template │ │ │
│ │ │ │ │ │ │(sensor/etc)│ │ │
│ │ └────────────┘ └────────────┘ └────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ AUTOMATION ENGINE │ │
│ │ │ │
│ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │
│ │ │ automation │ │ script │ │ scene │ │ │
│ │ └────────────┘ └────────────┘ └────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ CONFIG & INTEGRATIONS │ │
│ │ │ │
│ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │
│ │ │config_entry│ │integration │ │ service │ │ │
│ │ └────────────┘ └────────────┘ └────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ EVENT BUS │ │
│ │ │ │
│ │ state_changed │ call_service │ automation_triggered │ custom │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────────┐
│ HA REGISTRY MIRROR │
├──────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Floor │────<│ Area │ │ Label │ │ Category │ │
│ └──────────┘ └────┬─────┘ └────┬─────┘ └──────────┘ │
│ │ │ │
│ ┌──────────┐ ┌────┴─────┐ ┌────┴─────┐ ┌──────────┐ │
│ │ConfigEntry│───>│ Device │<────│EntityLabel│<───│ Entity │ │
│ └──────────┘ └──────────┘ └──────────┘ └────┬─────┘ │
│ │ │ │
│ │ ┌────────────────────────────┬─────────┘ │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │Integration│ │ Helper │ │ Scene │ │ Script │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ │
│ ┌──────────┐ ┌──────────┐ │
│ │ Service │ │ Event │ │
│ └──────────┘ └──────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────────┐
│ AETHER AGENT LAYER │
├──────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Agent │────>│Conversation│──>│ Message │ │ Insight │ │
│ └──────────┘ └────┬─────┘ └──────────┘ └────┬─────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────┐ ┌──────────┐ │
│ │Automation│ │ Dashboard│ │
│ │ Proposal │ │ │ │
│ └──────────┘ └──────────┘ │
│ │
│ ┌──────────┐ ┌──────────┐ │
│ │Discovery │ │Checkpoint│ │
│ │ Session │ │(LangGraph)│ │
│ └──────────┘ └──────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────────┘
These entities mirror Home Assistant's internal registries to provide full access to all HA primitives.
Status: Not Yet Implemented — Area has a nullable
floor_idcolumn but no Floor table exists.
Represents a floor/level in the home (HA 2024.x+).
| Field | Type | Description | Constraints |
|---|---|---|---|
| id | UUID | Primary key | Generated |
| ha_floor_id | String | Home Assistant floor ID | Unique |
| name | String | Floor name | e.g., Ground Floor, Basement |
| level | Integer | Floor level number | 0 = ground, negative = below |
| icon | String | MDI icon | e.g., mdi:home-floor-1 |
| aliases | String[] | Alternative names | For NLP matching |
| created_at | Timestamp | Record creation | Immutable |
| updated_at | Timestamp | Last sync | Auto-updated |
Represents a physical area/room in the home.
| Field | Type | Description | Constraints |
|---|---|---|---|
| id | UUID | Primary key | Generated |
| ha_area_id | String | Home Assistant area ID | Unique |
| name | String | Area name | e.g., Living Room, Kitchen |
| floor_id | UUID | FK to Floor | Nullable |
| icon | String | MDI icon | e.g., mdi:sofa |
| picture | String | Area picture URL | Nullable |
| aliases | String[] | Alternative names | For NLP matching |
| created_at | Timestamp | Record creation | Immutable |
| updated_at | Timestamp | Last sync | Auto-updated |
Status: Not Yet Implemented
Custom tagging system for entities/devices/areas (HA 2024.x+).
| Field | Type | Description | Constraints |
|---|---|---|---|
| id | UUID | Primary key | Generated |
| ha_label_id | String | Home Assistant label ID | Unique |
| name | String | Label name | e.g., Energy Monitor, Critical |
| color | String | Hex color | e.g., #FF5722 |
| icon | String | MDI icon | e.g., mdi:tag |
| description | String | Label purpose | Nullable |
| created_at | Timestamp | Record creation | Immutable |
| updated_at | Timestamp | Last sync | Auto-updated |
Status: Not Yet Implemented
Domain-specific category for organization.
| Field | Type | Description | Constraints |
|---|---|---|---|
| id | UUID | Primary key | Generated |
| ha_category_id | String | Home Assistant category ID | Unique |
| domain | String | HA domain | automation, script, scene, helper |
| name | String | Category name | e.g., Lighting, Security |
| icon | String | MDI icon | Nullable |
| created_at | Timestamp | Record creation | Immutable |
| updated_at | Timestamp | Last sync | Auto-updated |
Represents a physical or logical device containing one or more entities.
| Field | Type | Description | Constraints |
|---|---|---|---|
| id | UUID | Primary key | Generated |
| ha_device_id | String | Home Assistant device ID | Unique |
| name | String | Device name | e.g., Living Room Thermostat |
| name_by_user | String | User-defined name | Nullable override |
| manufacturer | String | Device manufacturer | e.g., Philips, Aqara |
| model | String | Device model | e.g., Hue White Ambiance |
| model_id | String | Model identifier | Nullable |
| hw_version | String | Hardware version | Nullable |
| sw_version | String | Software/firmware version | Nullable |
| serial_number | String | Serial number | Nullable |
| area_id | UUID | FK to Area | Nullable |
| config_entry_id | UUID | FK to ConfigEntry | Required |
| via_device_id | UUID | FK to Device (parent) | For hubs/gateways |
| connections | JSONB | Network connections | MAC, IP, Zigbee addr, etc. |
| identifiers | JSONB | Unique identifiers | Integration-specific IDs |
| disabled_by | String | Disabled reason | user, integration, config_entry |
| entry_type | String | Device type | service, null for normal devices |
| labels | UUID[] | FK to Label | Many-to-many |
| created_at | Timestamp | First discovered | Immutable |
| updated_at | Timestamp | Last sync | Auto-updated |
Indexes: ha_device_id (unique), manufacturer, model, area_id, config_entry_id
Represents a Home Assistant entity (the controllable unit).
| Field | Type | Description | Constraints |
|---|---|---|---|
| id | UUID | Primary key | Generated |
| ha_entity_id | String | Home Assistant entity ID | Unique, e.g., light.living_room |
| unique_id | String | Integration unique ID | From integration |
| platform | String | Integration platform | e.g., hue, zwave_js |
| domain | String | HA domain | See Domain Reference below |
| friendly_name | String | Human-readable name | From HA attributes |
| original_name | String | Integration-provided name | Before user override |
| device_id | UUID | FK to Device | Nullable (not all have device) |
| area_id | UUID | FK to Area | Can override device's area |
| icon | String | MDI icon | User or integration-defined |
| entity_category | String | Entity category | config, diagnostic, or null |
| state | String | Current state | Domain-specific |
| attributes | JSONB | Full HA attributes | Brightness, temperature, etc. |
| capabilities | JSONB | Supported features | Domain-specific capabilities |
| supported_features | Integer | Feature bitmask | HA feature flags |
| device_class | String | Device class | temperature, motion, etc. |
| unit_of_measurement | String | Unit | °C, kWh, %, etc. |
| disabled_by | String | Disabled reason | user, integration, config_entry |
| hidden_by | String | Hidden reason | user, integration |
| labels | UUID[] | FK to Label | Many-to-many |
| last_reported | Timestamp | Last state report | From HA |
| last_changed | Timestamp | Last state change | From HA |
| last_updated | Timestamp | Last attribute update | From HA |
| created_at | Timestamp | First discovered | Immutable |
| updated_at | Timestamp | Last sync | Auto-updated |
Domain Reference:
| Domain | Description | Example Entities |
|---|---|---|
light |
Lighting control | Dimmable bulbs, LED strips |
switch |
Binary switch | Smart plugs, relays |
sensor |
Sensor readings | Temperature, humidity |
binary_sensor |
Binary state | Motion, door/window |
climate |
HVAC control | Thermostats, AC |
cover |
Covers/blinds | Curtains, garage doors |
fan |
Fan control | Ceiling fans, ventilation |
lock |
Lock control | Smart locks |
media_player |
Media devices | TVs, speakers |
vacuum |
Robot vacuums | Roomba, Roborock |
camera |
Camera feeds | IP cameras |
alarm_control_panel |
Security systems | Home alarm |
humidifier |
Humidity control | Humidifiers, dehumidifiers |
water_heater |
Water heating | Boilers |
button |
Trigger actions | Integration buttons |
number |
Numeric input | Volume, speed settings |
select |
Dropdown selection | Mode selection |
text |
Text input | Custom text fields |
siren |
Siren control | Alarms |
update |
Software updates | Firmware updates |
weather |
Weather data | Weather services |
person |
Person tracking | Presence detection |
zone |
Geographic zones | Home, Work |
sun |
Sun position | Sunrise/sunset |
device_tracker |
Device location | Phone, tracker |
Indexes: ha_entity_id (unique), domain, device_id, area_id, friendly_name (full-text)
Status: Not Yet Implemented
Represents an integration configuration (how integrations are set up).
| Field | Type | Description | Constraints |
|---|---|---|---|
| id | UUID | Primary key | Generated |
| ha_entry_id | String | Home Assistant config entry ID | Unique |
| domain | String | Integration domain | e.g., hue, zwave_js |
| title | String | User-friendly title | e.g., Hue Bridge |
| source | String | Entry source | user, discovery, import, etc. |
| state | String | Entry state | loaded, setup_error, not_loaded |
| disabled_by | String | Disabled reason | user or null |
| pref_disable_new_entities | Boolean | Auto-disable new entities | Default: false |
| pref_disable_polling | Boolean | Disable polling | Default: false |
| options | JSONB | Entry options | Integration-specific |
| version | Integer | Config version | For migrations |
| minor_version | Integer | Minor version | For migrations |
| created_at | Timestamp | Entry creation | From HA |
| updated_at | Timestamp | Last sync | Auto-updated |
Status: Not Yet Implemented
Represents an available HA integration (metadata).
| Field | Type | Description | Constraints |
|---|---|---|---|
| id | UUID | Primary key | Generated |
| domain | String | Integration domain | Unique, e.g., hue |
| name | String | Integration name | e.g., Philips Hue |
| documentation | String | Docs URL | Link to HA docs |
| codeowners | String[] | Maintainers | GitHub usernames |
| config_flow | Boolean | Has config flow | UI configuration |
| iot_class | String | IoT class | local_polling, cloud_push, etc. |
| quality_scale | String | Quality rating | platinum, gold, silver, bronze |
| requirements | String[] | Python packages | Dependencies |
| version | String | Integration version | For HACS integrations |
| is_built_in | Boolean | Core integration | vs HACS |
| created_at | Timestamp | Record creation | Immutable |
| updated_at | Timestamp | Last sync | Auto-updated |
Status: Not Yet Implemented
Represents a user-created helper entity.
| Field | Type | Description | Constraints |
|---|---|---|---|
| id | UUID | Primary key | Generated |
| entity_id | UUID | FK to Entity | Required |
| helper_type | String | Helper type | See Helper Types below |
| config | JSONB | Helper configuration | Type-specific |
| editable | Boolean | User-editable | Usually true |
| category_id | UUID | FK to Category | Optional |
| created_at | Timestamp | Record creation | Immutable |
| updated_at | Timestamp | Last sync | Auto-updated |
Helper Types:
| Type | Domain | Purpose | Config Fields |
|---|---|---|---|
input_boolean |
input_boolean |
Toggle state | initial, icon |
input_number |
input_number |
Numeric value | min, max, step, mode, unit_of_measurement |
input_text |
input_text |
Text value | min, max, pattern, mode |
input_select |
input_select |
Dropdown | options, initial |
input_datetime |
input_datetime |
Date/time | has_date, has_time, initial |
input_button |
input_button |
Press trigger | icon |
counter |
counter |
Increment/decrement | initial, minimum, maximum, step, restore |
timer |
timer |
Countdown timer | duration, restore |
schedule |
schedule |
Weekly schedule | Complex schedule blocks |
group |
group |
Entity grouping | entities, all (require all on) |
template |
sensor/binary_sensor/etc. |
Template-based | value_template, attribute_templates |
Represents a Home Assistant scene (snapshot of entity states).
| Field | Type | Description | Constraints |
|---|---|---|---|
| id | UUID | Primary key | Generated |
| ha_scene_id | String | Home Assistant scene ID | Unique |
| entity_id | UUID | FK to Entity | scene.* entity |
| name | String | Scene name | e.g., Movie Night |
| icon | String | MDI icon | Nullable |
| entity_states | JSONB | Target states | {entity_id: {state, attributes}} |
| category_id | UUID | FK to Category | Optional |
| created_at | Timestamp | Record creation | Immutable |
| updated_at | Timestamp | Last sync | Auto-updated |
Represents a Home Assistant script (reusable action sequence).
| Field | Type | Description | Constraints |
|---|---|---|---|
| id | UUID | Primary key | Generated |
| ha_script_id | String | Home Assistant script ID | Unique, e.g., script.notify_all |
| entity_id | UUID | FK to Entity | script.* entity |
| alias | String | Script name | Human-readable |
| description | String | Script description | Nullable |
| icon | String | MDI icon | Nullable |
| mode | String | Execution mode | single, restart, queued, parallel |
| max | Integer | Max parallel runs | For queued/parallel modes |
| max_exceeded | String | Overflow action | silent, warning, error |
| sequence | JSONB | Action sequence | List of HA actions |
| fields | JSONB | Input fields | Script parameters |
| variables | JSONB | Script variables | Template variables |
| category_id | UUID | FK to Category | Optional |
| trace_enabled | Boolean | Enable tracing | Default: true |
| last_triggered | Timestamp | Last execution | From HA |
| created_at | Timestamp | Record creation | Immutable |
| updated_at | Timestamp | Last sync | Auto-updated |
Represents an available HA service (callable actions).
| Field | Type | Description | Constraints |
|---|---|---|---|
| id | UUID | Primary key | Generated |
| domain | String | Service domain | e.g., light, homeassistant |
| service | String | Service name | e.g., turn_on, reload |
| name | String | Human-readable name | e.g., Turn on |
| description | String | Service description | What it does |
| fields | JSONB | Service parameters | Schema for each field |
| target | JSONB | Target specification | Entity/device/area selectors |
| response | JSONB | Response schema | For services with responses |
| created_at | Timestamp | Record creation | Immutable |
| updated_at | Timestamp | Last sync | Auto-updated |
Common Service Patterns:
| Domain | Services | Description |
|---|---|---|
homeassistant |
turn_on, turn_off, toggle, reload |
Generic entity control |
light |
turn_on, turn_off, toggle |
Light control with brightness/color |
switch |
turn_on, turn_off, toggle |
Switch control |
climate |
set_temperature, set_hvac_mode, set_preset_mode |
HVAC control |
cover |
open_cover, close_cover, set_cover_position |
Cover control |
media_player |
play_media, volume_set, media_pause |
Media control |
notify |
* |
Notification services |
automation |
trigger, turn_on, turn_off, reload |
Automation control |
script |
*, turn_on, turn_off, reload |
Script execution |
scene |
apply, turn_on, reload |
Scene activation |
input_* |
set_value, increment, decrement |
Helper control |
Indexes: (domain, service) (unique)
Status: Not Yet Implemented
Represents HA event types for agent awareness.
| Field | Type | Description | Constraints |
|---|---|---|---|
| id | UUID | Primary key | Generated |
| event_type | String | Event type | e.g., state_changed, call_service |
| description | String | Event description | What triggers it |
| data_schema | JSONB | Event data schema | Expected fields |
| is_internal | Boolean | Internal event | Core vs integration |
| created_at | Timestamp | Record creation | Immutable |
Key Event Types:
| Event Type | Description | Data Fields |
|---|---|---|
state_changed |
Entity state change | entity_id, old_state, new_state |
call_service |
Service called | domain, service, service_data |
automation_triggered |
Automation fired | name, entity_id, source |
script_started |
Script began | name, entity_id |
homeassistant_start |
HA startup | — |
homeassistant_stop |
HA shutdown | — |
component_loaded |
Integration loaded | component |
service_registered |
New service | domain, service |
device_registry_updated |
Device change | action, device_id |
entity_registry_updated |
Entity change | action, entity_id |
area_registry_updated |
Area change | action, area_id |
floor_registry_updated |
Floor change | action, floor_id |
label_registry_updated |
Label change | action, label_id |
These entities are specific to Project Aether's agent orchestration layer.
Tracks entity discovery runs by the Librarian agent.
| Field | Type | Description | Constraints |
|---|---|---|---|
| id | UUID | Primary key | Generated |
| started_at | Timestamp | Session start | Immutable |
| completed_at | Timestamp | Session end | Nullable until complete |
| entities_found | Integer | Count of entities discovered | >= 0 |
| entities_added | Integer | New entities | >= 0 |
| entities_removed | Integer | Removed entities | >= 0 |
| entities_updated | Integer | Changed entities | >= 0 |
| devices_found | Integer | Count of devices discovered | >= 0 |
| devices_added | Integer | New devices | >= 0 |
| areas_found | Integer | Count of areas discovered | >= 0 |
| floors_found | Integer | Count of floors discovered | >= 0 |
| labels_found | Integer | Count of labels discovered | >= 0 |
| integrations_found | Integer | Count of integrations | >= 0 |
| services_found | Integer | Count of services discovered | >= 0 |
| status | Enum | Session status | running, completed, failed |
| error_message | String | Error details if failed | Nullable |
| mlflow_run_id | String | MLflow tracking ID | For observability |
Represents an agent in the system (for tracing purposes).
| Field | Type | Description | Constraints |
|---|---|---|---|
| id | UUID | Primary key | Generated |
| name | String | Agent identifier | librarian, categorizer, architect, developer, data_scientist |
| description | String | Agent purpose | Human-readable |
| version | String | Agent version | SemVer |
| prompt_template | Text | System prompt | Versioned in MLflow |
| created_at | Timestamp | Record creation | Immutable |
A dialogue session between user and an agent (primarily Architect).
| Field | Type | Description | Constraints |
|---|---|---|---|
| id | UUID | Primary key | Generated |
| agent_id | UUID | FK to Agent | Required |
| user_id | String | User identifier | Default: default_user |
| title | String | Conversation summary | Auto-generated |
| status | Enum | Conversation state | active, completed, archived |
| context | JSONB | Conversation context | Entities involved, preferences |
| created_at | Timestamp | Started | Immutable |
| updated_at | Timestamp | Last activity | Auto-updated |
Individual messages within a conversation.
| Field | Type | Description | Constraints |
|---|---|---|---|
| id | UUID | Primary key | Generated |
| conversation_id | UUID | FK to Conversation | Required |
| role | Enum | Message author | user, assistant, system |
| content | Text | Message content | Required |
| tool_calls | JSONB | Function calls made | Nullable |
| tool_results | JSONB | Function call results | Nullable |
| tokens_used | Integer | Token count | For cost tracking |
| latency_ms | Integer | Response time | For performance tracking |
| mlflow_span_id | String | Trace span ID | For observability |
| created_at | Timestamp | Message time | Immutable |
Represents an existing Home Assistant automation (synced from HA).
| Field | Type | Description | Constraints |
|---|---|---|---|
| id | UUID | Primary key | Generated |
| ha_automation_id | String | Home Assistant automation ID | Unique |
| entity_id | UUID | FK to Entity | automation.* entity |
| alias | String | Automation name | Human-readable |
| description | String | Automation description | Nullable |
| mode | String | Execution mode | single, restart, queued, parallel |
| max | Integer | Max parallel runs | For queued/parallel modes |
| max_exceeded | String | Overflow action | silent, warning, error |
| trigger | JSONB | HA trigger config | Trigger definitions |
| condition | JSONB | HA conditions | Condition definitions |
| action | JSONB | HA actions | Action sequence |
| variables | JSONB | Automation variables | Template variables |
| trace_enabled | Boolean | Enable tracing | Default: true |
| stored_traces | Integer | Traces to keep | Default: 5 |
| is_enabled | Boolean | Automation enabled | From entity state |
| last_triggered | Timestamp | Last execution | From HA |
| category_id | UUID | FK to Category | Optional |
| created_at | Timestamp | Record creation | Immutable |
| updated_at | Timestamp | Last sync | Auto-updated |
Trigger Types:
| Type | Description | Key Fields |
|---|---|---|
state |
State change | entity_id, from, to, for |
numeric_state |
Numeric threshold | entity_id, above, below |
time |
Time of day | at |
time_pattern |
Recurring time | hours, minutes, seconds |
sun |
Sunrise/sunset | event, offset |
zone |
Zone enter/exit | entity_id, zone, event |
device |
Device trigger | device_id, device-specific |
mqtt |
MQTT message | topic, payload |
webhook |
HTTP webhook | webhook_id |
event |
HA event | event_type, event_data |
homeassistant |
HA lifecycle | event (start/shutdown) |
tag |
NFC/RFID tag | tag_id, device_id |
calendar |
Calendar event | entity_id, event |
template |
Template true | value_template |
persistent_notification |
Notification | notification_id |
conversation |
Voice command | command |
A proposed automation rule (from agents, requiring HITL approval).
| Field | Type | Description | Constraints |
|---|---|---|---|
| id | UUID | Primary key | Generated |
| conversation_id | UUID | FK to Conversation | Source conversation |
| name | String | Automation name | Human-readable |
| description | Text | What it does | User-facing |
| trigger | JSONB | HA trigger config | Time, state, event, etc. |
| conditions | JSONB | HA conditions | Optional |
| actions | JSONB | HA actions | Required |
| mode | String | Execution mode | Default: single |
| status | Enum | Proposal state | See state machine below |
| ha_automation_id | String | HA automation ID | Set after deployment |
| proposed_at | Timestamp | When proposed | Immutable |
| approved_at | Timestamp | When approved | Nullable |
| approved_by | String | Approver | Nullable |
| deployed_at | Timestamp | When deployed to HA | Nullable |
| rolled_back_at | Timestamp | When rolled back | Nullable |
| rejection_reason | String | Why rejected | Nullable |
| mlflow_run_id | String | Tracking ID | For observability |
State Machine:
┌──────────┐
│ draft │
└────┬─────┘
│ propose()
▼
┌──────────┐
┌───────>│ proposed │<──────┐
│ └────┬─────┘ │
│ │ │
│ approve() │ reject() │
│ ▼ │
│ ┌──────────┐ │
│ │ approved │ │
│ └────┬─────┘ │
│ │ │
│ deploy() │ │
│ ▼ │
│ ┌──────────┐ │
│ │ deployed │───────┤
│ └────┬─────┘ │
│ │ │
│ rollback() │ │
│ ▼ │
│ ┌──────────┐ │
└────────│rolled_back│──────┘
└──────────┘
│
expire()/archive()
▼
┌──────────┐
│ archived │
└──────────┘
Data-driven observation from the Data Scientist agent.
| Field | Type | Description | Constraints |
|---|---|---|---|
| id | UUID | Primary key | Generated |
| type | Enum | Insight category | energy_optimization, anomaly_detection, usage_pattern, cost_saving, maintenance_prediction |
| title | String | Brief summary | Human-readable |
| description | Text | Detailed explanation | Markdown supported |
| evidence | JSONB | Supporting data | Charts, statistics, queries |
| confidence | Float | Confidence score | 0.0 - 1.0 |
| impact | String | Potential impact | low, medium, high, critical |
| entities | UUID[] | Related entities | FK to Entity |
| script_path | String | Generated analysis script | Path in MLflow artifacts |
| script_output | JSONB | Script execution results | Stdout, figures, metrics |
| status | Enum | Insight state | pending, reviewed, actioned, dismissed |
| mlflow_run_id | String | Tracking ID | For observability |
| created_at | Timestamp | Generated | Immutable |
| reviewed_at | Timestamp | When reviewed | Nullable |
| actioned_at | Timestamp | When actioned | Nullable |
Status: Not Yet Implemented — Dashboard generation exists in agents but no persistent Dashboard table.
Generated Home Assistant dashboard configuration.
| Field | Type | Description | Constraints |
|---|---|---|---|
| id | UUID | Primary key | Generated |
| conversation_id | UUID | FK to Conversation | Source conversation |
| name | String | Dashboard name | Human-readable |
| description | Text | Purpose | User-facing |
| theme | String | Visual theme | auto, light, dark |
| layout | JSONB | HA dashboard YAML | Lovelace configuration |
| entities | UUID[] | Included entities | FK to Entity |
| status | Enum | Dashboard state | draft, approved, deployed, archived |
| ha_dashboard_id | String | HA dashboard ID | Set after deployment |
| deployed_at | Timestamp | When deployed | Nullable |
| created_at | Timestamp | Generated | Immutable |
| updated_at | Timestamp | Last modified | Auto-updated |
LangGraph manages this table for workflow state persistence.
| Field | Type | Description | Constraints |
|---|---|---|---|
| thread_id | String | Workflow thread | Primary key part |
| checkpoint_id | String | Checkpoint version | Primary key part |
| parent_id | String | Parent checkpoint | For branching |
| checkpoint | JSONB | Serialized state | LangGraph format |
| metadata | JSONB | Additional metadata | Custom fields |
| created_at | Timestamp | Checkpoint time | Immutable |
ha_floor_idmust be uniquelevelshould be unique per floor (no duplicate levels)
ha_area_idmust be uniquefloor_idmust reference existing Floor if provided
ha_label_idmust be uniquecolormust be valid hex color format
ha_device_idmust be uniqueconfig_entry_idmust reference existing ConfigEntryvia_device_idmust reference existing Device if provided (no circular refs)area_idmust reference existing Area if provided
ha_entity_idmust match pattern:{domain}.{object_id}domainmust be valid HA domaindevice_idmust reference existing Device if providedarea_idmust reference existing Area if providedentity_categorymust beconfig,diagnostic, or null
ha_entry_idmust be uniquestatemust beloaded,setup_error,setup_retry,not_loaded, orfailed_unload
entity_idmust reference existing Entityhelper_typemust be valid helper typeconfigmust match schema forhelper_type
ha_scene_idmust be uniqueentity_idmust reference Entity with domainsceneentity_statesmust contain valid entity_id keys
ha_script_idmust be uniqueentity_idmust reference Entity with domainscriptmodemust besingle,restart,queued, orparallelmaxrequired if mode isqueuedorparallel
(domain, service)combination must be uniquefieldsmust be valid JSON schema
ha_automation_idmust be uniqueentity_idmust reference Entity with domainautomationmodemust besingle,restart,queued, orparallel
- Cannot transition from
archivedto any other state approved_byrequired when status isapprovedha_automation_idrequired when status isdeployed- HITL: status cannot change from
proposedtodeployedwithout going throughapproved
confidencemust be between 0.0 and 1.0script_pathrequired forenergy_optimizationtypeevidencemust contain at least one data point
layoutmust be valid HA Lovelace YAML structureentitiesmust reference existing Entity records
-- ===========================================
-- HA REGISTRY INDEXES
-- ===========================================
-- Floor lookups
CREATE UNIQUE INDEX idx_floor_ha_id ON floors(ha_floor_id);
CREATE UNIQUE INDEX idx_floor_level ON floors(level);
-- Area lookups
CREATE UNIQUE INDEX idx_area_ha_id ON areas(ha_area_id);
CREATE INDEX idx_area_floor ON areas(floor_id);
CREATE INDEX idx_area_name ON areas(name);
-- Label lookups
CREATE UNIQUE INDEX idx_label_ha_id ON labels(ha_label_id);
-- Category lookups
CREATE UNIQUE INDEX idx_category_ha_id ON categories(ha_category_id);
CREATE INDEX idx_category_domain ON categories(domain);
-- Device lookups
CREATE UNIQUE INDEX idx_device_ha_id ON devices(ha_device_id);
CREATE INDEX idx_device_manufacturer ON devices(manufacturer);
CREATE INDEX idx_device_model ON devices(model);
CREATE INDEX idx_device_area ON devices(area_id);
CREATE INDEX idx_device_config_entry ON devices(config_entry_id);
CREATE INDEX idx_device_via ON devices(via_device_id);
-- Entity lookups
CREATE UNIQUE INDEX idx_entity_ha_id ON entities(ha_entity_id);
CREATE INDEX idx_entity_domain ON entities(domain);
CREATE INDEX idx_entity_device ON entities(device_id);
CREATE INDEX idx_entity_area ON entities(area_id);
CREATE INDEX idx_entity_platform ON entities(platform);
CREATE INDEX idx_entity_device_class ON entities(device_class);
CREATE INDEX idx_entity_friendly_name ON entities USING gin(to_tsvector('english', friendly_name));
-- ConfigEntry lookups
CREATE UNIQUE INDEX idx_config_entry_ha_id ON config_entries(ha_entry_id);
CREATE INDEX idx_config_entry_domain ON config_entries(domain);
CREATE INDEX idx_config_entry_state ON config_entries(state);
-- Integration lookups
CREATE UNIQUE INDEX idx_integration_domain ON integrations(domain);
-- Helper lookups
CREATE INDEX idx_helper_type ON helpers(helper_type);
CREATE INDEX idx_helper_entity ON helpers(entity_id);
-- Scene lookups
CREATE UNIQUE INDEX idx_scene_ha_id ON scenes(ha_scene_id);
CREATE INDEX idx_scene_entity ON scenes(entity_id);
-- Script lookups
CREATE UNIQUE INDEX idx_script_ha_id ON scripts(ha_script_id);
CREATE INDEX idx_script_entity ON scripts(entity_id);
-- Service lookups
CREATE UNIQUE INDEX idx_service_domain_service ON services(domain, service);
-- HA Automation lookups
CREATE UNIQUE INDEX idx_ha_automation_ha_id ON ha_automations(ha_automation_id);
CREATE INDEX idx_ha_automation_entity ON ha_automations(entity_id);
-- Event lookups
CREATE UNIQUE INDEX idx_event_type ON events(event_type);
-- Label relationships (many-to-many)
CREATE INDEX idx_entity_labels ON entity_labels(entity_id);
CREATE INDEX idx_device_labels ON device_labels(device_id);
-- ===========================================
-- AETHER AGENT INDEXES
-- ===========================================
-- Conversation queries
CREATE INDEX idx_conversation_user ON conversations(user_id);
CREATE INDEX idx_conversation_status ON conversations(status);
CREATE INDEX idx_message_conversation ON messages(conversation_id);
-- Automation proposal workflow
CREATE INDEX idx_proposal_status ON automation_proposals(status);
CREATE INDEX idx_proposal_conversation ON automation_proposals(conversation_id);
-- Insight discovery
CREATE INDEX idx_insight_type ON insights(type);
CREATE INDEX idx_insight_status ON insights(status);
-- Discovery sessions
CREATE INDEX idx_discovery_started ON discovery_sessions(started_at);
-- ===========================================
-- TEMPORAL INDEXES
-- ===========================================
CREATE INDEX idx_entity_last_changed ON entities(last_changed);
CREATE INDEX idx_entity_last_updated ON entities(last_updated);
CREATE INDEX idx_device_updated ON devices(updated_at);
CREATE INDEX idx_ha_automation_triggered ON ha_automations(last_triggered);
CREATE INDEX idx_script_triggered ON scripts(last_triggered);- Create
floorstable - Create
areastable with FK to floors - Create
labelstable - Create
categoriestable - Create
config_entriestable - Create
integrationstable
- Create
devicestable with FKs to areas, config_entries - Create
entitiestable with FKs to devices, areas - Create
entity_labelsjunction table - Create
device_labelsjunction table
- Create
helperstable with FK to entities - Create
scenestable with FK to entities - Create
scriptstable with FK to entities - Create
ha_automationstable with FK to entities - Create
servicestable - Create
eventstable
- Create
agentstable with seed data - Create
conversationstable - Create
messagestable - Create
automation_proposalstable - Create
insightstable - Create
dashboardstable - Create
discovery_sessionstable - Let LangGraph create its
checkpointstable
- Run Librarian discovery to sync all HA registries
- Populate floors, areas, labels, devices, entities
- Sync scenes, scripts, automations
- Index all services and events
The following table maps HA MCP tools to entities they populate:
| MCP Tool | Target Entity | Notes |
|---|---|---|
system_overview |
All registries | Initial discovery |
list_entities |
Entity | Core entity sync |
get_entity |
Entity | Single entity detail |
domain_summary_tool |
Entity | Domain statistics |
search_entities_tool |
Entity | Search-based discovery |
list_automations |
HAAutomation | Automation sync |
get_history |
(temporal analysis) | For insights |
call_service_tool |
Service | Service registry |
To fully populate the data model, the MCP server needs these additional tools:
| Needed Tool | Purpose | HA WebSocket API |
|---|---|---|
list_devices |
Device registry | config/device_registry/list |
list_areas |
Area registry | config/area_registry/list |
list_floors |
Floor registry | config/floor_registry/list |
list_labels |
Label registry | config/label_registry/list |
list_categories |
Category registry | config/category_registry/list |
list_config_entries |
Config entries | config_entries/get |
list_integrations |
Integration manifest | manifest/list |
list_services |
Service registry | get_services |
list_scripts |
Script registry | script/list |
list_scenes |
Scene registry | scene/list |
list_helpers |
Helper entities | Filter by input_*, counter, timer, etc. |
subscribe_events |
Event stream | subscribe_events |
For extending the MCP server, here are the key HA WebSocket commands:
// Device Registry
{ "type": "config/device_registry/list" }
{ "type": "config/device_registry/update", "device_id": "...", "area_id": "...", "labels": [...] }
// Entity Registry
{ "type": "config/entity_registry/list" }
{ "type": "config/entity_registry/get", "entity_id": "light.living_room" }
{ "type": "config/entity_registry/update", "entity_id": "...", "area_id": "...", "labels": [...] }
// Area Registry
{ "type": "config/area_registry/list" }
{ "type": "config/area_registry/create", "name": "...", "floor_id": "..." }
{ "type": "config/area_registry/update", "area_id": "...", "name": "...", "labels": [...] }
// Floor Registry
{ "type": "config/floor_registry/list" }
{ "type": "config/floor_registry/create", "name": "...", "level": 0 }
// Label Registry
{ "type": "config/label_registry/list" }
{ "type": "config/label_registry/create", "name": "...", "color": "#FF5722" }
// Category Registry
{ "type": "config/category_registry/list", "scope": "automation" }
// Config Entries
{ "type": "config_entries/get" }
{ "type": "config_entries/flow", "handler": "hue" }
// Services
{ "type": "get_services" }
// Automations, Scripts, Scenes
{ "type": "automation/config", "entity_id": "automation.example" }
{ "type": "script/config", "entity_id": "script.example" }
{ "type": "scene/config", "entity_id": "scene.example" }
// Events
{ "type": "subscribe_events", "event_type": "state_changed" }
{ "type": "fire_event", "event_type": "custom_event", "event_data": {...} }
// State
{ "type": "get_states" }
{ "type": "call_service", "domain": "light", "service": "turn_on", "service_data": {...} }