"""Maintenance object model.""" from __future__ import annotations from dataclasses import dataclass, field from typing import Any from uuid import uuid4 from ..const import slugify_object_name @dataclass class MaintenanceObject: """Represents a physical object that requires maintenance. Examples: pool pump, car, HVAC system, water softener. """ id: str = field(default_factory=lambda: uuid4().hex) name: str = "" area_id: str | None = None manufacturer: str | None = None model: str | None = None serial_number: str | None = None installation_date: str | None = None # ISO format YYYY-MM-DD warranty_expiry: str | None = None # ISO YYYY-MM-DD: warranty expiry date (#67) documentation_url: str | None = None # v1.4.0: link to PDF manual / vendor page (#43) notes: str | None = None # v1.4.10: free-form notes — part numbers, procedures (#46) # 2.19: attach to an EXISTING HA device — the object's task entities land # on that device's page instead of creating an own virtual device. ha_device_id: str | None = None # 2.19: another maintenance object's entry_id as parent — the object's # device nests under it (HA via_device hierarchy). parent_entry_id: str | None = None # 2.20 (journey N3): seasonal pause. paused_at set = paused (ISO timestamp # marker); paused_until (ISO date) optionally auto-resumes on that day. paused_at: str | None = None paused_until: str | None = None # 2.20 (journey N1): replace flow lineage. On the retired predecessor, # replaced_by_entry_id points at the successor; on the successor, # predecessor_entry_id points back — history stays findable in both # directions. predecessor_entry_id: str | None = None replaced_by_entry_id: str | None = None task_ids: list[str] = field(default_factory=list) def to_dict(self) -> dict[str, Any]: """Serialize to dictionary for config entry storage.""" return { "id": self.id, "name": self.name, "area_id": self.area_id, "manufacturer": self.manufacturer, "model": self.model, "serial_number": self.serial_number, "installation_date": self.installation_date, "warranty_expiry": self.warranty_expiry, "documentation_url": self.documentation_url, "notes": self.notes, "ha_device_id": self.ha_device_id, "parent_entry_id": self.parent_entry_id, "paused_at": self.paused_at, "paused_until": self.paused_until, "predecessor_entry_id": self.predecessor_entry_id, "replaced_by_entry_id": self.replaced_by_entry_id, "task_ids": self.task_ids, } @classmethod def from_dict(cls, data: dict[str, Any]) -> MaintenanceObject: """Deserialize from dictionary.""" return cls( id=data.get("id", uuid4().hex), name=data.get("name", ""), area_id=data.get("area_id"), manufacturer=data.get("manufacturer"), model=data.get("model"), serial_number=data.get("serial_number"), installation_date=data.get("installation_date"), warranty_expiry=data.get("warranty_expiry"), documentation_url=data.get("documentation_url"), notes=data.get("notes"), ha_device_id=data.get("ha_device_id"), parent_entry_id=data.get("parent_entry_id"), paused_at=data.get("paused_at"), paused_until=data.get("paused_until"), predecessor_entry_id=data.get("predecessor_entry_id"), replaced_by_entry_id=data.get("replaced_by_entry_id"), task_ids=data.get("task_ids", []), ) @property def slug(self) -> str: """Return a slugified version of the name for use in unique IDs.""" return slugify_object_name(self.name)