notification.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. """Pydantic schemas for notification providers."""
  2. from datetime import datetime
  3. from enum import Enum
  4. from typing import Any
  5. from pydantic import BaseModel, Field, field_validator
  6. class ProviderType(str, Enum):
  7. """Supported notification provider types."""
  8. CALLMEBOT = "callmebot"
  9. NTFY = "ntfy"
  10. PUSHOVER = "pushover"
  11. TELEGRAM = "telegram"
  12. EMAIL = "email"
  13. class NotificationProviderBase(BaseModel):
  14. """Base schema for notification providers."""
  15. name: str = Field(..., min_length=1, max_length=100, description="User-defined name")
  16. provider_type: ProviderType = Field(..., description="Type of notification provider")
  17. enabled: bool = Field(default=True, description="Whether notifications are enabled")
  18. config: dict[str, Any] = Field(..., description="Provider-specific configuration")
  19. # Event triggers - print lifecycle
  20. on_print_start: bool = Field(default=False, description="Notify on print start")
  21. on_print_complete: bool = Field(default=True, description="Notify on print complete")
  22. on_print_failed: bool = Field(default=True, description="Notify on print failed")
  23. on_print_stopped: bool = Field(default=True, description="Notify when print is stopped/cancelled")
  24. on_print_progress: bool = Field(default=False, description="Notify at 25%, 50%, 75% progress")
  25. # Event triggers - printer status
  26. on_printer_offline: bool = Field(default=False, description="Notify when printer goes offline")
  27. on_printer_error: bool = Field(default=False, description="Notify on printer errors (AMS, etc.)")
  28. on_filament_low: bool = Field(default=False, description="Notify when filament is running low")
  29. # Quiet hours
  30. quiet_hours_enabled: bool = Field(default=False, description="Enable quiet hours")
  31. quiet_hours_start: str | None = Field(default=None, description="Start time in HH:MM format")
  32. quiet_hours_end: str | None = Field(default=None, description="End time in HH:MM format")
  33. # Printer filter
  34. printer_id: int | None = Field(default=None, description="Specific printer ID or null for all")
  35. @field_validator("quiet_hours_start", "quiet_hours_end")
  36. @classmethod
  37. def validate_time_format(cls, v: str | None) -> str | None:
  38. if v is None:
  39. return v
  40. try:
  41. parts = v.split(":")
  42. if len(parts) != 2:
  43. raise ValueError("Invalid time format")
  44. hour, minute = int(parts[0]), int(parts[1])
  45. if not (0 <= hour <= 23 and 0 <= minute <= 59):
  46. raise ValueError("Invalid time range")
  47. return f"{hour:02d}:{minute:02d}"
  48. except (ValueError, TypeError):
  49. raise ValueError("Time must be in HH:MM format (e.g., 22:00)")
  50. class NotificationProviderCreate(NotificationProviderBase):
  51. """Schema for creating a notification provider."""
  52. pass
  53. class NotificationProviderUpdate(BaseModel):
  54. """Schema for updating a notification provider (all fields optional)."""
  55. name: str | None = Field(default=None, min_length=1, max_length=100)
  56. provider_type: ProviderType | None = None
  57. enabled: bool | None = None
  58. config: dict[str, Any] | None = None
  59. # Event triggers - print lifecycle
  60. on_print_start: bool | None = None
  61. on_print_complete: bool | None = None
  62. on_print_failed: bool | None = None
  63. on_print_stopped: bool | None = None
  64. on_print_progress: bool | None = None
  65. # Event triggers - printer status
  66. on_printer_offline: bool | None = None
  67. on_printer_error: bool | None = None
  68. on_filament_low: bool | None = None
  69. # Quiet hours
  70. quiet_hours_enabled: bool | None = None
  71. quiet_hours_start: str | None = None
  72. quiet_hours_end: str | None = None
  73. # Printer filter
  74. printer_id: int | None = None
  75. class NotificationProviderResponse(NotificationProviderBase):
  76. """Schema for notification provider API responses."""
  77. id: int
  78. last_success: datetime | None = None
  79. last_error: str | None = None
  80. last_error_at: datetime | None = None
  81. created_at: datetime
  82. updated_at: datetime
  83. class Config:
  84. from_attributes = True
  85. class NotificationTestRequest(BaseModel):
  86. """Schema for testing notification configuration."""
  87. provider_type: ProviderType
  88. config: dict[str, Any]
  89. class NotificationTestResponse(BaseModel):
  90. """Schema for test notification response."""
  91. success: bool
  92. message: str
  93. # Provider-specific config schemas for documentation/validation reference
  94. class CallMeBotConfig(BaseModel):
  95. """CallMeBot/WhatsApp configuration."""
  96. phone: str = Field(..., description="Phone number with country code (e.g., +1234567890)")
  97. apikey: str = Field(..., description="API key from CallMeBot")
  98. class NtfyConfig(BaseModel):
  99. """ntfy configuration."""
  100. server: str = Field(default="https://ntfy.sh", description="ntfy server URL")
  101. topic: str = Field(..., description="Topic name to publish to")
  102. auth_token: str | None = Field(default=None, description="Optional authentication token")
  103. class PushoverConfig(BaseModel):
  104. """Pushover configuration."""
  105. user_key: str = Field(..., description="Your Pushover user key")
  106. app_token: str = Field(..., description="Your Pushover application token")
  107. priority: int = Field(default=0, ge=-2, le=2, description="Message priority (-2 to 2)")
  108. class TelegramConfig(BaseModel):
  109. """Telegram bot configuration."""
  110. bot_token: str = Field(..., description="Bot token from @BotFather")
  111. chat_id: str = Field(..., description="Chat ID to send messages to")
  112. class EmailConfig(BaseModel):
  113. """Email/SMTP configuration."""
  114. smtp_server: str = Field(..., description="SMTP server hostname")
  115. smtp_port: int = Field(default=587, description="SMTP port (587 for TLS, 465 for SSL)")
  116. username: str = Field(..., description="SMTP username/email")
  117. password: str = Field(..., description="SMTP password or app password")
  118. from_email: str = Field(..., description="From email address")
  119. to_email: str = Field(..., description="Recipient email address")
  120. use_tls: bool = Field(default=True, description="Use TLS encryption")