from __future__ import annotations import json import re import time from datetime import datetime, timezone from pathlib import Path from typing import Any from .models import EventRecord class EventEmitter: """Writes atomic JSON event files to the events directory.""" def __init__( self, events_dir: Path, node_name: str, location_tag: str = "" ) -> None: self._events_dir = events_dir self._node_name = node_name self._location_tag = location_tag self._seq = 0 events_dir.mkdir(parents=True, exist_ok=True) def _make_id(self, event_type: str, service: str) -> str: # Sequence suffix guarantees uniqueness even when multiple events of the # same type are emitted within the same millisecond. self._seq += 1 ts = int(time.time()) svc_slug = re.sub(r"[^a-z0-9]", "-", (service or "ha").lower())[:32].strip("-") return f"evt-{self._node_name}-{ts}-{event_type}-{svc_slug}-{self._seq}" def emit( self, event_type: str, severity: str, service: str, message: str, payload: dict[str, Any] | None = None, ) -> str: event_id = self._make_id(event_type, service) merged: dict[str, Any] = {} if self._location_tag: merged["location_tag"] = self._location_tag merged.update(payload or {}) record = EventRecord( id=event_id, timestamp=int(time.time()), date=datetime.now(timezone.utc).isoformat(), type=event_type, severity=severity, node=self._node_name, service=service, message=message, payload=merged, ) path = self._events_dir / f"{event_id}.json" tmp = path.with_suffix(".tmp") tmp.write_text(json.dumps(record.model_dump(), indent=2)) tmp.rename(path) return event_id