from pydantic import BaseModel, Field from typing import Optional, List, Dict, Any, Literal from datetime import datetime from enum import Enum class SensorType(str, Enum): ENERGY = "energy" CO2 = "co2" TEMPERATURE = "temperature" HUMIDITY = "humidity" HVAC = "hvac" LIGHTING = "lighting" SECURITY = "security" class SensorStatus(str, Enum): ONLINE = "online" OFFLINE = "offline" ERROR = "error" MAINTENANCE = "maintenance" class CO2Status(str, Enum): GOOD = "good" MODERATE = "moderate" POOR = "poor" CRITICAL = "critical" class OccupancyLevel(str, Enum): LOW = "low" MEDIUM = "medium" HIGH = "high" # Base Models class SensorReading(BaseModel): """Individual sensor reading model""" sensor_id: str = Field(..., description="Unique sensor identifier") room: Optional[str] = Field(None, description="Room where sensor is located") sensor_type: SensorType = Field(..., description="Type of sensor") timestamp: int = Field(..., description="Unix timestamp of reading") created_at: datetime = Field(default_factory=datetime.utcnow, description="Record creation timestamp") # Sensor values energy: Optional[Dict[str, Any]] = Field(None, description="Energy reading with value and unit") co2: Optional[Dict[str, Any]] = Field(None, description="CO2 reading with value and unit") temperature: Optional[Dict[str, Any]] = Field(None, description="Temperature reading with value and unit") humidity: Optional[Dict[str, Any]] = Field(None, description="Humidity reading with value and unit") motion: Optional[Dict[str, Any]] = Field(None, description="Motion detection reading") # Metadata metadata: Optional[Dict[str, Any]] = Field(default_factory=dict, description="Additional sensor metadata") class Config: json_encoders = { datetime: lambda v: v.isoformat() } class LegacySensorReading(BaseModel): """Legacy sensor reading format for backward compatibility""" sensor_id: str = Field(..., alias="sensorId") timestamp: int value: float unit: str created_at: datetime = Field(default_factory=datetime.utcnow) class Config: allow_population_by_field_name = True class SensorMetadata(BaseModel): """Sensor configuration and metadata""" sensor_id: str = Field(..., description="Unique sensor identifier") name: str = Field(..., description="Human-readable sensor name") sensor_type: SensorType = Field(..., description="Type of sensor") room: Optional[str] = Field(None, description="Room assignment") status: SensorStatus = Field(default=SensorStatus.OFFLINE, description="Current sensor status") # Physical location and installation details location: Optional[str] = Field(None, description="Physical location description") floor: Optional[str] = Field(None, description="Floor level") building: Optional[str] = Field(None, description="Building identifier") # Technical specifications model: Optional[str] = Field(None, description="Sensor model") manufacturer: Optional[str] = Field(None, description="Sensor manufacturer") firmware_version: Optional[str] = Field(None, description="Firmware version") hardware_version: Optional[str] = Field(None, description="Hardware version") # Network and connectivity ip_address: Optional[str] = Field(None, description="IP address if network connected") mac_address: Optional[str] = Field(None, description="MAC address") connection_type: Optional[str] = Field(None, description="Connection type (wifi, ethernet, zigbee, etc.)") # Power and maintenance battery_level: Optional[float] = Field(None, description="Battery level percentage") last_maintenance: Optional[datetime] = Field(None, description="Last maintenance date") next_maintenance: Optional[datetime] = Field(None, description="Next scheduled maintenance") # Operational settings sampling_rate: Optional[int] = Field(None, description="Data sampling rate in seconds") calibration_date: Optional[datetime] = Field(None, description="Last calibration date") # Capabilities monitoring_capabilities: List[str] = Field(default_factory=list, description="List of monitoring capabilities") control_capabilities: List[str] = Field(default_factory=list, description="List of control capabilities") # Timestamps installed_at: Optional[datetime] = Field(None, description="Installation timestamp") last_seen: Optional[datetime] = Field(None, description="Last communication timestamp") created_at: datetime = Field(default_factory=datetime.utcnow, description="Record creation timestamp") updated_at: datetime = Field(default_factory=datetime.utcnow, description="Record update timestamp") class Config: json_encoders = { datetime: lambda v: v.isoformat() if v else None } class RoomMetrics(BaseModel): """Aggregated room-level metrics""" room: str = Field(..., description="Room identifier") timestamp: int = Field(..., description="Metrics calculation timestamp") created_at: datetime = Field(default_factory=datetime.utcnow, description="Record creation timestamp") # Sensor inventory sensor_count: int = Field(0, description="Total number of sensors in room") active_sensors: List[str] = Field(default_factory=list, description="List of active sensor IDs") sensor_types: List[SensorType] = Field(default_factory=list, description="Types of sensors present") # Energy metrics energy: Optional[Dict[str, Any]] = Field(None, description="Energy consumption metrics") # Format: {"current": float, "total": float, "average": float, "peak": float, "unit": str} # Environmental metrics co2: Optional[Dict[str, Any]] = Field(None, description="CO2 level metrics") # Format: {"current": float, "average": float, "max": float, "min": float, "status": CO2Status, "unit": str} temperature: Optional[Dict[str, Any]] = Field(None, description="Temperature metrics") # Format: {"current": float, "average": float, "max": float, "min": float, "unit": str} humidity: Optional[Dict[str, Any]] = Field(None, description="Humidity metrics") # Format: {"current": float, "average": float, "max": float, "min": float, "unit": str} # Occupancy and usage occupancy_estimate: OccupancyLevel = Field(default=OccupancyLevel.LOW, description="Estimated occupancy level") motion_detected: bool = Field(default=False, description="Recent motion detection status") # Time-based metrics last_activity: Optional[datetime] = Field(None, description="Last detected activity timestamp") daily_usage_hours: Optional[float] = Field(None, description="Estimated daily usage in hours") class Config: json_encoders = { datetime: lambda v: v.isoformat() if v else None } class SystemEvent(BaseModel): """System events and alerts""" event_id: str = Field(..., description="Unique event identifier") event_type: str = Field(..., description="Type of event") severity: Literal["info", "warning", "error", "critical"] = Field(..., description="Event severity") timestamp: int = Field(..., description="Event timestamp") created_at: datetime = Field(default_factory=datetime.utcnow, description="Record creation timestamp") # Event details title: str = Field(..., description="Event title") description: str = Field(..., description="Event description") source: Optional[str] = Field(None, description="Event source (sensor_id, system component, etc.)") # Context sensor_id: Optional[str] = Field(None, description="Related sensor ID") room: Optional[str] = Field(None, description="Related room") # Event data data: Optional[Dict[str, Any]] = Field(default_factory=dict, description="Additional event data") # Status tracking acknowledged: bool = Field(default=False, description="Whether event has been acknowledged") resolved: bool = Field(default=False, description="Whether event has been resolved") acknowledged_by: Optional[str] = Field(None, description="Who acknowledged the event") resolved_by: Optional[str] = Field(None, description="Who resolved the event") acknowledged_at: Optional[datetime] = Field(None, description="Acknowledgment timestamp") resolved_at: Optional[datetime] = Field(None, description="Resolution timestamp") class Config: json_encoders = { datetime: lambda v: v.isoformat() if v else None } class DataQuery(BaseModel): """Data query parameters for historical data retrieval""" sensor_ids: Optional[List[str]] = Field(None, description="Filter by sensor IDs") rooms: Optional[List[str]] = Field(None, description="Filter by rooms") sensor_types: Optional[List[SensorType]] = Field(None, description="Filter by sensor types") # Time range start_time: Optional[int] = Field(None, description="Start timestamp (Unix)") end_time: Optional[int] = Field(None, description="End timestamp (Unix)") # Aggregation aggregate: Optional[str] = Field(None, description="Aggregation method (avg, sum, min, max)") interval: Optional[str] = Field(None, description="Aggregation interval (1m, 5m, 1h, 1d)") # Pagination limit: int = Field(default=100, description="Maximum number of records to return") offset: int = Field(default=0, description="Number of records to skip") # Sorting sort_by: str = Field(default="timestamp", description="Field to sort by") sort_order: Literal["asc", "desc"] = Field(default="desc", description="Sort order") class DataResponse(BaseModel): """Response model for data queries""" data: List[Dict[str, Any]] = Field(default_factory=list, description="Query results") total_count: int = Field(0, description="Total number of matching records") query: DataQuery = Field(..., description="Original query parameters") execution_time_ms: float = Field(..., description="Query execution time in milliseconds") class HealthCheck(BaseModel): """Health check response model""" status: str = Field(..., description="Overall system status") timestamp: datetime = Field(default_factory=datetime.utcnow) # Database status mongodb_connected: bool = Field(..., description="MongoDB connection status") redis_connected: bool = Field(..., description="Redis connection status") # Data statistics total_sensors: int = Field(0, description="Total number of registered sensors") active_sensors: int = Field(0, description="Number of active sensors") total_readings: int = Field(0, description="Total sensor readings in database") # System metrics uptime_seconds: float = Field(..., description="System uptime in seconds") memory_usage_mb: Optional[float] = Field(None, description="Memory usage in MB") class Config: json_encoders = { datetime: lambda v: v.isoformat() }