Files
Home-Assistant/custom_components/maintenance_supporter/models/maintenance_object.py
T
2026-07-08 10:43:39 -04:00

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)