# SPDX-License-Identifier: GPL-3.0-or-later
# Copyright (C) 2025 Jibril Sharafi

openapi: 3.0.0
info:
  title: EnergyMe - Home | API
  description: API documentation for EnergyMe - Home
  version: 1.1.0

servers:
  - url: /
    description: Current server (auto-detected from browser)

paths:
  # Health check endpoint
  /api/v1/health:
    get:
      summary: Health check
      description: Check if the API is running and responsive
      tags:
        - System
      responses:
        '200':
          description: Service is healthy
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: "ok"
                  uptime:
                    type: integer
                    format: seconds
                  timestamp:
                    type: string
                    format: date-time

  # Authentication endpoints  
  /api/v1/auth/status:
    get:
      summary: Get authentication status
      description: Check if the current session is authenticated
      tags:
        - Authentication
      responses:
        '200':
          description: Authentication status
          content:
            application/json:
              schema:
                type: object
                properties:
                  usingDefaultPassword:
                    type: boolean
                  username:
                    type: string

  /api/v1/auth/change-password:
    post:
      summary: Change password
      description: Change the authentication password (requires current password for security)
      tags:
        - Authentication
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - currentPassword
                - newPassword
              properties:
                currentPassword:
                  type: string
                  description: Current password for verification
                newPassword:
                  type: string
                  description: New password to set
            example:
              currentPassword: "oldpassword123"
              newPassword: "newpassword456"
      responses:
        '200':
          $ref: '#/components/responses/Success'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /api/v1/auth/reset-password:
    post:
      summary: Reset password to default
      description: Reset the authentication password to the default value
      tags:
        - Authentication
      responses:
        '200':
          $ref: '#/components/responses/Success'
        '500':
          $ref: '#/components/responses/InternalServerError'

  # OTA (Over-The-Air) Update endpoints
  /api/v1/ota/upload:
    post:
      summary: Upload firmware for OTA update
      description: |
        Upload a new firmware binary for over-the-air update.
        
        **Requirements:**
        - File must be in .bin format
        - Minimum file size: 100KB
        - File must contain valid ESP32 firmware
        - Optional: Include X-MD5 header for verification
      tags:
        - OTA Updates
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                firmware:
                  type: string
                  format: binary
                  description: Firmware binary file (.bin format, minimum 100KB)
      parameters:
        - in: header
          name: X-MD5
          required: false
          schema:
            type: string
            pattern: '^[a-f0-9]{32}$'
          description: Optional MD5 hash of the firmware file for verification
      responses:
        '200':
          $ref: '#/components/responses/Success'
        '400':
          $ref: '#/components/responses/BadRequest'

  /api/v1/ota/status:
    get:
      summary: Get OTA update status
      description: Get information about the current firmware and update status
      tags:
        - OTA Updates
      responses:
        '200':
          description: OTA status information
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: "idle"
                  canRollback:
                    type: boolean
                    example: true
                  currentPartition:
                    type: string
                    example: "app0"
                  hasError:
                    type: boolean
                    example: false
                  lastError:
                    type: string
                    example: "No Error"
                  size:
                    type: integer
                    example: 0
                  progress:
                    type: integer
                    example: 0
                  remaining:
                    type: integer
                    example: 0
                  progressPercent:
                    type: integer
                    example: 0
                  currentVersion:
                    type: string
                    example: "00.11.04"
                  currentMD5:
                    type: string
                    example: "ad1783afd0b38f856d5010921712e2ed"

  /api/v1/ota/rollback:
    post:
      summary: Rollback firmware
      description: Rollback to the previous firmware version
      tags:
        - OTA Updates
      responses:
        '200':
          $ref: '#/components/responses/Success'
        '400':
          $ref: '#/components/responses/BadRequest'

  # System Management endpoints
  /api/v1/system/info:
    get:
      summary: Get system information
      description: Get detailed system information including firmware, hardware, build details, and safe mode status
      tags:
        - System
      responses:
        '200':
          description: System information
          content:
            application/json:
              schema:
                type: object
                properties:
                  static:
                    type: object
                    description: |
                      Static system information (hardware, firmware version, etc.).
                      Groups: `product`, `firmware`, `hardware`, `sdk`, `monitoring`, `device`, `factory`.
                      `factory.serialNumber` and `factory.manufacturingUnixTs` come from NVS `factory_ns`
                      and default to `"Unknown"` / `0` on community (unprovisioned) devices.
                  dynamic:
                    type: object
                    description: |
                      Dynamic system information (uptime, memory usage, temperature, etc.).
                      Includes a `cpu` block with auto-calibrated per-core load %, and per-task
                      heartbeat fields (`lastTickMillis`, `loopCount`, `staleMs`) under each
                      entry in `tasks` for starvation/stuck-task diagnosis.
                    properties:
                      cpu:
                        type: object
                        description: |
                          Per-core CPU load %, auto-calibrated by the FreeRTOS idle-hook profiler.
                          Each core's "100% idle" baseline is the highest idle-tick rate ever
                          observed; load = `100 * (1 - currentRate / baselineRate)`. Reports 0
                          before the first sample window completes (~1 s after boot).
                        properties:
                          core0Percent:
                            type: integer
                            minimum: 0
                            maximum: 100
                            example: 38
                          core1Percent:
                            type: integer
                            minimum: 0
                            maximum: 100
                            example: 12
                          sampleWindowMs:
                            type: integer
                            description: Wall-clock milliseconds covered by the most recent sample window (nominally ~1000)
                            example: 1000
                      tasks:
                        type: object
                        description: |
                          Per-task block. Each entry has the existing stack fields plus heartbeat
                          fields. `lastTickMillis` is updated by the task at the top of every loop
                          iteration; `staleMs` is `now - lastTickMillis` (or 0 if the task is not
                          instrumented, e.g. one-shot tasks like `mqttOta`). A `staleMs` value
                          significantly larger than the task's expected loop interval indicates
                          the task is starved or stuck.
                        additionalProperties:
                          type: object
                          properties:
                            allocatedStack:
                              type: integer
                              description: Allocated stack size in bytes
                            minimumFreeStack:
                              type: integer
                              description: Lowest free stack ever observed (uxTaskGetStackHighWaterMark)
                            freePercentage:
                              type: number
                            usedPercentage:
                              type: number
                            lastTickMillis:
                              type: integer
                              description: millis64() at the top of the task's last loop iteration; 0 if task is not instrumented
                            loopCount:
                              type: integer
                              description: Total loop iterations since boot (instrumented tasks only)
                            staleMs:
                              type: integer
                              description: Milliseconds since last heartbeat; 0 if task is not instrumented
                  safeMode:
                    type: object
                    description: Safe mode status and restart protection information
                    properties:
                      active:
                        type: boolean
                        description: Whether safe mode is currently active
                      canRestartNow:
                        type: boolean
                        description: Whether the device can be restarted now
                      minimumUptimeRemainingMs:
                        type: integer
                        description: Milliseconds remaining before restart is allowed
                      message:
                        type: string
                        description: Human-readable message about safe mode (only present if active)
                      action:
                        type: string
                        description: Recommended recovery action (only present if active)

  /api/v1/system/statistics:
    get:
      summary: Get system statistics
      description: Get runtime statistics and performance metrics
      tags:
        - System
      responses:
        '200':
          description: System statistics
          content:
            application/json:
              schema:
                type: object
                properties:
                  ade7953TotalInterrupts:
                    type: integer
                  ade7953TotalHandledInterrupts:
                    type: integer
                  ade7953ZxInterrupts:
                    type: integer
                  ade7953ServicePasses:
                    type: integer
                    description: Semaphore takes serviced; ade7953TotalInterrupts minus this is ISR wakeups coalesced into another pass
                  ade7953UnhandledInterrupts:
                    type: integer
                    description: Interrupt status snapshots with none of the known bits set
                  ade7953ReadingCount:
                    type: integer
                  ade7953ReadingCountFailure:
                    type: integer
                  mqttMessagesPublished:
                    type: integer
                  mqttMessagesPublishedError:
                    type: integer
                  mqttMeterPointsDropped:
                    type: integer
                    description: Meter queue was full; oldest point evicted to make room
                  mqttGridPointsDropped:
                    type: integer
                    description: Grid queue was full; oldest point evicted to make room
                  customMqttMessagesPublished:
                    type: integer
                  customMqttMessagesPublishedError:
                    type: integer
                  modbusRequests:
                    type: integer
                  modbusRequestsError:
                    type: integer
                  influxdbUploadCount:
                    type: integer
                  influxdbUploadCountError:
                    type: integer
                  wifiConnection:
                    type: integer
                  wifiConnectionError:
                    type: integer
                  webServerRequests:
                    type: integer
                  webServerRequestsError:
                    type: integer
                  logVerbose:
                    type: integer
                  logDebug:
                    type: integer
                  logInfo:
                    type: integer
                  logWarning:
                    type: integer
                  logError:
                    type: integer
                  logFatal:
                    type: integer
                  logDropped:
                    type: integer

  /api/v1/system/restart:
    post:
      summary: Restart the system
      description: Initiate a system restart
      tags:
        - System
      responses:
        '200':
          $ref: '#/components/responses/Success'
        '423':
          description: Restart locked
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                success: false
                error: "Failed to initiate restart. Another restart may already be in progress or restart is currently locked"

  /api/v1/system/factory-reset:
    post:
      summary: Factory reset
      description: Reset the system to factory defaults (clears all configuration)
      tags:
        - System
      responses:
        '200':
          $ref: '#/components/responses/Success'

  /api/v1/system/safe-mode:
    get:
      summary: Get safe mode status
      description: Check if the device is in safe mode (rapid restart protection) and get timing information
      tags:
        - System
      responses:
        '200':
          description: Safe mode status
          content:
            application/json:
              schema:
                type: object
                properties:
                  active:
                    type: boolean
                    description: Whether safe mode is currently active
                    example: true
                  canRestartNow:
                    type: boolean
                    description: Whether the device can be restarted now (minimum uptime reached)
                    example: false
                  minimumUptimeRemainingMs:
                    type: integer
                    description: Milliseconds remaining before restart is allowed
                    example: 180000
                  message:
                    type: string
                    description: Human-readable message about safe mode status
                    example: "Device in safe mode - restart protection active to prevent loops"
                  action:
                    type: string
                    description: Recommended action to recover from safe mode
                    example: "Perform OTA update to fix underlying issue"

  /api/v1/system/safe-mode/clear:
    post:
      summary: Clear safe mode
      description: Manually clear safe mode counters and restart to re-enable ADE7953 monitoring
      tags:
        - System
      responses:
        '200':
          $ref: '#/components/responses/Success'

  /api/v1/system/secrets:
    get:
      summary: Check secrets availability
      description: Check if the system has secrets/certificates available
      tags:
        - System
      responses:
        '200':
          description: Secrets status
          content:
            application/json:
              schema:
                type: object
                properties:
                  hasSecrets:
                    type: boolean

  /api/v1/system/time:
    get:
      summary: Get system time
      description: Get current system time and synchronization status
      tags:
        - System
      responses:
        '200':
          description: System time information
          content:
            application/json:
              schema:
                type: object
                properties:
                  synced:
                    type: boolean
                    description: Whether time is synchronized (via NTP or manual)
                    example: true
                  unixTime:
                    type: integer
                    description: Current Unix timestamp in seconds
                    example: 1732620000
                  isoTime:
                    type: string
                    description: Current time in ISO 8601 format (UTC)
                    example: "2025-11-26T12:00:00.000Z"
    post:
      summary: Set system time
      description: Manually set system time for devices without internet connectivity. Useful for static IP setups or isolated networks where NTP is unavailable.
      tags:
        - System
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - unixTime
              properties:
                unixTime:
                  type: integer
                  description: Unix timestamp in seconds to set
                  example: 1732620000
      responses:
        '200':
          description: Time set successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                    example: "Time synchronized"
                  newTime:
                    type: string
                    description: New time in ISO 8601 format
                    example: "2025-11-26T12:00:00.000Z"
        '400':
          $ref: '#/components/responses/BadRequest'

  /api/v1/system/issues:
    get:
      summary: Get device issues
      description: List current device issues (runtime-derived, ISA-18.2-lite lifecycle). Issues disappear only when the condition is gone and the user acknowledged them.
      tags:
        - System
      responses:
        '200':
          description: Device issues
          content:
            application/json:
              schema:
                type: object
                properties:
                  issues:
                    type: array
                    items:
                      type: object
                      properties:
                        code:
                          type: string
                          description: Issue code (snake_case)
                          example: "channel_polarity_mismatch"
                        channel:
                          type: integer
                          description: Channel index (omitted for global issues)
                          example: 0
                        state:
                          type: string
                          enum: [active_unacked, active_acked, cleared_unacked]
                          example: "active_unacked"
                        severity:
                          type: string
                          enum: [info, warning, error]
                          example: "error"
                        firstSeenUnix:
                          type: integer
                          description: Unix timestamp when the issue was first raised
                          example: 1781099726
                        lastChangeUnix:
                          type: integer
                          description: Unix timestamp of the last state change
                          example: 1781099726
                        occurrences:
                          type: integer
                          description: Number of times the issue was raised this boot
                          example: 1
                        message:
                          type: string
                          description: Human-readable description
                          example: "Channel 0: readings persistently negative and clamped to 0 W. The reverse flag and the physical CT orientation disagree - fix either."

  /api/v1/system/issues/ack:
    post:
      summary: Acknowledge device issues
      description: Acknowledge a single issue (by code, plus channel for channel-scoped codes) or all issues at once. Acknowledged active issues stay listed until their condition clears; acknowledged cleared issues are removed.
      tags:
        - System
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                all:
                  type: boolean
                  description: Acknowledge every unacknowledged issue
                  example: true
                code:
                  type: string
                  description: Issue code to acknowledge (alternative to all)
                  example: "channel_polarity_mismatch"
                channel:
                  type: integer
                  description: Channel index (required for channel-scoped codes)
                  example: 0
      responses:
        '200':
          $ref: '#/components/responses/Success'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'

  /api/v1/firmware/update-info:
    get:
      summary: Get firmware update information
      description: Get information about current firmware version, build details, and available updates
      tags:
        - System
      responses:
        '200':
          description: Firmware update information
          content:
            application/json:
              schema:
                type: object
                properties:
                  currentVersion:
                    type: string
                    description: Current firmware version
                    example: "00.11.09"
                  buildDate:
                    type: string
                    description: Firmware build date
                    example: "Aug  3 2025"
                  isLatest:
                    type: boolean
                    description: Whether the current firmware is the latest release for this major version
                    example: false
                  availableVersion:
                    type: string
                    description: Latest available version for this major version (only present when isLatest is false)
                    example: "2.0.1"
                  releaseDate:
                    type: string
                    description: Release date of the available version (only present when isLatest is false)
                    example: "2025-08-10T14:30:00Z"
                  updateUrl:
                    type: string
                    description: Direct download URL for the firmware binary (only present when isLatest is false)
                    example: "https://github.com/jibrilsharafi/EnergyMe-Home/releases/download/v2.0.1/energyme_home_2.0.1.bin"
                  changelogUrl:
                    type: string
                    description: GitHub release page URL with changelog (only present when isLatest is false)
                    example: "https://github.com/jibrilsharafi/EnergyMe-Home/releases/tag/v2.0.1"

  # File Operations endpoints
  /api/v1/list-files:
    get:
      summary: List LittleFS files
      description: |
        Get a list of all files stored in LittleFS with their sizes.
        Optionally filter by folder path to list only files within a specific directory.
        
        Energy data is organized in subdirectories:
        - `energy/daily/` - Daily energy CSV files (YYYY-MM-DD.csv.gz)
        - `energy/monthly/` - Monthly consolidated archives (YYYY-MM.csv.gz)
        - `energy/yearly/` - Yearly consolidated archives (YYYY.csv.gz)
      tags:
        - Files
      parameters:
        - name: folder
          in: query
          required: false
          description: |
            Optional folder path to filter files. When provided, only files within this 
            folder are returned with paths relative to the folder.
            Examples: `energy/daily`, `energy/monthly`, `energy/yearly`
          schema:
            type: string
          example: ""
      responses:
        '200':
          description: List of files with their sizes
          content:
            application/json:
              schema:
                type: object
                additionalProperties:
                  type: integer
                  description: File size in bytes
              examples:
                allFiles:
                  summary: All files (no folder filter)
                  value:
                    "log.txt": 2048
                    "energy/daily/2025-11-25.csv.gz": 512
                    "energy/monthly/2025-10.csv.gz": 1024
                dailyFiles:
                  summary: Daily energy files only
                  value:
                    "2025-11-25.csv.gz": 512
                    "2025-11-24.csv.gz": 498
                    "2025-11-23.csv.gz": 501
        '500':
          $ref: '#/components/responses/InternalServerError'

  /api/v1/files/{filepath}:
    get:
      summary: Get file content
      description: Download or view the content of a specific file from LittleFS
      tags:
        - Files
      parameters:
        - name: filepath
          in: path
          required: true
          description: Path to the file in LittleFS (without leading slash)
          schema:
            type: string
          example: "logger/log.txt"
        - name: download
          in: query
          required: false
          description: Force file to be downloaded as attachment instead of displayed inline
          schema:
            type: boolean
            default: false
          example: true
      responses:
        '200':
          description: File content returned successfully
          content:
            '*/*':
              schema:
                type: string
                format: binary
              examples:
                text_file:
                  summary: Text file content
                  value: "2025-08-03 12:34:56 [INFO] System started"
                json_file:
                  summary: JSON configuration file
                  value: |
                    {
                      "enabled": true,
                      "sampleTime": 1000
                    }
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'

    post:
      summary: Upload file to LittleFS
      description: |
        Upload a file to the LittleFS filesystem.
        
        **Requirements:**
        - File path is specified in the URL
        - Supports chunked uploads for large files
        - Minimum 1KB free space required
        - Existing files will be overwritten
      tags:
        - Files
      parameters:
        - name: filepath
          in: path
          required: true
          description: Target path for the file in LittleFS (without leading slash)
          schema:
            type: string
          example: "config/settings.json"
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                file:
                  type: string
                  format: binary
                  description: File content to upload
          application/octet-stream:
            schema:
              type: string
              format: binary
              description: Raw file content
      responses:
        '200':
          $ref: '#/components/responses/Success'
        '400':
          $ref: '#/components/responses/BadRequest'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /api/v1/delete-file:
    post:
      summary: Delete file from LittleFS
      description: |
        Remove a specific file from the LittleFS filesystem using a POST request with JSON body.
        This endpoint uses POST instead of DELETE to avoid wildcard routing issues with the web server.
      tags:
        - Files
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - path
              properties:
                path:
                  type: string
                  description: Path to the file in LittleFS (with or without leading slash)
                  example: "/energy/2025-10-11.csv.gz"
            examples:
              with_leading_slash:
                summary: Path with leading slash
                value:
                  path: "/energy/2025-10-11.csv.gz"
              without_leading_slash:
                summary: Path without leading slash
                value:
                  path: "energy/2025-10-11.csv.gz"
      responses:
        '200':
          $ref: '#/components/responses/Success'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'

  # Backup endpoints
  /api/v1/backup/configuration:
    get:
      summary: Download configuration backup as JSON file
      description: |
        Export the complete NVS configuration data as JSON for backup or transfer to another device.

        **Response:**
        - Returns JSON file with `Content-Disposition: attachment` header to force download
        - Filename format: `config_backup_<deviceId>_<timestamp>.json`
        - Each NVS key includes explicit type metadata for accurate restoration
      tags:
        - Backup
      responses:
        '200':
          description: Configuration backup data
          content:
            application/json:
              schema:
                type: object
                required:
                  - version
                  - type
                  - deviceId
                  - firmwareVersion
                  - sketchMD5
                  - timestamp
                  - nvs
                properties:
                  version:
                    type: integer
                    description: Backup format version
                    example: 1
                  type:
                    type: string
                    description: Backup type identifier
                    example: "configuration"
                  deviceId:
                    type: string
                    description: Unique device identifier
                    example: "A1B2C3D4E5F6"
                  firmwareVersion:
                    type: string
                    description: Current firmware version
                    example: "00.11.09"
                  sketchMD5:
                    type: string
                    description: MD5 hash of the current firmware sketch
                    example: "ad1783afd0b38f856d5010921712e2ed"
                  timestamp:
                    type: string
                    format: date-time
                    description: Backup timestamp in ISO 8601 format
                    example: "2025-02-15T10:30:45Z"
                  nvs:
                    type: object
                    description: NVS data organized by namespace
                    additionalProperties:
                      type: object
                      description: Namespace containing configuration keys and values
                      additionalProperties: true
                    example:
                      "preferences":
                        "sampleTime": 
                          type: u16
                          value: 1000
                        "enabled":
                          type: u8
                          value: 1
                      "mqtt":
                        "server":
                          type: str
                          value: "mqtt.example.com"
                        "port":
                          type: u16
                          value: 1883
          headers:
            Content-Disposition:
              description: File download header with device ID and ISO timestamp
              schema:
                type: string
              example: 'attachment; filename="config_backup_588c81c47a50_2026-02-16T13:01:54.177Z.json"'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /api/v1/backup/filesystem:
    get:
      summary: Download LittleFS filesystem backup (no temporary files)
      description: |
        Downloads a complete backup of the LittleFS filesystem as a TAR archive.
        Includes all energy data files, logs, and configuration files.

        **Key Features:**
        - **NO temporary files created in LittleFS** (works even when filesystem is >50% full)
        - Streamed directly from source files to HTTP response
        - Transfer typically completes in 1-2 seconds on local network
        - Supports multiple concurrent downloads

        **Extraction:**
        Extract on any system with tar support:
        ```bash
        tar -xf littlefs_backup_<deviceId>_<timestamp>.tar
        ```

        **Compression:**
        Archive is uncompressed TAR format (energy files are already .csv.gz).
        To compress after download:
        ```bash
        gzip littlefs_backup_<deviceId>_<timestamp>.tar
        ```
      tags:
        - Backup
      responses:
        '200':
          description: Backup streamed successfully
          content:
            application/x-tar:
              schema:
                type: string
                format: binary
          headers:
            Content-Disposition:
              description: File download header with device ID and ISO timestamp
              schema:
                type: string
              example: 'attachment; filename="littlefs_backup_588c81c47a50_2026-02-16T13:01:54.177Z.tar"'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /api/v1/restore/configuration:
    post:
      summary: Restore configuration from backup file
      description: |
        Restore NVS configuration from a previously downloaded backup JSON file.

        **Process:**
        1. Upload backup JSON file (multipart/form-data)
        2. Validates backup JSON format and metadata
        3. Checks firmware version compatibility (same major version, >= backup version)
        4. Checks device ID compatibility (configurable with ?force=true)
        5. Validates no excluded namespaces present (security/device-specific data)
        6. Saves validated backup to temporary storage
        7. Sets restore flag in NVS and triggers device restart
        8. During boot, restores all NVS keys before services start (natural "blocked state")
        9. Device continues normal boot with restored configuration

        **Important Notes:**
        - Backup must be from same major firmware version (e.g., 1.x.x can restore on 1.y.z where y.z >= x.x)
        - Restores values exactly as they were backed up (type-safe with explicit type metadata)
        - Skips sensitive namespaces: auth_ns, nvs.net80211, phy, factory_ns
        - Device will restart automatically
        - Cross-device restore is blocked by default (use ?force=true to override)
        - Partial restore continues even if some keys fail to restore

        **Example curl command:**
        ```bash
        curl --digest -u admin:energyme00 -X POST \
          -F "file=@config_backup_deviceid_timestamp.json" \
          http://192.168.2.75/api/v1/restore/configuration
        ```
      tags:
        - Restore
      parameters:
        - name: force
          in: query
          schema:
            type: boolean
          description: Skip device ID validation (allows cross-device restore)
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                file:
                  type: string
                  format: binary
                  description: Configuration backup JSON file (must include version, type, firmwareVersion, and nvs fields)
      responses:
        '200':
          description: Configuration restore initiated (device will restart)
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: "success"
                  message:
                    type: string
                    example: "Configuration restore initiated. Device will restart."
        '400':
          description: Invalid backup format, version mismatch, or device mismatch
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: "error"
                  message:
                    type: string
                    example: "Firmware version incompatible: backup from 1.5.0, current 1.0.0. Can only restore backups from same major version <= current."
        '500':
          $ref: '#/components/responses/InternalServerError'

  /api/v1/restore/filesystem:
    post:
      summary: Restore filesystem from TAR backup
      description: |
        Restore LittleFS filesystem from a previously downloaded TAR archive.

        **Process:**
        1. Validates available storage space
        2. Receives and saves TAR file to temporary location in LittleFS
        3. Extracts TAR archive to LittleFS root (overwrites existing files)
        4. Removes temporary TAR file

        **Important Notes:**
        - Extraction overwrites existing files with same names
        - Requires sufficient free space to temporarily store the TAR file
        - All timestamps and permissions are normalized during extraction
      tags:
        - Restore
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                file:
                  type: string
                  format: binary
                  description: LittleFS TAR backup file
      responses:
        '200':
          description: Filesystem restored successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: "success"
                  message:
                    type: string
                    example: "Filesystem restored successfully"
        '400':
          description: Invalid TAR format
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: "error"
                  message:
                    type: string
                    example: "Invalid TAR format"
        '500':
          $ref: '#/components/responses/InternalServerError'

  # Network Management endpoints
  /api/v1/network/wifi/reset:
    post:
      summary: Reset WiFi configuration
      description: Reset WiFi settings and restart the WiFi connection
      tags:
        - Network
      responses:
        '200':
          $ref: '#/components/responses/Success'

  /api/v1/network/wifi/credentials:
    post:
      summary: Set WiFi credentials
      description: |
        Save new WiFi credentials and immediately restart the device.
        After receiving new credentials, the device will save them and perform a restart.
        Upon reboot, the device will attempt to connect to the specified WiFi network using the new credentials.
        
        **Note:** The API returns as soon as the credentials are saved and the restart is triggered. The device will temporarily be unavailable during the restart process.
        Monitor the device status or LED indicators to verify successful connection after reboot.
      tags:
        - Network
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - ssid
                - password
              properties:
                ssid:
                  type: string
                  description: WiFi SSID (network name)
                  minLength: 1
                  maxLength: 32
                  example: "MyHomeNetwork"
                password:
                  type: string
                  description: WiFi password (WPA/WPA2/WPA3). Use empty string for open networks.
                  minLength: 0
                  maxLength: 63
                  example: "mySecurePassword123"
            examples:
              home_network:
                summary: Home WiFi
                value:
                  ssid: "MyHomeNetwork"
                  password: "mySecurePassword123"
              open_network:
                summary: Open network (no password)
                value:
                  ssid: "FreeWiFi"
                  password: ""
      responses:
        '200':
          description: WiFi credentials successfully queued for connection attempt
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    example: "WiFi credentials updated successfully. It will restart and attempt to connect to the new network."
        '400':
          description: Invalid request parameters
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                    example: "SSID exceeds maximum length of 32 characters"
        '500':
          description: Failed to queue credentials (e.g., WiFi task not running)
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                    example: "Failed to queue WiFi credentials. Please try again."

  /api/v1/network/config:
    get:
      summary: Get network configuration
      description: Retrieve the current network configuration (static IP).
      tags:
        - Network
      responses:
        '200':
          description: Current network configuration
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NetworkConfiguration'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    put:
      summary: Update network configuration (full update)
      description: |
        Replace the full network configuration. All fields are required.
        The device restarts to apply the new settings, so it will be temporarily unavailable.

        **Warning:** A misconfigured static IP can make the device unreachable. Recover by
        power-cycling and using the WiFi config portal / reset.
      tags:
        - Network
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NetworkConfiguration'
            examples:
              static_ip:
                summary: Static IP
                value:
                  useStaticIp: true
                  ip: "192.168.1.50"
                  gateway: "192.168.1.1"
                  subnet: "255.255.255.0"
                  dns1: "8.8.8.8"
                  dns2: "1.1.1.1"
                  fallbackToDhcp: true
              dhcp:
                summary: DHCP (default)
                value:
                  useStaticIp: false
                  ip: ""
                  gateway: ""
                  subnet: ""
                  dns1: ""
                  dns2: ""
                  fallbackToDhcp: true
      responses:
        '200':
          $ref: '#/components/responses/Success'
        '400':
          description: Invalid network configuration
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    patch:
      summary: Update network configuration (partial update)
      description: |
        Update one or more network configuration fields. At least one valid field is required.
        Cross-field rules still apply (enabling static IP requires valid ip/gateway/subnet).
        The device restarts to apply the new settings.
      tags:
        - Network
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NetworkConfiguration'
            examples:
              enable_static_ip:
                summary: Switch to a static IP
                value:
                  useStaticIp: true
                  ip: "192.168.1.50"
                  gateway: "192.168.1.1"
                  subnet: "255.255.255.0"
      responses:
        '200':
          $ref: '#/components/responses/Success'
        '400':
          description: Invalid network configuration
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/v1/network/config/reset:
    post:
      summary: Reset network configuration
      description: Reset the network configuration to defaults (DHCP). The device restarts to apply.
      tags:
        - Network
      responses:
        '200':
          $ref: '#/components/responses/Success'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  # Crash Monitor endpoints
  /api/v1/crash/info:
    get:
      summary: Get crash analysis information
      description: Retrieve comprehensive crash information including reset reason, backtrace, and core dump availability
      tags:
        - Crash Monitor
      responses:
        '200':
          description: Crash information retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  resetReason:
                    type: string
                    example: "Task watchdog"
                  resetReasonCode:
                    type: integer
                    example: 6
                  crashCount:
                    type: integer
                    example: 3
                  consecutiveCrashCount:
                    type: integer
                    example: 0
                  resetCount:
                    type: integer
                    example: 15
                  consecutiveResetCount:
                    type: integer
                    example: 0
                  hasCoreDump:
                    type: boolean
                    example: true
                  coreDumpSize:
                    type: integer
                    example: 40356
                  coreDumpAddress:
                    type: integer
                    example: 16711680
                  taskName:
                    type: string
                    example: "loopTask"
                  programCounter:
                    type: integer
                    example: 1107625244
                  taskControlBlock:
                    type: integer
                    example: 1070273268
                  appElfSha256:
                    type: string
                    example: "820c1a865"
                  backtrace:
                    type: object
                    properties:
                      depth:
                        type: integer
                        example: 2
                      corrupted:
                        type: boolean
                        example: false
                      addresses:
                        type: array
                        items:
                          type: integer
                        example: [1107625244, 1077407178]
                      debugCommand:
                        type: string
                        example: "xtensa-esp32-elf-addr2line -pfC -e .pio/build/esp32dev/firmware.elf 0x4205051c 0x4037edca "
        '500':
          $ref: '#/components/responses/InternalServerError'

  /api/v1/crash/dump:
    get:
      summary: Get core dump data
      description: Retrieve core dump binary data as base64-encoded chunks with pagination support
      tags:
        - Crash Monitor
      parameters:
        - name: offset
          in: query
          description: Starting byte offset in the core dump
          required: false
          schema:
            type: integer
            default: 0
            minimum: 0
        - name: size
          in: query
          description: Chunk size in bytes
          required: false
          schema:
            type: integer
            default: 2048
            minimum: 1
            maximum: 8192
      responses:
        '200':
          description: Core dump chunk retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  totalSize:
                    type: integer
                    description: Total size of the core dump
                    example: 4096
                  offset:
                    type: integer
                    description: Starting offset of this chunk
                    example: 0
                  requestedChunkSize:
                    type: integer
                    description: Requested chunk size
                    example: 1024
                  actualChunkSize:
                    type: integer
                    description: Actual chunk size returned
                    example: 1024
                  hasMore:
                    type: boolean
                    description: Whether more data is available
                    example: true
                  encoding:
                    type: string
                    description: Data encoding format
                    example: "base64"
                  data:
                    type: string
                    description: Base64-encoded core dump data
                    example: "SGVsbG8gV29ybGQ="
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /api/v1/crash/clear:
    post:
      summary: Clear core dump from flash
      description: Remove the stored core dump from flash memory
      tags:
        - Crash Monitor
      responses:
        '200':
          $ref: '#/components/responses/Success'
        '404':
          $ref: '#/components/responses/NotFound'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'

  # Logging endpoints
  /api/v1/logs/level:
    get:
      summary: Get current log levels
      description: Get the current print and save log levels
      tags:
        - Logging
      responses:
        '200':
          description: Current log levels
          content:
            application/json:
              schema:
                type: object
                properties:
                  print:
                    type: string
                    enum: [VERBOSE, DEBUG, INFO, WARNING, ERROR, FATAL]
                    example: "INFO"
                  save:
                    type: string
                    enum: [VERBOSE, DEBUG, INFO, WARNING, ERROR, FATAL]
                    example: "WARNING"
    put:
      summary: Set log levels (full update)
      description: Update both print and save log levels. Both fields are required for a full update.
      tags:
        - Logging
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - print
                - save
              properties:
                print:
                  $ref: '#/components/schemas/LogLevel'
                  description: Set the print log level
                save:
                  $ref: '#/components/schemas/LogLevel'
                  description: Set the save log level
            example:
              print: "DEBUG"
              save: "ERROR"
      responses:
        '200':
          $ref: '#/components/responses/Success'
        '400':
          $ref: '#/components/responses/BadRequest'
    patch:
      summary: Update log levels (partial update)
      description: Update one or both log levels. At least one field must be provided.
      tags:
        - Logging
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                print:
                  $ref: '#/components/schemas/LogLevel'
                  description: Set the print log level (optional)
                save:
                  $ref: '#/components/schemas/LogLevel'
                  description: Set the save log level (optional)
              minProperties: 1
            examples:
              print_only:
                summary: Update print level only
                value:
                  print: "DEBUG"
              save_only:
                summary: Update save level only
                value:
                  save: "ERROR"
              both:
                summary: Update both levels
                value:
                  print: "DEBUG"
                  save: "ERROR"
      responses:
        '200':
          $ref: '#/components/responses/Success'
        '400':
          $ref: '#/components/responses/BadRequest'

  /api/v1/logs:
    get:
      summary: Get all logs
      description: Retrieve all stored log entries
      tags:
        - Logging
      responses:
        '200':
          description: A list of all log entries
          content:
            text/plain:
              schema:
                type: string
                description: Log entries in plain text format

  /api/v1/logs/clear:
    post:
      summary: Clear logs
      description: Clear all stored log entries
      tags:
        - Logging
      responses:
        '200':
          $ref: '#/components/responses/Success'

  /api/v1/logs-udp-destination:
    get:
      summary: Get UDP log destination
      description: Get the current IP address where UDP logs are sent
      tags:
        - Logging
      responses:
        '200':
          description: Current UDP log destination
          content:
            application/json:
              schema:
                type: object
                properties:
                  destination:
                    type: string
                    format: ipv4
                    example: "239.255.255.250"
        '500':
          $ref: '#/components/responses/InternalServerError'
    put:
      summary: Set UDP log destination
      description: Configure the IP address where UDP logs should be sent (supports both unicast and multicast addresses)
      tags:
        - Logging
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - destination
              properties:
                destination:
                  type: string
                  format: ipv4
                  description: IP address for UDP log destination (e.g., "192.168.1.100" for unicast or "239.255.255.250" for multicast)
            example:
              destination: "192.168.1.100"
      responses:
        '200':
          $ref: '#/components/responses/Success'
        '400':
          $ref: '#/components/responses/BadRequest'

  # Custom MQTT endpoints
  /api/v1/custom-mqtt/config:
    get:
      summary: Get custom MQTT configuration
      description: Retrieve the current custom MQTT configuration settings
      tags:
        - Custom MQTT
      responses:
        '200':
          description: Current custom MQTT configuration
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CustomMqttConfiguration'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    put:
      summary: Update custom MQTT configuration (full update)
      description: Update the complete custom MQTT configuration. All required fields must be provided.
      tags:
        - Custom MQTT
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CustomMqttConfiguration'
      responses:
        '200':
          description: Configuration updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Invalid configuration data
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    patch:
      summary: Update custom MQTT configuration (partial update)
      description: Update specific fields in the custom MQTT configuration. Only provided fields will be updated.
      tags:
        - Custom MQTT
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                enabled:
                  type: boolean
                  description: Whether custom MQTT is enabled
                server:
                  type: string
                  description: MQTT broker server address or hostname
                  maxLength: 64
                port:
                  type: integer
                  description: MQTT broker port number
                  minimum: 1
                  maximum: 65535
                clientid:
                  type: string
                  description: Unique MQTT client identifier
                  maxLength: 32
                topic:
                  type: string
                  description: MQTT topic for publishing meter data
                  maxLength: 64
                frequency:
                  type: integer
                  description: Publishing frequency in seconds
                  minimum: 1
                  maximum: 3600
                useCredentials:
                  type: boolean
                  description: Whether to use username/password authentication
                username:
                  type: string
                  description: MQTT username
                  maxLength: 32
                password:
                  type: string
                  description: MQTT password
                  maxLength: 64
              minProperties: 1
            examples:
              enable_only:
                summary: Enable MQTT only
                value:
                  enabled: true
              server_config:
                summary: Update server settings
                value:
                  server: "new-mqtt-broker.com"
                  port: 1883
              credentials:
                summary: Update credentials
                value:
                  useCredentials: true
                  username: "new_user"
                  password: "new_password"
      responses:
        '200':
          description: Configuration updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Invalid configuration data
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/v1/custom-mqtt/status:
    get:
      summary: Get custom MQTT status
      description: Get the current status and runtime information for custom MQTT
      tags:
        - Custom MQTT
      responses:
        '200':
          description: Custom MQTT status and runtime information
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GenericStatus'

  /api/v1/custom-mqtt/config/reset:
    post:
      summary: Reset custom MQTT configuration
      description: Reset the custom MQTT configuration to default values
      tags:
        - Custom MQTT
      responses:
        '200':
          description: Custom MQTT configuration reset successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /api/v1/mqtt/cloud-services:
    get:
      summary: Get cloud services status
      description: Get the current status of cloud services (enabled/disabled)
      tags:
        - MQTT
      responses:
        '200':
          description: Current cloud services status
          content:
            application/json:
              schema:
                type: object
                required:
                  - enabled
                properties:
                  enabled:
                    type: boolean
                    description: Whether cloud services are enabled
                    example: true
        '500':
          $ref: '#/components/responses/InternalServerError'
    put:
      summary: Set cloud services status
      description: Enable or disable cloud services
      tags:
        - MQTT
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - enabled
              properties:
                enabled:
                  type: boolean
                  description: Whether to enable or disable cloud services
                  example: true
      responses:
        '200':
          description: Cloud services status updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '500':
          $ref: '#/components/responses/InternalServerError'

  # InfluxDB endpoints
  /api/v1/influxdb/config:
    get:
      summary: Get InfluxDB configuration
      description: Retrieve the current InfluxDB configuration settings
      tags:
        - InfluxDB
      responses:
        '200':
          description: Current InfluxDB configuration
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InfluxDbConfiguration'
    put:
      summary: Update InfluxDB configuration (full update)
      description: Update the complete InfluxDB configuration. All required fields must be provided.
      tags:
        - InfluxDB
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/InfluxDbConfiguration'
      responses:
        '200':
          description: Configuration updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Invalid configuration data
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    patch:
      summary: Update InfluxDB configuration (partial update)
      description: Update specific fields in the InfluxDB configuration. Only provided fields will be updated.
      tags:
        - InfluxDB
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                enabled:
                  type: boolean
                  description: Whether InfluxDB integration is enabled
                server:
                  type: string
                  description: InfluxDB server address or hostname
                  maxLength: 64
                port:
                  type: integer
                  description: InfluxDB server port number
                  minimum: 1
                  maximum: 65535
                version:
                  type: integer
                  description: InfluxDB version (1 or 2)
                  enum: [1, 2]
                database:
                  type: string
                  description: Database name (InfluxDB v1)
                  maxLength: 64
                username:
                  type: string
                  description: Username for authentication (InfluxDB v1)
                  maxLength: 32
                password:
                  type: string
                  description: Password for authentication (InfluxDB v1)
                  maxLength: 64
                organization:
                  type: string
                  description: Organization name (InfluxDB v2)
                  maxLength: 64
                bucket:
                  type: string
                  description: Bucket name (InfluxDB v2)
                  maxLength: 64
                token:
                  type: string
                  description: Authentication token (InfluxDB v2)
                  maxLength: 64
                measurement:
                  type: string
                  description: Measurement name for data points
                  maxLength: 64
                frequency:
                  type: integer
                  description: Data sending frequency in seconds
                  minimum: 1
                  maximum: 3600
                useSsl:
                  type: boolean
                  description: Whether to use HTTPS for connections
              minProperties: 1
            examples:
              enable_only:
                summary: Enable InfluxDB only
                value:
                  enabled: true
              server_config:
                summary: Update server settings
                value:
                  server: "new-influxdb-server.com"
                  port: 8086
              v2_config:
                summary: Update InfluxDB v2 settings
                value:
                  version: 2
                  organization: "my-new-org"
                  bucket: "energy-data"
                  token: "new-auth-token"
      responses:
        '200':
          description: Configuration updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Invalid configuration data
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/v1/influxdb/status:
    get:
      summary: Get InfluxDB status
      description: Get the current status and runtime information for InfluxDB
      tags:
        - InfluxDB
      responses:
        '200':
          description: InfluxDB status and runtime information
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GenericStatus'

  /api/v1/influxdb/config/reset:
    post:
      summary: Reset InfluxDB configuration
      description: Reset the InfluxDB configuration to default values
      tags:
        - InfluxDB
      responses:
        '200':
          description: InfluxDB configuration reset successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '500':
          $ref: '#/components/responses/InternalServerError'

  # LED Management endpoints
  /api/v1/led/brightness:
    get:
      summary: Get LED brightness
      description: Get the current LED brightness level
      tags:
        - LED
      responses:
        '200':
          description: Current LED brightness level
          content:
            application/json:
              schema:
                type: object
                properties:
                  brightness:
                    type: integer
                    description: Current brightness level (0-100)
                    minimum: 0
                    maximum: 100
                    example: 75
                  max_brightness:
                    type: integer
                    description: Maximum possible brightness value
                    example: 100
    put:
      summary: Set LED brightness
      description: Update the LED brightness level
      tags:
        - LED
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - brightness
              properties:
                brightness:
                  type: integer
                  description: Brightness level to set (0-100)
                  minimum: 0
                  maximum: 100
                  example: 75
      responses:
        '200':
          $ref: '#/components/responses/Success'
        '400':
          $ref: '#/components/responses/BadRequest'

  # ADE7953 Energy Meter endpoints
  /api/v1/ade7953/config:
    get:
      summary: Get ADE7953 configuration
      description: Retrieve the current ADE7953 energy meter configuration settings
      tags:
        - ADE7953
      responses:
        '200':
          description: Current ADE7953 configuration
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Ade7953Configuration'
    put:
      summary: Update ADE7953 configuration (full update)
      description: Update the complete ADE7953 configuration. All required fields must be provided.
      tags:
        - ADE7953
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Ade7953Configuration'
      responses:
        '200':
          description: Configuration updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Invalid configuration data
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    patch:
      summary: Update ADE7953 configuration (partial update)
      description: Update specific fields in the ADE7953 configuration. Only provided fields will be updated.
      tags:
        - ADE7953
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                aVGain:
                  type: integer
                  description: Voltage gain calibration
                aIGain:
                  type: integer
                  description: Current gain calibration for channel A
                bIGain:
                  type: integer
                  description: Current gain calibration for channel B
              minProperties: 1
            examples:
              gain_only:
                summary: Update gain settings only
                value:
                  aVGain: 4194304
                  aIGain: 4194304
              calibration:
                summary: Update calibration values
                value:
                  phCalA: 10
                  phCalB: 10
      responses:
        '200':
          description: Configuration updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Invalid configuration data
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/v1/ade7953/config/reset:
    post:
      summary: Reset ADE7953 configuration
      description: Reset the ADE7953 configuration to default values
      tags:
        - ADE7953
      responses:
        '200':
          description: ADE7953 configuration reset successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'

  /api/v1/ade7953/sample-time:
    get:
      summary: Get ADE7953 sample time
      description: Get the current sampling time interval in milliseconds
      tags:
        - ADE7953
      responses:
        '200':
          description: Current sample time
          content:
            application/json:
              schema:
                type: object
                properties:
                  sampleTime:
                    type: integer
                    description: Sample time in milliseconds
                    example: 200
    put:
      summary: Set ADE7953 sample time
      description: Update the sampling time interval in milliseconds
      tags:
        - ADE7953
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - sampleTime
              properties:
                sampleTime:
                  type: integer
                  description: Sample time in milliseconds
                  minimum: 200
                  maximum: 20000
                  example: 1000
      responses:
        '200':
          description: Sample time updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          $ref: '#/components/responses/BadRequest'

  /api/v1/ade7953/channel:
    get:
      summary: Get channel configuration
      description: Get the configuration for a specific channel or all channels. If index parameter is provided, returns single channel data. If omitted, returns all channels data.
      tags:
        - ADE7953
      parameters:
        - name: index
          in: query
          required: false
          description: Channel index (0-16). If omitted, returns all channels.
          schema:
            type: integer
            minimum: 0
            maximum: 16
          example: 0
      responses:
        '200':
          description: Channel configuration(s)
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/ChannelData'
                    description: Single channel response (when index provided)
                  - type: object
                    description: All channels response (when index omitted)
                    properties:
                      channels:
                        type: array
                        items:
                          $ref: '#/components/schemas/ChannelData'
              examples:
                single_channel:
                  summary: Single channel response
                  value:
                    index: 0
                    active: true
                    reverse: false
                    label: "Main Circuit"
                    phase: "A"
                    ctSpecification:
                      currentRating: 100.0
                      voltageOutput: 1.0
                all_channels:
                  summary: All channels response
                  value:
                    channels:
                      - index: 0
                        active: true
                        reverse: false
                        label: "Main Circuit"
                        phase: "A"
                        ctSpecification:
                          currentRating: 100.0
                          voltageOutput: 1.0
                      - index: 1
                        active: false
                        reverse: false
                        label: "Kitchen"
                        phase: "A"
                        ctSpecification:
                          currentRating: 50.0
                          voltageOutput: 1.0
        '400':
          $ref: '#/components/responses/BadRequest'
    put:
      summary: Update channel configuration (full update)
      description: Update the complete configuration for a specific channel. All fields must be provided including the channel index.
      tags:
        - ADE7953
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ChannelData'
      responses:
        '200':
          description: Channel configuration updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
    patch:
      summary: Update channel configuration (partial update)
      description: Update specific fields in a channel configuration. Channel index must be provided.
      tags:
        - ADE7953
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - index
              properties:
                index:
                  type: integer
                  minimum: 0
                  maximum: 16
                  description: Channel index
                active:
                  type: boolean
                  description: Whether the channel is active
                reverse:
                  type: boolean
                  description: Whether to reverse the current direction
                label:
                  type: string
                  maxLength: 32
                  description: Human-readable channel label
                phase:
                  $ref: '#/components/schemas/Phase'
                ctSpecification:
                  $ref: '#/components/schemas/CtSpecification'
                groupLabel:
                  type: string
                  maxLength: 64
                  description: Human-readable label for the group. Channels with the same groupLabel are aggregated together.
                role:
                  type: string
                  enum: [load, grid, pv, battery, inverter]
                  description: "Channel role: load, grid (+ import/- export), pv (+ generation), battery (+ discharge/- charge), inverter (PV + battery DC-coupled)"
              minProperties: 2
            examples:
              enable_channel:
                summary: Enable channel
                value:
                  index: 1
                  active: true
                  label: "Kitchen Circuit"
              ct_config:
                summary: Update CT specifications
                value:
                  index: 2
                  ctSpecification:
                    currentRating: 50.0
                    voltageOutput: 1.0
              three_phase_group:
                summary: Configure three-phase grouping
                value:
                  index: 1
                  groupLabel: "Main Three-Phase"
              grid_channel:
                summary: Mark as grid channel
                value:
                  index: 0
                  role: "grid"
                  label: "Grid"
              solar_production:
                summary: Mark as solar production channel
                value:
                  index: 5
                  role: "pv"
                  label: "Solar PV"
              battery_channel:
                summary: Mark as battery channel
                value:
                  index: 6
                  role: "battery"
                  label: "Battery System"
      responses:
        '200':
          description: Channel configuration updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          $ref: '#/components/responses/BadRequest'

  /api/v1/ade7953/channels:
    put:
      summary: Update all channels configuration (bulk update)
      description: Update the configuration for multiple channels at once. This is useful for backup/restore operations. All channels in the array will be updated with their complete configuration.
      tags:
        - ADE7953
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: array
              items:
                $ref: '#/components/schemas/ChannelData'
              minItems: 1
              maxItems: 16
              uniqueItems: true
            example:
              - index: 0
                active: true
                reverse: false
                label: "General"
                phase: 1
                ctSpecification:
                  currentRating: 50.0
                  voltageOutput: 0.333
                  scalingFraction: 0.0
              - index: 1
                active: true
                reverse: false
                label: "Luce prese"
                phase: 1
                ctSpecification:
                  currentRating: 30.0
                  voltageOutput: 0.333
                  scalingFraction: 0.0
              - index: 6
                active: true
                reverse: true
                label: "Boiler"
                phase: 1
                ctSpecification:
                  currentRating: 30.0
                  voltageOutput: 0.333
                  scalingFraction: 0.0
      responses:
        '200':
          description: Channels configuration updated successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                    example: "Channels updated successfully"
        '400':
          description: Bad request - invalid channel configuration or duplicate indices
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                invalid_array:
                  summary: Invalid request format
                  value:
                    success: false
                    error: "Request body must be an array of channel configurations"
                duplicate_index:
                  summary: Duplicate channel indices
                  value:
                    success: false
                    error: "Duplicate channel index: 1"
                invalid_channel:
                  summary: Invalid channel configuration
                  value:
                    success: false
                    error: "Invalid channel configuration for channel 2"
                too_many_channels:
                  summary: Too many channels
                  value:
                    success: false
                    error: "Too many channels in array (maximum: 16)"

  /api/v1/ade7953/channel/reset:
    post:
      summary: Reset channel configuration
      description: Reset a specific channel configuration to default values
      tags:
        - ADE7953
      parameters:
        - name: index
          in: query
          required: true
          description: Channel index (0-16)
          schema:
            type: integer
            minimum: 0
            maximum: 16
          example: 1
      responses:
        '200':
          description: Channel configuration reset successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          $ref: '#/components/responses/BadRequest'

  /api/v1/ade7953/register:
    get:
      summary: Read ADE7953 register
      description: Read a value from a specific ADE7953 register
      tags:
        - ADE7953
      parameters:
        - name: address
          in: query
          required: true
          description: Register address (hexadecimal or decimal)
          schema:
            type: integer
          example: 0x031E
        - name: bits
          in: query
          required: true
          description: Number of bits to read (8, 16, 24, or 32)
          schema:
            type: integer
            enum: [8, 16, 24, 32]
          example: 32
        - name: signed
          in: query
          required: false
          description: Whether the data is signed
          schema:
            type: boolean
            default: false
          example: false
      responses:
        '200':
          description: Register value read successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  address:
                    type: integer
                    description: Register address
                  bits:
                    type: integer
                    description: Number of bits
                  signed:
                    type: boolean
                    description: Whether the data is signed
                  value:
                    type: integer
                    description: Register value
        '400':
          $ref: '#/components/responses/BadRequest'
    put:
      summary: Write ADE7953 register
      description: Write a value to a specific ADE7953 register
      tags:
        - ADE7953
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - address
                - bits
                - value
              properties:
                address:
                  type: integer
                  description: Register address (hexadecimal or decimal)
                  example: 0x031E
                bits:
                  type: integer
                  enum: [8, 16, 24, 32]
                  description: Number of bits to write
                  example: 32
                value:
                  type: integer
                  description: Value to write to the register
                  example: 4194304
      responses:
        '200':
          description: Register written successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          $ref: '#/components/responses/BadRequest'

  /api/v1/ade7953/meter-values:
    get:
      summary: Get meter values
      description: Get real-time meter values for all active channels or a specific channel
      tags:
        - ADE7953
      parameters:
        - name: index
          in: query
          required: false
          description: Channel index (0-16). If omitted, returns all active channels.
          schema:
            type: integer
            minimum: 0
            maximum: 16
          example: 0
      responses:
        '200':
          description: Meter values for all channels or the specified channel
          content:
            application/json:
              schema:
                oneOf:
                  - type: array
                    description: All channels response (when index omitted)
                    items:
                      type: object
                      properties:
                        index:
                          type: integer
                          description: Channel index
                        label:
                          type: string
                          description: Channel label
                        phase:
                          type: integer
                          description: Channel phase (1, 2, 3, or 4 for 240V split-phase)
                        groupLabel:
                          type: string
                          description: Channel group label (if configured)
                        role:
                          type: string
                          description: Channel role (load, grid, pv, battery, inverter)
                        data:
                          $ref: '#/components/schemas/MeterValues'
                  - $ref: '#/components/schemas/MeterValues'
                    description: Single channel response (when index provided)
              examples:
                all_channels:
                  summary: All channels response
                  value:
                    - index: 0
                      label: "Main Circuit"
                      phase: 1
                      groupLabel: "Group 0"
                      role: "grid"
                      data:
                        voltage: 230.2
                        current: 5.45
                        activePower: 1254.6
                        reactivePower: 45.2
                        apparentPower: 1256.4
                        powerFactor: 0.998
                        activeEnergyImported: 12345.6
                        activeEnergyExported: 0.0
                        reactiveEnergyImported: 234.5
                        reactiveEnergyExported: 0.0
                        apparentEnergy: 12346.8
                single_channel:
                  summary: Single channel response
                  value:
                    voltage: 230.2
                    current: 5.45
                    activePower: 1254.6
                    reactivePower: 45.2
                    apparentPower: 1256.4
                    powerFactor: 0.998
                    activeEnergyImported: 12345.6
                    activeEnergyExported: 0.0
                    reactiveEnergyImported: 234.5
                    reactiveEnergyExported: 0.0
                    apparentEnergy: 12346.8
        '404':
          $ref: '#/components/responses/NotFound'

  /api/v1/ade7953/grid-frequency:
    get:
      summary: Get grid frequency
      description: Get the current AC grid frequency in Hz
      tags:
        - ADE7953
      responses:
        '200':
          description: Current grid frequency
          content:
            application/json:
              schema:
                type: object
                properties:
                  gridFrequency:
                    type: number
                    format: float
                    description: Grid frequency in Hz
                    example: 50.02

  /api/v1/ade7953/energy/reset:
    post:
      summary: Reset energy values
      description: |
        Reset accumulated energy values to zero. When the optional `index` query
        parameter is provided, only that channel is reset; otherwise all channels
        are reset.
      tags:
        - ADE7953
      parameters:
        - name: index
          in: query
          required: false
          description: Channel index to reset. If omitted, all channels are reset.
          schema:
            type: integer
            minimum: 0
            maximum: 16
          example: 0
      responses:
        '200':
          description: Energy values reset successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Invalid channel index
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/v1/ade7953/energy:
    put:
      summary: Set energy values
      description: Set accumulated energy values for a specific channel
      tags:
        - ADE7953
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - channel
              properties:
                channel:
                  type: integer
                  minimum: 0
                  maximum: 16
                  description: Channel index
                  example: 0
                activeEnergyImported:
                  type: number
                  format: float
                  description: Active energy imported in Wh
                  example: 1250.5
                activeEnergyExported:
                  type: number
                  format: float
                  description: Active energy exported in Wh
                  example: 0.0
                reactiveEnergyImported:
                  type: number
                  format: float
                  description: Reactive energy imported in VARh
                  example: 45.2
                reactiveEnergyExported:
                  type: number
                  format: float
                  description: Reactive energy exported in VARh
                  example: 0.0
                apparentEnergy:
                  type: number
                  format: float
                  description: Apparent energy in VAh
                  example: 1251.3
      responses:
        '200':
          description: Energy values updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          $ref: '#/components/responses/BadRequest'

  /api/v1/ade7953/waveform/arm:
    post:
      summary: Arm waveform capture
      description: |
        Arms the waveform capture system to capture voltage and current samples for a specific channel.
        The capture is non-blocking and will start when the specified channel becomes active in the multiplexer cycle.
        Wait time varies from 200ms to 3+ seconds depending on channel position in the cycle.
      tags:
        - ADE7953
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - channelIndex
              properties:
                channelIndex:
                  type: integer
                  minimum: 0
                  maximum: 16
                  description: Channel index to capture waveform from
                  example: 0
      responses:
        '200':
          description: Waveform capture armed successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: "armed"
                  channelIndex:
                    type: integer
                    example: 0
        '400':
          $ref: '#/components/responses/BadRequest'
        '409':
          description: Capture already in progress
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                    example: "Capture already in progress"

  /api/v1/ade7953/waveform/status:
    get:
      summary: Get waveform capture status
      description: |
        Returns the current state of the waveform capture system.
        Use this endpoint to poll for completion after arming a capture.
      tags:
        - ADE7953
      responses:
        '200':
          description: Current capture status
          content:
            application/json:
              schema:
                type: object
                properties:
                  state:
                    type: string
                    enum: [idle, armed, capturing, complete, error]
                    description: |
                      - idle: No capture in progress
                      - armed: Waiting for channel to become active
                      - capturing: Currently sampling waveform
                      - complete: Capture finished, data ready
                      - error: Capture failed
                    example: "complete"

  /api/v1/ade7953/waveform/data:
    get:
      summary: Get captured waveform data
      description: |
        Retrieves the captured waveform data including voltage, current, and microsecond timestamps.
        Returns 404 if no complete capture is available.
        Successfully fetching data resets the capture state to idle.
        
        **Phase Information:**
        - `voltagePhase`: Phase of the voltage reference (always from channel 0)
        - `currentPhase`: Phase of the current being measured
        - `phaseShiftDegrees`: Calculated phase shift to align voltage with current
          - Positive values: voltage leads current (shift forward)
          - Negative values: voltage lags current (shift backward)
          - 0°: Both on same phase (no shift needed)
        
        Phase angles follow standard ABC sequence:
        - Phase 1 (A): 0°
        - Phase 2 (B): -120° (lags A by 120°)
        - Phase 3 (C): +120° (leads A by 120°, or lags B by 120°)
      tags:
        - ADE7953
      responses:
        '200':
          description: Waveform data retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  channelIndex:
                    type: integer
                    description: Channel that was captured
                    example: 0
                  state:
                    type: string
                    example: "complete"
                  captureStartUnixMillis:
                    type: integer
                    format: int64
                    description: Unix timestamp in milliseconds when capture started
                    example: 1729612345678
                  captureStartMicros:
                    type: integer
                    format: int64
                    description: Microsecond timestamp at capture start (for relative timing)
                    example: 1234567890
                  sampleCount:
                    type: integer
                    description: Number of samples captured
                    example: 1200
                  voltagePhase:
                    type: integer
                    description: Phase number of voltage reference (1=A, 2=B, 3=C; 4 not applicable for waveform capture)
                    example: 3
                    enum: [1, 2, 3]
                  currentPhase:
                    type: integer
                    description: Phase number of current measurement (1=A, 2=B, 3=C; 4 not applicable for waveform capture)
                    example: 1
                    enum: [1, 2, 3]
                  phaseShiftDegrees:
                    type: number
                    format: float
                    description: Phase shift in degrees to align voltage with current (negative=lag, positive=lead)
                    example: -120.0
                  voltage:
                    type: array
                    description: Voltage samples in volts
                    items:
                      type: number
                      format: float
                    example: [230.5, 231.2, 230.8]
                  current:
                    type: array
                    description: Current samples in amps
                    items:
                      type: number
                      format: float
                    example: [5.2, 5.3, 5.1]
                  microsDelta:
                    type: array
                    description: Cumulative microseconds from capture start for each sample
                    items:
                      type: integer
                      format: int64
                    example: [20, 53, 87, 120]
        '204':
          description: No complete waveform data available
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                    example: "No waveform data available"

tags:
  - name: System
    description: System management and information endpoints
  - name: Authentication
    description: Authentication and security endpoints
  - name: OTA Updates
    description: Over-the-air firmware update endpoints
  - name: Crash Monitor
    description: Crash analysis and core dump management endpoints
  - name: Network
    description: Network and WiFi management endpoints
  - name: Logging
    description: Log management and configuration endpoints
  - name: ADE7953
    description: ADE7953 energy meter configuration, measurement, and control endpoints
  - name: Custom MQTT
    description: Custom MQTT broker configuration and status endpoints
  - name: InfluxDB
    description: InfluxDB time-series database configuration and status endpoints
  - name: LED
    description: LED brightness control and status endpoints
  - name: Files
    description: LittleFS file system operations and management endpoints
  - name: Backup
    description: Configuration backup and export endpoints
  - name: Restore
    description: Configuration restore and import endpoints
    
components:
  # Common response patterns
  responses:
    Success:
      description: Operation completed successfully
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/SuccessResponse'
    
    BadRequest:
      description: Bad request - invalid input parameters
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            invalid_params:
              summary: Invalid parameters
              value:
                success: false
                error: "Invalid input parameters"
            validation_failed:
              summary: Validation failed
              value:
                success: false
                error: "Request validation failed"
    
    Unauthorized:
      description: Authentication required or invalid credentials
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            error: "Authentication required"
    
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            error: "Resource not found"
    
    MethodNotAllowed:
      description: HTTP method not allowed for this endpoint
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            error: "Method not allowed"
    
    InternalServerError:
      description: Internal server error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            error: "Internal server error occurred"

  schemas:
    SuccessResponse:
      type: object
      required:
        - success
        - message
      properties:
        success:
          type: boolean
          example: true
        message:
          type: string
          example: "Operation completed successfully"
    
    ErrorResponse:
      type: object
      required:
        - success
        - error
      properties:
        success:
          type: boolean
          example: false
        error:
          type: string
          example: "Error message describing what went wrong"
    
    LogLevel:
      type: string
      enum: [VERBOSE, DEBUG, INFO, WARNING, ERROR, FATAL]
      description: Log level enumeration
    
    CustomMqttConfiguration:
      type: object
      required:
        - enabled
        - server
        - port
        - clientid
        - topic
        - frequency
        - useCredentials
      properties:
        enabled:
          type: boolean
          description: Whether custom MQTT is enabled
          example: false
        server:
          type: string
          description: MQTT broker server address or hostname
          example: "mqtt.test.mosquitto.org"
          maxLength: 64
        port:
          type: integer
          description: MQTT broker port number
          example: 1883
          minimum: 1
          maximum: 65535
        clientid:
          type: string
          description: Unique MQTT client identifier
          example: "energyme-home"
          maxLength: 32
        topic:
          type: string
          description: MQTT topic for publishing meter data
          example: "energy/meter"
          maxLength: 64
        frequency:
          type: integer
          description: Publishing frequency in seconds
          example: 10
          minimum: 1
          maximum: 3600
        useCredentials:
          type: boolean
          description: Whether to use username/password authentication
          example: false
        username:
          type: string
          description: MQTT username (required if useCredentials is true)
          example: "mqtt_user"
          maxLength: 32
        password:
          type: string
          description: MQTT password (required if useCredentials is true)
          example: "mqtt_password"
          maxLength: 64

    NetworkConfiguration:
      type: object
      required:
        - useStaticIp
        - ip
        - gateway
        - subnet
        - dns1
        - dns2
        - fallbackToDhcp
      properties:
        useStaticIp:
          type: boolean
          description: Use a static IP address instead of DHCP
          example: false
        ip:
          type: string
          description: Static IPv4 address (required when useStaticIp is true)
          example: "192.168.1.50"
          maxLength: 15
        gateway:
          type: string
          description: Default gateway IPv4 address (required when useStaticIp is true)
          example: "192.168.1.1"
          maxLength: 15
        subnet:
          type: string
          description: Subnet mask (required when useStaticIp is true)
          example: "255.255.255.0"
          maxLength: 15
        dns1:
          type: string
          description: Primary DNS server (optional). When empty, defaults to the gateway address.
          example: "8.8.8.8"
          maxLength: 15
        dns2:
          type: string
          description: Secondary DNS server (optional, empty string to omit)
          example: "1.1.1.1"
          maxLength: 15
        fallbackToDhcp:
          type: boolean
          description: |
            When static IP is enabled, automatically revert to DHCP at runtime if the static IP has
            no working internet connectivity (e.g. wrong subnet/gateway). The static config is kept
            in NVS and retried on the next boot. Independently, after several consecutive failed
            static-IP boots the device ignores the static IP at startup and comes up on DHCP, so a
            bad static IP can never permanently lock you out. Recommended to keep enabled.
          example: true

    InfluxDbConfiguration:
      type: object
      required:
        - enabled
        - server
        - port
        - version
        - database
        - username
        - password
        - organization
        - bucket
        - token
        - measurement
        - frequency
        - useSsl
      properties:
        enabled:
          type: boolean
          description: Whether InfluxDB integration is enabled
          example: false
        server:
          type: string
          description: InfluxDB server address or hostname
          example: "localhost"
          maxLength: 64
        port:
          type: integer
          description: InfluxDB server port number
          example: 8086
          minimum: 1
          maximum: 65535
        version:
          type: integer
          description: InfluxDB version (1 or 2)
          example: 2
          enum: [1, 2]
        database:
          type: string
          description: Database name (InfluxDB v1)
          example: "energyme-home"
          maxLength: 64
        username:
          type: string
          description: Username for authentication (InfluxDB v1)
          example: "user"
          maxLength: 32
        password:
          type: string
          description: Password for authentication (InfluxDB v1)
          example: "password"
          maxLength: 64
        organization:
          type: string
          description: Organization name (InfluxDB v2)
          example: "my-org"
          maxLength: 64
        bucket:
          type: string
          description: Bucket name (InfluxDB v2)
          example: "energyme-home"
          maxLength: 64
        token:
          type: string
          description: Authentication token (InfluxDB v2)
          example: "my-token"
          maxLength: 64
        measurement:
          type: string
          description: Measurement name for data points
          example: "meter"
          maxLength: 64
        frequency:
          type: integer
          description: Data sending frequency in seconds
          example: 15
          minimum: 1
          maximum: 3600
        useSsl:
          type: boolean
          description: Whether to use HTTPS for connections
          example: false

    GenericStatus:
      type: object
      required:
        - status
        - statusTimestamp
      properties:
        status:
          type: string
          description: Status of the last operation
          example: "Data sent successfully"
        statusTimestamp:
          type: string
          format: date-time
          description: Timestamp of the last operation
          example: "2025-07-31T12:34:56Z"

    # ADE7953-specific schemas
    Ade7953Configuration:
      type: object
      required:
        - aVGain
        - aIGain
        - bIGain
        - aIRmsOs
        - bIRmsOs
        - aWGain
        - bWGain
        - aWattOs
        - bWattOs
        - aVarGain
        - bVarGain
        - aVarOs
        - bVarOs
        - aVaGain
        - bVaGain
        - aVaOs
        - bVaOs
        - phCalA
        - phCalB
      properties:
        aVGain:
          type: integer
          description: Voltage gain calibration for channel A
          example: 4194304
        aIGain:
          type: integer
          description: Current gain calibration for channel A
          example: 4194304
        bIGain:
          type: integer
          description: Current gain calibration for channel B
          example: 4194304
        aIRmsOs:
          type: integer
          description: RMS current offset for channel A
          example: 0
        bIRmsOs:
          type: integer
          description: RMS current offset for channel B
          example: 0
        aWGain:
          type: integer
          description: Active power gain for channel A
          example: 4194304
        bWGain:
          type: integer
          description: Active power gain for channel B
          example: 4194304
        aWattOs:
          type: integer
          description: Active power offset for channel A
          example: 0
        bWattOs:
          type: integer
          description: Active power offset for channel B
          example: 0
        aVarGain:
          type: integer
          description: Reactive power gain for channel A
          example: 4194304
        bVarGain:
          type: integer
          description: Reactive power gain for channel B
          example: 4194304
        aVarOs:
          type: integer
          description: Reactive power offset for channel A
          example: 0
        bVarOs:
          type: integer
          description: Reactive power offset for channel B
          example: 0
        aVaGain:
          type: integer
          description: Apparent power gain for channel A
          example: 4194304
        bVaGain:
          type: integer
          description: Apparent power gain for channel B
          example: 4194304
        aVaOs:
          type: integer
          description: Apparent power offset for channel A
          example: 0
        bVaOs:
          type: integer
          description: Apparent power offset for channel B
          example: 0
        phCalA:
          type: integer
          description: Phase calibration for channel A
          example: 0
        phCalB:
          type: integer
          description: Phase calibration for channel B
          example: 0

    Phase:
      type: integer
      enum: [1, 2, 3, 4]
      description: Electrical phase assignment (1=Phase A, 2=Phase B, 3=Phase C, 4=240V Split Phase)
      example: 1

    CtSpecification:
      type: object
      required:
        - currentRating
        - voltageOutput
        - scalingFraction
      properties:
        currentRating:
          type: number
          format: float
          description: CT current rating in Amperes
          example: 30.0
          minimum: 1.0
          maximum: 300.0
        voltageOutput:
          type: number
          format: float
          description: CT voltage output in Volts
          example: 0.333
          minimum: 0.100
          maximum: 1.0
        scalingFraction:
          type: number
          format: float
          description: Scaling adjustment fraction (-0.5 to +0.5 for ±50% adjustment)
          example: 0.0
          minimum: -0.5
          maximum: 0.5

    ChannelData:
      type: object
      required:
        - index
        - active
        - reverse
        - label
        - phase
        - ctSpecification
      properties:
        index:
          type: integer
          minimum: 0
          maximum: 16
          description: Channel index (0-16)
          example: 1
        active:
          type: boolean
          description: Whether the channel is active and monitoring
          example: true
        reverse:
          type: boolean
          description: Whether to reverse the current direction measurement
          example: false
        label:
          type: string
          maxLength: 64
          description: Human-readable channel label
          example: "Kitchen Circuit"
        phase:
          $ref: '#/components/schemas/Phase'
        ctSpecification:
          $ref: '#/components/schemas/CtSpecification'
        groupLabel:
          type: string
          maxLength: 64
          description: Human-readable label for the group. Channels with the same groupLabel are aggregated together in the UI.
          example: "Main Three-Phase"
        role:
          type: string
          enum: [load, grid, pv, battery, inverter]
          description: "Channel role determining measurement behavior and sign convention"
          example: "load"
          default: "load"

    MeterValues:
      type: object
      required:
        - voltage
        - current
        - activePower
        - reactivePower
        - apparentPower
        - powerFactor
        - activeEnergyImported
        - activeEnergyExported
        - reactiveEnergyImported
        - reactiveEnergyExported
        - apparentEnergy
      properties:
        voltage:
          type: number
          format: float
          description: RMS voltage in Volts
          example: 230.2
        current:
          type: number
          format: float
          description: RMS current in Amperes
          example: 5.45
        activePower:
          type: number
          format: float
          description: Active power in Watts
          example: 1254.6
        reactivePower:
          type: number
          format: float
          description: Reactive power in VAR
          example: 45.2
        apparentPower:
          type: number
          format: float
          description: Apparent power in VA
          example: 1255.4
        powerFactor:
          type: number
          format: float
          description: Power factor (-1 to 1)
          example: 0.998
          minimum: -1.0
          maximum: 1.0
        activeEnergyImported:
          type: number
          format: float
          description: Accumulated active energy imported in Wh
          example: 12345.6
        activeEnergyExported:
          type: number
          format: float
          description: Accumulated active energy exported in Wh
          example: 0.0
        reactiveEnergyImported:
          type: number
          format: float
          description: Accumulated reactive energy imported in VARh
          example: 456.7
        reactiveEnergyExported:
          type: number
          format: float
          description: Accumulated reactive energy exported in VARh
          example: 0.0
        apparentEnergy:
          type: number
          format: float
          description: Accumulated apparent energy in VAh
          example: 12356.8