96 lines
3.9 KiB
Python
96 lines
3.9 KiB
Python
"""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)
|