""" Pydantic models for Demand Response Service """ from datetime import datetime from typing import List, Dict, Optional, Literal from pydantic import BaseModel, Field from enum import Enum # Enums class InvitationStatus(str, Enum): """Invitation status states""" PENDING = "pending" SCHEDULED = "scheduled" ACTIVE = "active" COMPLETED = "completed" CANCELLED = "cancelled" class ResponseType(str, Enum): """Device response types""" WAITING = "WAITING" YES = "YES" NO = "NO" class EventStatus(str, Enum): """DR event status states""" SCHEDULED = "scheduled" ACTIVE = "active" COMPLETED = "completed" CANCELLED = "cancelled" class InstructionType(str, Enum): """Device participation instruction types""" PARTICIPATION = "participation" # Full DR participation (100%) SHIFTING = "shifting" # Partial participation (0-20%) OFF = "off" # No DR participation # Invitation Models class EventRequest(BaseModel): """Request model for creating a DR event (alias for DRInvitationCreate)""" event_time: datetime = Field(..., description="When the DR event should occur") load_kwh: float = Field(..., description="Target load reduction in kWh", gt=0) load_percentage: float = Field(..., description="Target reduction as percentage of total load", ge=0, le=100) iots: List[str] = Field(..., description="List of device IDs to participate", min_items=1) duration_minutes: int = Field(59, description="Event duration in minutes", gt=0, le=120) class Config: json_schema_extra = { "example": { "event_time": "2025-12-10T14:00:00", "load_kwh": 5.0, "load_percentage": 15.0, "iots": ["sensor_1", "sensor_2"], "duration_minutes": 59 } } class DRInvitationCreate(BaseModel): """Request model for creating a DR invitation""" event_time: datetime = Field(..., description="When the DR event should occur") load_kwh: float = Field(..., description="Target load reduction in kWh", gt=0) load_percentage: float = Field(..., description="Target reduction as percentage of total load", ge=0, le=100) iots: List[str] = Field(..., description="List of device IDs to participate", min_items=1) duration_minutes: int = Field(59, description="Event duration in minutes", gt=0, le=120) class Config: json_schema_extra = { "example": { "event_time": "2025-12-10T14:00:00", "load_kwh": 5.0, "load_percentage": 15.0, "iots": ["sensor_1", "sensor_2"], "duration_minutes": 59 } } class DRInvitationResponse(BaseModel): """Response model for device answering invitation""" event_id: str = Field(..., description="Event identifier") iot_id: str = Field(..., description="Device identifier") response: ResponseType = Field(..., description="Device response (YES/NO)") committed_reduction_kw: Optional[float] = Field(None, description="Committed power reduction in kW", ge=0) class Config: json_schema_extra = { "example": { "event_id": "550e8400-e29b-41d4-a716-446655440000", "iot_id": "sensor_1", "response": "YES", "committed_reduction_kw": 2.5 } } class DRInvitation(BaseModel): """Full DR invitation model""" event_id: str = Field(..., description="Unique event identifier") created_at: datetime = Field(..., description="Invitation creation time") event_time: datetime = Field(..., description="Scheduled event start time") load_kwh: float = Field(..., description="Target load reduction in kWh") load_percentage: float = Field(..., description="Target reduction percentage") iots: List[str] = Field(..., description="Participating device IDs") duration_minutes: int = Field(..., description="Event duration in minutes") response: str = Field(..., description="Overall response status") status: str = Field(..., description="Invitation status") class Config: json_encoders = { datetime: lambda v: v.isoformat() if v else None } json_schema_extra = { "example": { "event_id": "550e8400-e29b-41d4-a716-446655440000", "created_at": "2025-12-10T13:45:00", "event_time": "2025-12-10T14:00:00", "load_kwh": 5.0, "load_percentage": 15.0, "iots": ["sensor_1", "sensor_2"], "duration_minutes": 59, "response": "WAITING", "status": "pending" } } # Event Models class EventScheduleRequest(BaseModel): """Request model for scheduling a DR event""" event_time: datetime = Field(..., description="Event start time") iots: List[str] = Field(..., description="Participating device IDs", min_items=1) load_reduction_kw: float = Field(..., description="Target reduction in kW", gt=0) duration_minutes: int = Field(59, description="Event duration in minutes", gt=0, le=120) class Config: json_schema_extra = { "example": { "event_time": "2025-12-10T14:00:00", "iots": ["sensor_1", "sensor_2"], "load_reduction_kw": 5.0, "duration_minutes": 59 } } class PowerSample(BaseModel): """Individual power sample during event""" timestamp: datetime = Field(..., description="Sample timestamp") device_powers: Dict[str, float] = Field(..., description="Device power readings (device_id -> kW)") interval_reduction_kwh: Optional[float] = Field(None, description="Reduction for this interval") class DREvent(BaseModel): """DR event execution model""" event_id: str = Field(..., description="Unique event identifier") invitation_id: Optional[str] = Field(None, description="Source invitation ID if applicable") start_time: datetime = Field(..., description="Event start time") end_time: datetime = Field(..., description="Event end time") status: EventStatus = Field(..., description="Event status") participating_devices: List[str] = Field(..., description="Device IDs participating") target_reduction_kw: float = Field(..., description="Target power reduction in kW") actual_reduction_kw: float = Field(0.0, description="Actual achieved reduction in kWh") power_samples: List[Dict] = Field(default_factory=list, description="Power samples during event") class Config: json_encoders = { datetime: lambda v: v.isoformat() if v else None } class ActiveEventResponse(BaseModel): """Response model for active event with real-time data""" event_id: str = Field(..., description="Event identifier") status: EventStatus = Field(..., description="Current status") start_time: datetime = Field(..., description="Event start time") end_time: datetime = Field(..., description="Event end time") participating_devices: List[str] = Field(..., description="Participating devices") target_reduction_kw: float = Field(..., description="Target reduction") actual_reduction_kw: float = Field(..., description="Current achieved reduction") current_device_powers: Optional[Dict[str, float]] = Field(None, description="Current device power readings") progress_percentage: Optional[float] = Field(None, description="Event progress (0-100%)") class Config: json_encoders = { datetime: lambda v: v.isoformat() if v else None } class LoadReductionRequest(BaseModel): """Request model for executing load reduction""" event_time: datetime = Field(..., description="Event start time") iot: str = Field(..., description="Device ID") class Config: json_schema_extra = { "example": { "event_time": "2025-12-10T14:00:00", "iot": "sensor_1" } } # Flexibility Models class DeviceFlexibility(BaseModel): """Per-device flexibility information""" device_id: str = Field(..., description="Device identifier") available_kw: float = Field(..., description="Available flexibility in kW", ge=0) instruction: str = Field(..., description="Current DR instruction") current_power: float = Field(..., description="Current power consumption in kW", ge=0) class FlexibilityResponse(BaseModel): """Response model for current flexibility""" timestamp: datetime = Field(..., description="Calculation timestamp") total_flexibility_kw: float = Field(..., description="Total available flexibility in kW", ge=0) devices: List[DeviceFlexibility] = Field(..., description="Per-device breakdown") class Config: json_encoders = { datetime: lambda v: v.isoformat() if v else None } json_schema_extra = { "example": { "timestamp": "2025-12-10T13:45:00", "total_flexibility_kw": 15.5, "devices": [ { "device_id": "sensor_1", "available_kw": 3.5, "instruction": "participation", "current_power": 3.5 }, { "device_id": "sensor_2", "available_kw": 0.8, "instruction": "shifting", "current_power": 4.0 } ] } } class DeviceInstructionUpdate(BaseModel): """Model for updating device instructions""" device_id: str = Field(..., description="Device identifier") instructions: Dict[str, str] = Field(..., description="Hourly instructions (hour -> instruction type)") class Config: json_schema_extra = { "example": { "device_id": "sensor_1", "instructions": { "0": "participation", "1": "shifting", "2": "off", "3": "participation" } } } # Configuration Models class AutoResponseConfig(BaseModel): """Auto-response configuration model""" enabled: bool = Field(..., description="Whether auto-response is enabled") max_reduction_percentage: float = Field(20.0, description="Maximum reduction percentage for auto-accept", ge=0, le=100) response_delay_seconds: int = Field(300, description="Delay before auto-responding (seconds)", ge=0) min_notice_minutes: int = Field(60, description="Minimum notice required for auto-accept (minutes)", ge=0) class Config: json_schema_extra = { "example": { "enabled": True, "max_reduction_percentage": 20.0, "response_delay_seconds": 300, "min_notice_minutes": 60 } } # Response Models class InvitationSendResponse(BaseModel): """Response for sending invitation""" event_id: str = Field(..., description="Created event identifier") response: str = Field(..., description="Initial response status") message: str = Field(..., description="Status message") class InvitationAnswerResponse(BaseModel): """Response for answering invitation""" success: bool = Field(..., description="Whether answer was recorded") message: str = Field(..., description="Status message") class EventScheduleResponse(BaseModel): """Response for scheduling event""" event_id: str = Field(..., description="Scheduled event identifier") message: str = Field(..., description="Status message") class PerformanceAnalytics(BaseModel): """Performance analytics response""" period_days: int = Field(..., description="Analysis period in days") total_events: int = Field(..., description="Total number of events") total_reduction_kwh: float = Field(..., description="Total energy reduced") total_target_kwh: float = Field(..., description="Total target reduction") average_reduction_kwh: float = Field(..., description="Average reduction per event") achievement_rate: float = Field(..., description="Achievement rate (%)") average_event_duration_minutes: int = Field(..., description="Average event duration") # Health Check Model class HealthResponse(BaseModel): """Health check response model""" service: str = Field(..., description="Service name") status: str = Field(..., description="Service status") timestamp: datetime = Field(..., description="Check timestamp") version: str = Field(..., description="Service version") class Config: json_encoders = { datetime: lambda v: v.isoformat() if v else None } json_schema_extra = { "example": { "service": "demand-response-service", "status": "healthy", "timestamp": "2025-12-10T13:45:00", "version": "1.0.0" } }