Files
sillyhome-future/app/core/models.py
2026-06-18 13:24:17 +02:00

136 lines
4.3 KiB
Python

from __future__ import annotations
from datetime import datetime, timezone
from enum import StrEnum
from pydantic import BaseModel, Field
class HandoffMode(StrEnum):
SHADOW = "shadow"
CANDIDATE = "candidate"
CONTROLLED = "controlled"
CONFLICT = "conflict"
ROLLBACK = "rollback"
class SafetyStage(StrEnum):
OBSERVE = "observe"
DRY_RUN = "dry_run"
ACTIVE = "active"
BLOCKED = "blocked"
class EntityState(BaseModel):
entity_id: str = Field(pattern=r"^[a-z0-9_]+\.[a-z0-9_]+$")
domain: str
state: str | None = None
changed_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
area_name: str | None = None
device_id: str | None = None
friendly_name: str | None = None
class StateEvent(BaseModel):
entity_id: str = Field(pattern=r"^[a-z0-9_]+\.[a-z0-9_]+$")
new_state: str | None = None
changed_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
attributes: dict[str, str] = Field(default_factory=dict)
class BehaviorPatternV2(BaseModel):
actuator_entity_id: str
target_state: str
trigger_entity_id: str | None = None
trigger_state: str | None = None
context: dict[str, str] = Field(default_factory=dict)
support: int = Field(default=1, ge=1)
confidence: float = Field(default=0.0, ge=0.0, le=1.0)
source: str = Field(default="observed", max_length=40)
updated_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
class LearningProfile(BaseModel):
actuator_entity_id: str
patterns: list[BehaviorPatternV2] = Field(default_factory=list)
feedback_positive: int = Field(default=0, ge=0)
feedback_negative: int = Field(default=0, ge=0)
model_version: str = "empty"
class ControlProfile(BaseModel):
actuator_entity_id: str
stage: SafetyStage = SafetyStage.OBSERVE
min_confidence: float = Field(default=0.82, ge=0.0, le=1.0)
manual_block: bool = False
cooldown_seconds: int = Field(default=900, ge=0)
handoff_mode: HandoffMode = HandoffMode.SHADOW
related_automation_ids: list[str] = Field(default_factory=list)
paused_automation_ids: list[str] = Field(default_factory=list)
dry_run_events: int = Field(default=0, ge=0)
dry_run_successes: int = Field(default=0, ge=0)
dry_run_failures: int = Field(default=0, ge=0)
active_ready: bool = False
active_readiness_reason: str = "Noch nicht bewertet."
class RoomProfile(BaseModel):
room_id: str = Field(pattern=r"^[a-z0-9_-]{1,64}$")
name: str
actuator_entity_ids: list[str] = Field(default_factory=list)
context_entity_ids: list[str] = Field(default_factory=list)
class SceneProfile(BaseModel):
scene_id: str = Field(pattern=r"^[a-z0-9_-]{1,64}$")
name: str
actuator_entity_ids: list[str] = Field(default_factory=list)
trigger_entity_id: str | None = None
confidence: float = Field(default=0.0, ge=0.0, le=1.0)
class Decision(BaseModel):
actuator_entity_id: str
target_state: str | None = None
confidence: float = Field(default=0.0, ge=0.0, le=1.0)
allowed: bool = False
executed: bool = False
dry_run: bool = False
reason: str
blockers: list[str] = Field(default_factory=list)
trigger_entity_id: str | None = None
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
class AuditEvent(BaseModel):
event_id: str
kind: str = Field(max_length=40)
entity_id: str | None = None
message: str = Field(max_length=700)
decision: Decision | None = None
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
class RuntimeState(BaseModel):
entities: dict[str, EntityState] = Field(default_factory=dict)
audit: list[AuditEvent] = Field(default_factory=list)
websocket_status: str = "unavailable"
class LearningState(BaseModel):
profiles: dict[str, LearningProfile] = Field(default_factory=dict)
rooms: dict[str, RoomProfile] = Field(default_factory=dict)
scenes: dict[str, SceneProfile] = Field(default_factory=dict)
class ControlState(BaseModel):
profiles: dict[str, ControlProfile] = Field(default_factory=dict)
global_enabled: bool = True
class BackupBundle(BaseModel):
exported_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
runtime: RuntimeState
learning: LearningState
control: ControlState