openapi: 3.1.0
info:
  title: Cozify Han API
  version: '1.0'
  termsOfService: https://en.cozify.fi/pages/privacy-policy
  license:
    name: Apache 2.0
    url: https://www.apache.org/licenses/LICENSE-2.0.html
  description: This is the [OpenAPI specification](./han-1.0.yaml) for the Cozify HAN reader.
  contact:
    email: support@cozify.fi
servers:
  - url: /
    description: Relative to the current HAN reader origin
security:
  - {}
tags:
  - name: System
    description: System status, events, and connectivity
  - name: Configuration
    description: HAN reader configuration and setup
  - name: Network
    description: Network operations and WiFi management
  - name: Meter
    description: Electricity meter data reported by the HAN reader
  - name: History
    description: Historical electricity meter data collected by the HAN reader
paths:
  /auth/token:
    get:
      operationId: AuthToken
      summary: AuthToken
      description: Returns a short-lived Cozify HAN bearer JWT for the built-in reader interface.
      tags:
        - System
      responses:
        '200':
          description: Short-lived scoped bearer token returned.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: false
                properties:
                  type:
                    type: string
                    enum:
                      - HAN_AUTH_TOKEN
                    example: HAN_AUTH_TOKEN
                  tokenType:
                    type: string
                    enum:
                      - Bearer
                    example: Bearer
                  accessToken:
                    type: string
                    description: HS512 JWT signed by the reader. The factory secret is never returned.
                  audience:
                    type: string
                    enum:
                      - cozify-han
                    example: cozify-han
                  scope:
                    type: string
                    enum:
                      - ota:write
                    example: ota:write
                  expiresIn:
                    type: integer
                    minimum: 1
                    description: Token lifetime in seconds.
                    example: 300
                required:
                  - type
                  - tokenType
                  - accessToken
                  - audience
                  - scope
                  - expiresIn
        '400':
          x-summary: Bad request
          description: Received parameters are not valid.
        '503':
          x-summary: Service unavailable
          description: The reader can not issue a token because it is not registered, has no signing key, or has no usable clock.
  /han:
    get:
      operationId: Info
      summary: Info
      description: |
        Returns general information about the HAN reader.
      tags:
        - System
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HanStateMessage'
        '400':
          x-summary: Bad request
          description: Received parameters are not valid.
        '401':
          x-summary: Unauthorized
          description: Invalid access token.
        '403':
          x-summary: Forbidden
          description: Access token does not have permission to access this resource.
        '503':
          x-summary: Service unavailable
          description: HAN reader is unable to process request.
  /ws:
    get:
      operationId: WebSocket
      summary: WebSocket
      description: |
        Establishes a WebSocket connection to the HAN reader.

        This endpoint upgrades the HTTP connection to a WebSocket.
        The WebSocket protocol is used for real-time, bidirectional communication.

        When the connection is established, the server sends an initial snapshot of the
        current HAN reader state so the client can initialize its view without making a
        separate request. After that, the connection remains open and the server streams
        ongoing updates as new HAN data and state changes become available.

        Message payload discriminator values used:
        - `HAN_METER_MESSAGE`
        - `HAN_STATE_MESSAGE`
        - `HAN_CONFIGURATION_MESSAGE`
        - `HAN_OTA_MESSAGE`
      tags:
        - System
      responses:
        '101':
          description: Switching Protocols (WebSocket handshake successful)
        '200':
          description: Successful operation
        '400':
          x-summary: Bad request
          description: Received parameters are not valid.
        '401':
          x-summary: Unauthorized
          description: Invalid access token.
        '403':
          x-summary: Forbidden
          description: Access token does not have permission to access this resource.
        '503':
          x-summary: Service unavailable
          description: HAN reader is unable to process request.
  /sse:
    get:
      operationId: ServerSentEvents
      summary: ServerSentEvents
      description: |
        Opens a stream of server-sent events (SSE) from the HAN reader.
        The response uses the `text/event-stream` content type.

        When the connection is established, the server sends an initial snapshot of the
        current HAN reader state so the client can initialize its view without making a
        separate request. After that, the connection remains open and the server streams
        ongoing updates as new HAN data and state changes become available.

        Payload discriminator values are used in event names to indicate the type:
        - `HAN_METER_MESSAGE`
        - `HAN_STATE_MESSAGE`
        - `HAN_CONFIGURATION_MESSAGE`
        - `HAN_OTA_MESSAGE`
      tags:
        - System
      responses:
        '200':
          description: Successful operation
          content:
            text/event-stream:
              schema:
                type: string
                description: Stream of events in SSE format
        '400':
          x-summary: Bad request
          description: Received parameters are not valid.
        '401':
          x-summary: Unauthorized
          description: Invalid access token.
        '403':
          x-summary: Forbidden
          description: Access token does not have permission to access this resource.
        '503':
          x-summary: Service unavailable
          description: HAN reader is unable to process request.
  /set-up:
    post:
      operationId: Setup
      summary: Setup
      description: |
        Sets up the HAN reader with basic WiFi configuration. Accepts a JSON object with WiFi enablement, SSID, and password. Returns success status and message. Saved network settings are applied live without rebooting.
      tags:
        - Configuration
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                s:
                  type: boolean
                  description: Enable WiFi (must be true)
                ss:
                  type: string
                  minLength: 1
                  maxLength: 32
                  x-maxBytes: 32
                  description: WiFi SSID. The maxLength constraint is a coarse character limit; the device additionally requires at most 32 bytes when encoded as UTF-8 (the 802.11 limit). Control characters are rejected. Accented characters cost more than one byte each, so the effective character count can be lower than 32. Rejected with "set-up:wifi:ssid:invalid" instead of being silently truncated. An empty SSID is rejected here as well, because this call exists to bring WiFi up.
                sp:
                  type: string
                  description: WiFi password
              required:
                - s
                - ss
                - sp
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    description: Whether the setup was successful
                  message:
                    type: string
                    description: Status or error message
                required:
                  - success
                  - message
        '400':
          x-summary: Bad request
          description: Received parameters are not valid.
        '401':
          x-summary: Unauthorized
          description: Invalid access token.
        '403':
          x-summary: Forbidden
          description: Access token does not have permission to access this resource.
        '503':
          x-summary: Service unavailable
          description: HAN reader is unable to process request.
  /configuration:
    get:
      operationId: Configuration
      summary: Configuration
      description: |
        Returns the current HAN reader configuration.
        - Meter settings include the main fuse value, optional meter timestamp timezone, and meter timestamp correction used by the HAN reader.
        - If the meter timezone is an empty string, the HAN reader uses the device timezone.
        - Supported timezone values include built-in Linux timezone IDs from /definitions.
        - Network policy controls whether the reader uses Ethernet, WiFi STA, explicit Ethernet plus STA, or automatic Ethernet-first fallback.
        - Ethernet and WiFi settings include enablement, network mode (dhcp/static), and network details.
        - Passwords and secrets are masked as "***" if configured.
      tags:
        - Configuration
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                type: object
                properties:
                  type:
                    type: string
                    description: Response type identifier
                    enum:
                      - HAN_CONFIGURATION
                  v:
                    type: string
                    description: Firmware version
                  t:
                    type: string
                    description: Device timezone. Built-in Linux timezone IDs from /definitions are accepted.
                  s:
                    type: boolean
                    description: Use spot price
                  p:
                    type: number
                    minimum: 0
                    description: Fixed electricity price in minor currency units per kWh, such as cents/kWh or öre/kWh.
                  pc:
                    type: string
                    description: Price currency.
                    enum:
                      - EUR
                      - SEK
                      - NOK
                      - DKK
                      - GBP
                      - PLN
                      - CZK
                      - HUF
                      - RON
                      - BGN
                      - CHF
                      - ISK
                  pa:
                    type: string
                    description: Spot price bidding area.
                    enum:
                      - FI
                      - EE
                      - SE1
                      - SE2
                      - SE3
                      - SE4
                  np:
                    type: string
                    description: Network policy. Automatic tries Ethernet first and falls back to STA if Ethernet is unavailable. Ethernet plus WiFi keeps both network chips active.
                    enum:
                      - auto
                      - eth
                      - sta
                      - eth_sta
                  up:
                    type: string
                    description: Firmware update policy. Automatic downloads and installs online updates. Download downloads online updates and waits for manual installation. Manual waits for the user before downloading online updates.
                    enum:
                      - automatic
                      - download
                      - manual
                  bhf:
                    type: boolean
                    description: Reboot the device after repeated failed attempts to open a cloud connection.
                  m:
                    type: object
                    description: Meter settings used by the HAN reader
                    properties:
                      f:
                        type: integer
                        minimum: 1
                        maximum: 65535
                        description: Main fuse value
                      t:
                        type: string
                        description: Optional meter timestamp timezone. Empty string means use the device timezone. Built-in Linux timezone IDs from /definitions are accepted.
                      o:
                        type: integer
                        description: Additional meter timestamp correction in seconds.
                    required:
                      - f
                      - o
                  e:
                    type: object
                    description: Ethernet settings
                    properties:
                      e:
                        type: boolean
                        deprecated: true
                        description: Obsolete Ethernet enabled compatibility field, derived from network policy. Use np instead. Legacy true/true updates map to auto; use np=eth_sta to keep both Ethernet and WiFi active.
                      'n':
                        type: object
                        description: Ethernet network settings
                        properties:
                          m:
                            $ref: '#/components/schemas/NetworkMode'
                          i:
                            type: string
                            format: ipv4
                            description: IP address
                          s:
                            type: string
                            format: ipv4
                            description: Netmask
                          g:
                            type: string
                            format: ipv4
                            description: Gateway
                          d1:
                            type: string
                            format: ipv4
                            description: DNS server. Only one DNS server is supported.
                        required:
                          - m
                  w:
                    type: object
                    description: WiFi settings
                    properties:
                      e:
                        type: boolean
                        deprecated: true
                        description: Obsolete WiFi enabled compatibility field, derived from network policy. Use np instead. Legacy true/true updates map to auto; use np=eth_sta to keep both Ethernet and WiFi active.
                      s:
                        type: string
                        description: WiFi SSID (empty if not set)
                      p:
                        type: string
                        description: WiFi password (masked as "***" if configured, empty otherwise)
                      w:
                        type: number
                        minimum: 2
                        maximum: 20
                        multipleOf: 0.5
                        description: WiFi power (in dBm)
                      z:
                        type: integer
                        deprecated: true
                        enum:
                          - 0
                        description: Obsolete WiFi power save compatibility field. Always 0 because modem power save is disabled.
                      b:
                        type: boolean
                        description: WiFi 11b enabled
                      'n':
                        type: object
                        description: WiFi network settings
                        properties:
                          m:
                            $ref: '#/components/schemas/NetworkMode'
                          i:
                            type: string
                            format: ipv4
                            description: IP address
                          s:
                            type: string
                            format: ipv4
                            description: Netmask
                          g:
                            type: string
                            format: ipv4
                            description: Gateway
                          d1:
                            type: string
                            format: ipv4
                            description: DNS server. Only one DNS server is supported.
                        required:
                          - m
                    required:
                      - e
                      - s
                      - p
                      - w
                      - z
                      - b
                      - 'n'
                required:
                  - type
                  - v
                  - t
                  - s
                  - p
                  - pc
                  - np
                  - m
                  - e
                  - w
        '400':
          x-summary: Bad request
          description: Received parameters are not valid.
        '401':
          x-summary: Unauthorized
          description: Invalid access token.
        '403':
          x-summary: Forbidden
          description: Access token does not have permission to access this resource.
        '503':
          x-summary: Service unavailable
          description: HAN reader is unable to process request.
    post:
      operationId: ConfigurationUpdate
      summary: ConfigurationUpdate
      description: |
        Updates the HAN reader configuration. Accepts a JSON object with any subset of the configuration fields as defined in the GET response. Returns success status and message. Some changes may require a HAN reader reboot.
      tags:
        - Configuration
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                t:
                  type: string
                  description: Device timezone. Built-in Linux timezone IDs from /definitions are accepted.
                s:
                  type: boolean
                  description: Use spot price
                p:
                  type: number
                  minimum: 0
                  description: Fixed electricity price in minor currency units per kWh, such as cents/kWh or öre/kWh.
                pc:
                  type: string
                  description: Price currency.
                  enum:
                    - EUR
                    - SEK
                    - NOK
                    - DKK
                    - GBP
                    - PLN
                    - CZK
                    - HUF
                    - RON
                    - BGN
                    - CHF
                    - ISK
                pa:
                  type: string
                  description: Spot price bidding area.
                  enum:
                    - FI
                    - EE
                    - SE1
                    - SE2
                    - SE3
                    - SE4
                np:
                  type: string
                  description: Network policy. Automatic tries Ethernet first and falls back to STA if Ethernet is unavailable. Ethernet plus WiFi keeps both network chips active.
                  enum:
                    - auto
                    - eth
                    - sta
                    - eth_sta
                up:
                  type: string
                  description: Firmware update policy. Automatic downloads and installs online updates. Download downloads online updates and waits for manual installation. Manual waits for the user before downloading online updates.
                  enum:
                    - automatic
                    - download
                    - manual
                bhf:
                  type: boolean
                  description: Reboot the device after repeated failed attempts to open a cloud connection.
                m:
                  type: object
                  description: Meter settings used by the HAN reader
                  properties:
                    f:
                      type: integer
                      minimum: 1
                      maximum: 65535
                      description: Main fuse value
                    t:
                      type: string
                      description: Optional meter timestamp timezone. Empty string means use the device timezone. Built-in Linux timezone IDs from /definitions are accepted.
                    o:
                      type: integer
                      description: Additional meter timestamp correction in seconds.
                e:
                  type: object
                  description: Ethernet settings
                  properties:
                    e:
                      type: boolean
                      deprecated: true
                      description: Obsolete Ethernet enabled compatibility field. Accepted only when np is omitted; stored as network policy. Use np instead. Legacy true/true updates map to auto; use np=eth_sta to keep both Ethernet and WiFi active.
                    'n':
                      type: object
                      description: Ethernet network settings
                      properties:
                        m:
                          type: string
                          description: Network mode (dhcp/static)
                          enum:
                            - dhcp
                            - static
                        i:
                          type: string
                          format: ipv4
                          description: IP address
                        s:
                          type: string
                          format: ipv4
                          description: Netmask
                        g:
                          type: string
                          format: ipv4
                          description: Gateway
                        d1:
                          type: string
                          format: ipv4
                          description: DNS server. Only one DNS server is supported.
                w:
                  type: object
                  description: WiFi settings
                  properties:
                    e:
                      type: boolean
                      deprecated: true
                      description: Obsolete WiFi enabled compatibility field. Accepted only when np is omitted; stored as network policy. Use np instead. Legacy true/true updates map to auto; use np=eth_sta to keep both Ethernet and WiFi active.
                    s:
                      type: string
                      maxLength: 32
                      x-maxBytes: 32
                      description: WiFi SSID. The maxLength constraint is a coarse character limit; the device additionally requires at most 32 bytes when encoded as UTF-8 (the 802.11 limit). Control characters are rejected. Accented characters cost more than one byte each, so the effective character count can be lower than 32. Rejected with "settings:wifi:ssid:invalid" instead of being silently truncated. An empty string clears the stored network.
                    p:
                      type: string
                      description: WiFi password ("***" to keep unchanged)
                    w:
                      type: number
                      minimum: 2
                      maximum: 20
                      multipleOf: 0.5
                      description: WiFi power (in dBm)
                    z:
                      type: integer
                      deprecated: true
                      enum:
                        - 0
                      description: Obsolete WiFi power save compatibility field. Accepted for compatibility but ignored; modem power save is always disabled.
                    b:
                      type: boolean
                      description: WiFi 11b enabled
                    'n':
                      type: object
                      description: WiFi network settings
                      properties:
                        m:
                          type: string
                          description: Network mode (dhcp/static)
                          enum:
                            - dhcp
                            - static
                        i:
                          type: string
                          format: ipv4
                          description: IP address
                        s:
                          type: string
                          format: ipv4
                          description: Netmask
                        g:
                          type: string
                          format: ipv4
                          description: Gateway
                        d1:
                          type: string
                          format: ipv4
                          description: DNS server. Only one DNS server is supported.
      responses:
        '200':
          description: Configuration update result
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    description: Whether the update was successful
                  message:
                    type: string
                    description: Status or error message
                  rebootRequired:
                    type: boolean
                    description: Whether a reboot is required to apply the saved change. Local configuration saves are applied live and normally return false.
                required:
                  - success
                  - message
        '400':
          x-summary: Bad request
          description: Received parameters are not valid.
        '401':
          x-summary: Unauthorized
          description: Invalid access token.
        '403':
          x-summary: Forbidden
          description: Access token does not have permission to access this resource.
        '503':
          x-summary: Service unavailable
          description: HAN reader is unable to process request.
  /definitions:
    get:
      operationId: Definitions
      summary: Definitions
      description: Returns static values and selectable definitions for UI clients.
      tags:
        - Configuration
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HanDefinitionsMessage'
        '400':
          x-summary: Bad request
          description: Received parameters are not valid.
        '401':
          x-summary: Unauthorized
          description: Invalid access token.
        '403':
          x-summary: Forbidden
          description: Access token does not have permission to access this resource.
        '503':
          x-summary: Service unavailable
          description: HAN reader is unable to process request.
  /reboot:
    post:
      operationId: Reboot
      summary: Reboot
      description: Requests a HAN reader reboot. Connected SSE and WebSocket clients receive a `HAN_STATE_MESSAGE` with `rebootPending` set to true before the device restarts.
      tags:
        - Configuration
      responses:
        '200':
          description: Reboot requested.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: false
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                    enum:
                      - reboot:ok
                    example: reboot:ok
                  rebootRequired:
                    type: boolean
                    description: Always true for a successful reboot request.
                    example: true
                required:
                  - success
                  - message
                  - rebootRequired
        '409':
          description: Reboot can not be requested because a reboot is already pending.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: false
                properties:
                  success:
                    type: boolean
                    example: false
                  message:
                    type: string
                    enum:
                      - reboot:already-pending
                    example: reboot:already-pending
                required:
                  - success
                  - message
  /meter:
    get:
      operationId: Meter
      summary: Meter
      description: Returns the latest electricity meter data received by the HAN reader. This endpoint is intended for the latest live reading values reported by the electricity meter.
      tags:
        - Meter
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HanMeterMessage'
        '400':
          x-summary: Bad request
          description: Received parameters are not valid.
        '401':
          x-summary: Unauthorized
          description: Invalid access token.
        '403':
          x-summary: Forbidden
          description: Access token does not have permission to access this resource.
        '503':
          x-summary: Service unavailable
          description: HAN reader has not received any data from the electricity meter yet.
  /meter/id:
    get:
      operationId: MeterIdentity
      summary: Meter identity
      description: Returns low-churn electricity meter identity fields parsed from HAN telegrams. This endpoint is intended for metadata that changes rarely, so clients can fetch meter identity separately from high-frequency /meter polling.
      tags:
        - Meter
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HanMeterIdentityMessage'
        '400':
          x-summary: Bad request
          description: Received parameters are not valid.
        '401':
          x-summary: Unauthorized
          description: Invalid access token.
        '403':
          x-summary: Forbidden
          description: Access token does not have permission to access this resource.
        '503':
          x-summary: Service unavailable
          description: HAN reader has not received any data from the electricity meter yet.
  /data:
    get:
      operationId: Data
      summary: Data
      description: Returns a compact legacy snapshot of the latest electricity meter reading, configured main fuse, current price, and estimated cost values.
      tags:
        - Meter
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HanCompactDataMessage'
        '400':
          x-summary: Bad request
          description: Received parameters are not valid.
        '401':
          x-summary: Unauthorized
          description: Invalid access token.
        '403':
          x-summary: Forbidden
          description: Access token does not have permission to access this resource.
        '503':
          x-summary: Service unavailable
          description: HAN reader has not received any data from the electricity meter yet.
  /prices:
    get:
      operationId: Prices
      summary: Prices
      description: Returns the current active electricity price, configured currency, and known price time slots.
      tags:
        - Meter
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HanPricesMessage'
        '400':
          x-summary: Bad request
          description: Received parameters are not valid.
        '401':
          x-summary: Unauthorized
          description: Invalid access token.
        '403':
          x-summary: Forbidden
          description: Access token does not have permission to access this resource.
        '503':
          x-summary: Service unavailable
          description: HAN reader is unable to process request.
  /history/hourly:
    get:
      operationId: HistoryHourly
      summary: Hourly history
      description: 'Returns recent high-resolution history samples. Arrays are column-oriented: values at the same index belong to the same sample. Samples are ordered oldest to newest. The newest sample can be a live rolling bucket for the current 10-second period and is marked incomplete until finalized.'
      tags:
        - History
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HanHourlyHistoryMessage'
        '400':
          x-summary: Bad request
          description: Received parameters are not valid.
        '401':
          x-summary: Unauthorized
          description: Invalid access token.
        '403':
          x-summary: Forbidden
          description: Access token does not have permission to access this resource.
        '503':
          x-summary: Service unavailable
          description: HAN reader is unable to process request.
  /history/weekly:
    get:
      operationId: HistoryWeekly
      summary: Weekly history
      description: 'Returns recent 15-minute history samples. Arrays are column-oriented: values at the same index belong to the same sample. Samples are ordered oldest to newest. The newest sample can be a live rolling bucket for the current 15-minute period and is marked incomplete until finalized.'
      tags:
        - History
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HanWeeklyHistoryMessage'
        '400':
          x-summary: Bad request
          description: Received parameters are not valid.
        '401':
          x-summary: Unauthorized
          description: Invalid access token.
        '403':
          x-summary: Forbidden
          description: Access token does not have permission to access this resource.
        '503':
          x-summary: Service unavailable
          description: HAN reader is unable to process request.
  /history/monthly:
    get:
      operationId: HistoryMonthly
      summary: Monthly history
      description: 'Returns recent daily history samples. Arrays are column-oriented: values at the same index belong to the same sample. Samples are ordered oldest to newest. The newest sample can be a live rolling bucket for the current day and is marked incomplete until finalized.'
      tags:
        - History
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HanMonthlyHistoryMessage'
        '400':
          x-summary: Bad request
          description: Received parameters are not valid.
        '401':
          x-summary: Unauthorized
          description: Invalid access token.
        '403':
          x-summary: Forbidden
          description: Access token does not have permission to access this resource.
        '503':
          x-summary: Service unavailable
          description: HAN reader is unable to process request.
  /history/yearly:
    get:
      operationId: HistoryYearly
      summary: Yearly history
      description: 'Returns recent monthly history samples. Arrays are column-oriented: values at the same index belong to the same sample. Samples are ordered oldest to newest. The newest sample can be a live rolling bucket for the current month and is marked incomplete until finalized.'
      tags:
        - History
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HanYearlyHistoryMessage'
        '400':
          x-summary: Bad request
          description: Received parameters are not valid.
        '401':
          x-summary: Unauthorized
          description: Invalid access token.
        '403':
          x-summary: Forbidden
          description: Access token does not have permission to access this resource.
        '503':
          x-summary: Service unavailable
          description: HAN reader is unable to process request.
  /telegram:
    get:
      operationId: Telegram
      summary: Telegram
      description: Returns the most recent successfully parsed HAN telegram as plain text. The response body contains the exact telegram text that produced the latest valid meter reading.
      tags:
        - Meter
      responses:
        '200':
          description: Successful operation
          content:
            text/plain:
              schema:
                type: string
        '400':
          x-summary: Bad request
          description: Received parameters are not valid.
        '401':
          x-summary: Unauthorized
          description: Invalid access token.
        '403':
          x-summary: Forbidden
          description: Access token does not have permission to access this resource.
        '503':
          x-summary: Service unavailable
          description: HAN reader has not parsed a valid telegram yet.
  /buffer:
    get:
      operationId: Buffer
      summary: Raw input buffer
      description: Returns the rolling raw HAN input buffer as bytes in oldest-to-newest order. The payload contains up to 4096 of the latest bytes received by the HAN reader before decoding.
      tags:
        - Meter
      responses:
        '200':
          description: Successful operation
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        '400':
          x-summary: Bad request
          description: Received parameters are not valid.
        '401':
          x-summary: Unauthorized
          description: Invalid access token.
        '403':
          x-summary: Forbidden
          description: Access token does not have permission to access this resource.
        '503':
          x-summary: Service unavailable
          description: HAN reader has not received any raw input bytes yet.
  /health:
    get:
      operationId: Health
      summary: Health
      description: |
        Returns health and status information about the HAN reader.
      tags:
        - System
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HanHealthMessage'
        '400':
          x-summary: Bad request
          description: Received parameters are not valid.
        '401':
          x-summary: Unauthorized
          description: Invalid access token.
        '403':
          x-summary: Forbidden
          description: Access token does not have permission to access this resource.
        '503':
          x-summary: Service unavailable
          description: HAN reader is unable to process request.
  /firmware:
    get:
      operationId: Firmware
      summary: Firmware
      description: Returns firmware version metadata for the HAN reader.
      tags:
        - System
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HanFirmwareMessage'
        '400':
          x-summary: Bad request
          description: Received parameters are not valid.
        '401':
          x-summary: Unauthorized
          description: Invalid access token.
        '403':
          x-summary: Forbidden
          description: Access token does not have permission to access this resource.
        '503':
          x-summary: Service unavailable
          description: HAN reader is unable to process request.
  /update:
    get:
      operationId: LegacyFirmwareUpdateState
      summary: LegacyFirmwareUpdateState
      description: Returns the legacy local firmware upload state for backwards compatibility with released `/update` integrations.
      tags:
        - System
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FirmwareUpdateState'
        '400':
          x-summary: Bad request
          description: Received parameters are not valid.
        '503':
          x-summary: Service unavailable
          description: HAN reader is unable to process request.
    post:
      operationId: LegacyFirmwareUpload
      summary: LegacyFirmwareUpload
      description: Uploads a firmware image using multipart form field `filename` and preserves the released `/update` behavior by activating the uploaded image and requesting reboot after validation succeeds. New staged OTA workflows are available under `/ota`.
      tags:
        - System
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              additionalProperties: false
              properties:
                filename:
                  type: string
                  format: binary
                  description: Firmware binary image.
              required:
                - filename
      responses:
        '200':
          description: Firmware upload handled and legacy update state returned.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FirmwareUpdateState'
        '400':
          x-summary: Bad request
          description: The request was not a valid multipart firmware upload.
        '404':
          x-summary: Not found
          description: Local OTA upload support is disabled on this reader.
        '409':
          x-summary: Conflict
          description: A firmware operation is already in progress.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FirmwareUpdateState'
        '503':
          x-summary: Service unavailable
          description: HAN reader is unable to process request.
  /ota:
    get:
      operationId: OtaState
      summary: OtaState
      description: Returns the current OTA lifecycle state. This is the REST snapshot equivalent of the `HAN_OTA_MESSAGE` realtime event.
      tags:
        - System
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HanOtaMessage'
        '400':
          x-summary: Bad request
          description: Received parameters are not valid.
        '503':
          x-summary: Service unavailable
          description: HAN reader is unable to process request.
    post:
      operationId: UploadOtaImage
      summary: UploadOtaImage
      description: Uploads an OTA firmware image using multipart form field `filename`. The request must include a Cozify HAN bearer JWT with `scope` containing `ota:write` or `ota:*`. The reader validates the image and stages it in the inactive OTA slot. Use `/ota/activate` to activate the staged image and reboot.
      tags:
        - System
      security:
        - CozifyHanJwt: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              additionalProperties: false
              properties:
                filename:
                  type: string
                  format: binary
                  description: Firmware binary image.
              required:
                - filename
      responses:
        '200':
          description: OTA upload accepted and OTA state returned.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HanOtaMessage'
        '400':
          x-summary: Bad request
          description: The request was not a valid multipart firmware upload.
        '401':
          x-summary: Unauthorized
          description: Missing or invalid OTA bearer token.
        '404':
          x-summary: Not found
          description: Local OTA upload support is disabled on this reader.
        '409':
          x-summary: Conflict
          description: An OTA operation is already in progress or staged.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HanOtaMessage'
        '503':
          x-summary: Service unavailable
          description: HAN reader is unable to process request.
  /ota/download:
    post:
      operationId: DownloadOtaImage
      summary: DownloadOtaImage
      description: Requests downloading the available cloud OTA image. The configured firmware update policy controls what happens after the image is staged. With `automatic`, the reader activates the staged image and reboots automatically. With `download` or `manual`, the image waits in `READY_TO_REBOOT` until `/ota/activate` is called. Progress and resulting state are reported through the returned `HAN_OTA_MESSAGE` snapshot and subsequent SSE or WebSocket `HAN_OTA_MESSAGE` events.
      tags:
        - System
      responses:
        '200':
          description: OTA download request accepted and current OTA state returned.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HanOtaMessage'
        '409':
          x-summary: Conflict
          description: No cloud OTA update is available, another OTA operation is active, a staged update is waiting for activation, or a reboot is pending.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HanOtaMessage'
        '503':
          x-summary: Service unavailable
          description: HAN reader is unable to process request.
  /ota/clear:
    post:
      operationId: ClearOtaState
      summary: ClearOtaState
      description: Clears a failed OTA state after the user has acknowledged the error. Active OTA operations, staged updates, and reboot-pending states are not cleared.
      tags:
        - System
      responses:
        '200':
          description: Failed OTA state cleared and current OTA state returned.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HanOtaMessage'
        '409':
          x-summary: Conflict
          description: The current OTA state can not be cleared because an OTA operation is active, a staged update is ready, or reboot is pending.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HanOtaMessage'
        '503':
          x-summary: Service unavailable
          description: HAN reader is unable to process request.
  /ota/activate:
    post:
      operationId: ActivateOtaImage
      summary: ActivateOtaImage
      description: Activates a staged OTA image and requests a reboot. This is useful for manual OTA flows after a validated image has reached `READY_TO_REBOOT`.
      tags:
        - System
      responses:
        '200':
          description: Staged firmware image activated and reboot requested.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HanOtaMessage'
        '409':
          x-summary: Conflict
          description: No staged firmware image is available to activate.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HanOtaMessage'
        '500':
          x-summary: Internal server error
          description: The staged firmware image could not be activated.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HanOtaMessage'
  /scan-networks:
    get:
      operationId: ScanNetworks
      summary: ScanNetworks
      description: |
        Initiates or returns the result of a WiFi network scan. If a scan is running or requested, returns status. If scan is complete, returns a list of found networks with details.

        **Note:** Performing a WiFi scan may temporarily disconnect the HAN reader from its currently connected WiFi network.

        - If the scan is running or just started, returns `{ "status": "SCAN_RUNNING" }`.
        - If the scan failed, returns `{ "status": "SCAN_FAILED" }`.
        - If the scan succeeded, returns `{ "status": "SCAN_SUCCESS", "networks": [...] }`.

        Each network entry includes SSID, BSSID, RSSI, channel, and authentication type.
      tags:
        - Network
      parameters:
        - in: header
          name: X-Scan-Start
          schema:
            type: string
          required: false
          description: If present, triggers a new WiFi scan.
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    $ref: '#/components/schemas/ScanStatus'
                  networks:
                    type: array
                    description: List of found WiFi networks (present only if status is SCAN_SUCCESS)
                    items:
                      type: object
                      properties:
                        ssid:
                          type: string
                          description: WiFi network SSID
                        bssid:
                          type: string
                          description: WiFi network BSSID (MAC address)
                        rssi:
                          type: integer
                          description: Signal strength (RSSI)
                        channel:
                          type: integer
                          description: WiFi channel
                        auth:
                          $ref: '#/components/schemas/WifiAuthType'
                      required:
                        - ssid
                        - bssid
                        - rssi
                        - channel
                        - auth
                required:
                  - status
        '400':
          x-summary: Bad request
          description: Received parameters are not valid.
        '401':
          x-summary: Unauthorized
          description: Invalid access token.
        '403':
          x-summary: Forbidden
          description: Access token does not have permission to access this resource.
        '503':
          x-summary: Service unavailable
          description: HAN reader is unable to process request.
components:
  securitySchemes:
    CozifyHanJwt:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: Cozify HAN bearer token signed as an HS512 JWT. Tokens must use audience `cozify-han`, identify this reader by `sub` or `kid`, include the required endpoint scope, and carry a future `exp` timestamp.
  schemas:
    HanStateMessage:
      type: object
      description: Current HAN reader state message.
      additionalProperties: false
      properties:
        type:
          type: string
          description: Message discriminator. Always HAN_STATE_MESSAGE.
          enum:
            - HAN_STATE_MESSAGE
          example: HAN_STATE_MESSAGE
        ts:
          type: integer
          format: int64
          description: Unix timestamp in milliseconds since epoch (UTC), according to the HAN reader clock.
          example: 1735682400000
        uuid:
          type: string
          description: Unique stable identifier of the HAN reader.
          example: 7f2f8c1e-2c6d-4a25-b2b1-6f6a27d5f5de
        name:
          type: string
          description: HAN reader hostname used on the local network.
          example: Cozify-HAN-123-456
        model:
          type: string
          description: Hardware model identifier.
          example: HAN
        serial:
          type: string
          description: HAN reader serial number.
          example: HAN12345678
        version:
          type: string
          description: Installed firmware version.
          example: 1.0.1.0
        state:
          type: string
          description: Internal lifecycle state of the HAN reader.
          example: REGISTERED
        mac:
          type: string
          description: HAN reader MAC address.
          example: AA:BB:CC:DD:EE:FF
        ethIp:
          type: string
          description: Ethernet IPv4 address, or 0.0.0.0 when not assigned.
          example: 192.168.1.50
        wifiIp:
          type: string
          description: Wi-Fi IPv4 address, or 0.0.0.0 when not assigned.
          example: 192.168.2.50
        online:
          type: boolean
          description: True when the HAN reader is considered online and operational.
          example: true
        rebootPending:
          type: boolean
          description: True when the HAN reader has accepted a reboot request and is about to restart.
          example: false
        rebootReason:
          type:
            - string
            - 'null'
          description: Reason for the pending reboot; null or omitted when no reboot is pending.
          enum:
            - null
            - UNKNOWN
            - FACTORY_RESET
            - FACTORY_REGISTER
            - OTA_UPDATE
            - CONFIG_UPDATE
            - USER_REQUESTED
            - HANDSHAKE_FAILED
            - FACTORY_NEW_ESTABLISHED
          example: OTA_UPDATE
        channel:
          type: string
          description: Firmware channel identifier.
          enum:
            - release
            - development
            - test
          example: release
      required:
        - type
        - ts
        - uuid
        - name
        - model
        - serial
        - version
        - state
        - mac
        - ethIp
        - wifiIp
        - online
        - rebootPending
        - channel
    NetworkMode:
      type: string
      enum:
        - dhcp
        - static
    HanDefinitionsMessage:
      type: object
      description: Static values and selectable definitions exposed for UI clients.
      additionalProperties: false
      properties:
        type:
          type: string
          description: Response type identifier.
          enum:
            - HAN_DEFINITIONS
        currencies:
          type: array
          description: Supported price currency values.
          items:
            type: string
            description: Value to send in configuration and read from data endpoints.
            enum:
              - EUR
              - SEK
              - NOK
              - DKK
              - GBP
              - PLN
              - CZK
              - HUF
              - RON
              - BGN
              - CHF
              - ISK
          example:
            - EUR
            - SEK
            - NOK
            - DKK
        priceAreas:
          type: array
          description: Supported spot price bidding areas.
          items:
            type: string
            description: Value to send in configuration and use for spot price lookup.
            enum:
              - FI
              - EE
              - SE1
              - SE2
              - SE3
              - SE4
          example:
            - FI
            - EE
            - SE1
            - SE2
            - SE3
            - SE4
        timezones:
          type: array
          description: Built-in Linux timezone IDs.
          items:
            type: string
            description: Value to send in configuration endpoints.
          example:
            - Etc/UTC
            - Europe/Helsinki
            - Europe/Tallinn
            - Europe/Stockholm
      required:
        - type
        - currencies
        - priceAreas
        - timezones
    HanMeterMessage:
      type: object
      description: Electricity meter data message received by the HAN reader.
      additionalProperties: false
      properties:
        type:
          type: string
          description: Identifier of the message type. Always HAN_METER_MESSAGE.
          enum:
            - HAN_METER_MESSAGE
          example: HAN_METER_MESSAGE
        ts:
          type: integer
          description: Unix timestamp in milliseconds when the HAN reader received the meter message.
          format: int64
          example: 173568240000
        ic:
          type: number
          description: Active cumulative power in kwh.
          format: double
          example: 1000
        ec:
          type: number
          description: Active cumulative power out kwh.
          format: double
          example: 10
        ric:
          type: number
          description: Reactive cumulative energy imported in kVArh.
          format: double
          example: 1
        rec:
          type: number
          description: Reactive cumulative energy exported in kVArh.
          format: double
          example: 1
        p:
          type: array
          deprecated: true
          items:
            type: number
            format: double
          description: Deprecated. Active power in W. First item is sum on all phases, second item is phase 1, third item is phase 2, and fourth item is phase 3.
          example:
            - 1000
            - 300
            - 400
            - 300
        pi:
          type: array
          items:
            type: number
            format: double
          description: Active power import/consumption in W. First item is total, then phase 1..3.
          example:
            - 1000
            - 300
            - 400
            - 300
        pe:
          type: array
          items:
            type: number
            format: double
          description: Active power export/overproduction in W. First item is total, then phase 1..3.
          example:
            - 0
            - 0
            - 0
            - 0
        r:
          type: array
          deprecated: true
          items:
            type: number
            format: double
          description: Deprecated. Reactive power in VAr. First item is sum on all phases, second item is phase 1, third item is phase 2, and fourth item is phase 3.
          example:
            - 10
            - 3
            - 4
            - 3
        ri:
          type: array
          items:
            type: number
            format: double
          description: Reactive power import in VAr. First item is total, then phase 1..3.
          example:
            - 10
            - 3
            - 4
            - 3
        re:
          type: array
          items:
            type: number
            format: double
          description: Reactive power export in VAr. First item is total, then phase 1..3.
          example:
            - 0
            - 0
            - 0
            - 0
        u:
          type: array
          items:
            type: number
            format: double
          description: Voltage in V. Item 0 is phase 1, item 1 is phase 2, and item 2 is phase 3.
          example:
            - 220
            - 220
            - 220
        i:
          type: array
          items:
            type: number
            format: double
          description: Current in A. Item 0 is phase 1, item 1 is phase 2, and item 2 is phase 3.
          example:
            - 6
            - 6
            - 6
      required:
        - type
        - ts
        - ic
        - ec
        - ric
        - rec
        - p
        - pi
        - pe
        - r
        - ri
        - re
        - u
        - i
    HanMeterIdentityMessage:
      type: object
      description: Electricity meter identity fields extracted from HAN telegrams.
      additionalProperties: false
      properties:
        type:
          type: string
          description: Message discriminator. Always HAN_METER_IDENTITY_MESSAGE.
          enum:
            - HAN_METER_IDENTITY_MESSAGE
          example: HAN_METER_IDENTITY_MESSAGE
        manufacturerId:
          type: string
          description: Meter manufacturer identifier from telegram metadata.
          maxLength: 4
          example: KFM
        deviceType:
          type: string
          description: Meter device type identifier from telegram metadata.
          maxLength: 2
          example: E
        meterId:
          type: string
          description: Meter serial number or unique meter identifier.
          maxLength: 16
          example: A123456789012345
        meterName:
          type: string
          description: Meter name from telegram metadata.
          maxLength: 16
          example: AM550
      required:
        - type
        - manufacturerId
        - deviceType
        - meterId
        - meterName
    HanCompactCostEstimate:
      type: object
      description: Estimated energy cost for the current active import power and price.
      additionalProperties: false
      properties:
        h:
          type: number
          format: double
          description: Estimated hourly cost.
          example: 10
        d:
          type: number
          format: double
          description: Estimated daily cost.
          example: 240
        w:
          type: number
          format: double
          description: Estimated weekly cost.
          example: 1680
        m:
          type: number
          format: double
          description: Estimated monthly cost.
          example: 7200
      required:
        - h
        - d
        - w
        - m
    HanCompactDataMessage:
      type: object
      description: Compact legacy electricity meter data snapshot.
      additionalProperties: false
      properties:
        type:
          type: string
          description: Message discriminator. Always HAN_COMPACT_DATA_MESSAGE.
          enum:
            - HAN_COMPACT_DATA_MESSAGE
          example: HAN_COMPACT_DATA_MESSAGE
        im:
          type: number
          format: double
          description: Calculated maximum import power from main fuse in W.
          example: 18000
        mf:
          type: integer
          format: int32
          description: Configured main fuse in A.
          example: 25
        i:
          type: number
          format: double
          description: Active import power in W.
          example: 1000
        e:
          type: number
          format: double
          description: Active export power in W.
          example: 0
        ri:
          type: number
          format: double
          description: Reactive import power in VAr.
          example: 10
        re:
          type: number
          format: double
          description: Reactive export power in VAr.
          example: 0
        ic:
          type: number
          format: double
          description: Active cumulative energy imported in kWh.
          example: 1000
        ec:
          type: number
          format: double
          description: Active cumulative energy exported in kWh.
          example: 10
        ric:
          type: number
          format: double
          description: Reactive cumulative energy imported in kVArh.
          example: 1
        rec:
          type: number
          format: double
          description: Reactive cumulative energy exported in kVArh.
          example: 1
        u1:
          type: number
          format: double
          description: Phase 1 voltage in V.
          example: 230
        u2:
          type: number
          format: double
          description: Phase 2 voltage in V.
          example: 230
        u3:
          type: number
          format: double
          description: Phase 3 voltage in V.
          example: 230
        i1:
          type: number
          format: double
          description: Phase 1 current in A.
          example: 6.1
        i2:
          type: number
          format: double
          description: Phase 2 current in A.
          example: 5.8
        i3:
          type: number
          format: double
          description: Phase 3 current in A.
          example: 6
        p:
          type:
            - number
            - 'null'
          format: double
          description: Current active electricity price in minor currency units per kWh, or null when spot pricing is active and no current price is available.
          example: 8.123456
        pc:
          type: string
          description: Price currency.
          enum:
            - EUR
            - SEK
            - NOK
            - DKK
            - GBP
            - PLN
            - CZK
            - HUF
            - RON
            - BGN
            - CHF
            - ISK
          example: EUR
        c:
          type: integer
          format: int64
          description: Current Unix timestamp in seconds.
          example: 1735682400
        ep:
          $ref: '#/components/schemas/HanCompactCostEstimate'
      required:
        - type
        - im
        - mf
        - i
        - e
        - ri
        - re
        - ic
        - ec
        - ric
        - rec
        - u1
        - u2
        - u3
        - i1
        - i2
        - i3
        - p
        - pc
        - c
        - ep
    HanPricesMessage:
      type: object
      description: Current active electricity price, effective currency, and price time slots known by the HAN reader.
      additionalProperties: false
      properties:
        type:
          type: string
          description: Response type identifier.
          enum:
            - HAN_PRICES_MESSAGE
          example: HAN_PRICES_MESSAGE
        price:
          type:
            - number
            - 'null'
          format: double
          description: Current active electricity price in minor currency units per kWh, or null when spot pricing is active and no current price is available.
          example: 8.123456
        currency:
          type: string
          description: Effective price currency. When spot pricing is active and available, this is the currency returned by the spot price service.
          enum:
            - EUR
            - SEK
            - NOK
            - DKK
            - GBP
            - PLN
            - CZK
            - HUF
            - RON
            - BGN
            - CHF
            - ISK
          example: EUR
        prices:
          type: array
          description: Price slots. Each item contains Unix start timestamp in milliseconds and price in minor currency units per kWh.
          items:
            type: array
            minItems: 2
            maxItems: 2
            items:
              type: number
              format: double
          example:
            - - 1735682400000
              - 8.123456
            - - 1735686000000
              - 9.234567
      required:
        - type
        - price
        - currency
        - prices
    HanHistoryFlags:
      type: integer
      format: int32
      description: Per-sample history flags as a bitmask. Bit 0 (value 1) means price missing, bit 1 (value 2) means late reading, bit 2 (value 4) means incomplete data, and bit 3 (value 8) means load integrity failed. The newest sample can be a live rolling bucket for the current period; rolling buckets are marked with incomplete data (value 4) until the period is finalized.
      minimum: 0
      maximum: 15
      example: 4
    HanHistoryPhaseCurrents:
      type: array
      description: Three phase current values in A. Item 0 is phase 1, item 1 is phase 2, and item 2 is phase 3.
      minItems: 3
      maxItems: 3
      items:
        type: number
        format: double
      example:
        - 6.1
        - 5.8
        - 6
    HanHourlyHistoryMessage:
      type: object
      description: Hourly history data with 10-second samples. The newest sample can be a live rolling 10-second bucket.
      additionalProperties: false
      properties:
        type:
          type: string
          description: Message discriminator. Always HAN_HOURLY_HISTORY_MESSAGE.
          enum:
            - HAN_HOURLY_HISTORY_MESSAGE
          example: HAN_HOURLY_HISTORY_MESSAGE
        x:
          type: integer
          format: int64
          description: Unix timestamp in seconds for the newest comparison point in this response.
          example: 1735682400
        t:
          type: array
          description: Sample timestamps as Unix timestamps in seconds, ordered oldest to newest.
          items:
            type: integer
            format: int64
          example:
            - 1735682390
            - 1735682400
        f:
          type: array
          items:
            $ref: '#/components/schemas/HanHistoryFlags'
          example:
            - 0
            - 0
        i:
          type: array
          description: Active import power in kW.
          items:
            type: number
            format: double
          example:
            - 1.234
            - 1.236
        e:
          type: array
          description: Active export power in kW.
          items:
            type: number
            format: double
          example:
            - 0
            - 0
        c_min:
          type: array
          description: Minimum current by phase in A for each sample.
          items:
            $ref: '#/components/schemas/HanHistoryPhaseCurrents'
        c_avg:
          type: array
          description: Average current by phase in A for each sample.
          items:
            $ref: '#/components/schemas/HanHistoryPhaseCurrents'
        c_max:
          type: array
          description: Maximum current by phase in A for each sample.
          items:
            $ref: '#/components/schemas/HanHistoryPhaseCurrents'
      required:
        - type
        - x
        - t
        - f
        - i
        - e
        - c_min
        - c_avg
        - c_max
    HanWeeklyHistoryMessage:
      type: object
      description: Weekly history data with 15-minute samples. The newest sample can be a live rolling 15-minute bucket.
      additionalProperties: false
      properties:
        type:
          type: string
          description: Message discriminator. Always HAN_WEEKLY_HISTORY_MESSAGE.
          enum:
            - HAN_WEEKLY_HISTORY_MESSAGE
          example: HAN_WEEKLY_HISTORY_MESSAGE
        x:
          type: integer
          format: int64
          description: Unix timestamp in seconds for the newest comparison point in this response.
          example: 1735682400
        t:
          type: array
          description: Sample timestamps as Unix timestamps in seconds, ordered oldest to newest.
          items:
            type: integer
            format: int64
          example:
            - 1735681500
            - 1735682400
        f:
          type: array
          items:
            $ref: '#/components/schemas/HanHistoryFlags'
          example:
            - 0
            - 0
        i:
          type: array
          description: Active import energy delta in kWh. The value is null when the previous sample is missing or cannot be trusted.
          items:
            type:
              - number
              - 'null'
            format: double
          example:
            - 0.321
            - null
        e:
          type: array
          description: Active export energy delta in kWh. The value is null when the previous sample is missing or cannot be trusted.
          items:
            type:
              - number
              - 'null'
            format: double
          example:
            - 0
            - null
        c_min:
          type: array
          description: Minimum current by phase in A for each sample.
          items:
            $ref: '#/components/schemas/HanHistoryPhaseCurrents'
        c_max:
          type: array
          description: Maximum current by phase in A for each sample.
          items:
            $ref: '#/components/schemas/HanHistoryPhaseCurrents'
        c_avg:
          type: array
          description: Average current by phase in A for each sample.
          items:
            $ref: '#/components/schemas/HanHistoryPhaseCurrents'
        p:
          type: array
          description: Electricity price for each sample in minor currency units per kWh, using the same price value format as /prices. Missing price values are returned as null.
          items:
            type:
              - number
              - 'null'
            format: double
          example:
            - 8.123456
            - null
        pc:
          type: string
          description: Price currency.
          example: EUR
      required:
        - type
        - x
        - t
        - f
        - i
        - e
        - c_min
        - c_max
        - c_avg
        - p
        - pc
    HanMonthlyHistoryMessage:
      type: object
      description: Monthly history data with daily samples. The newest sample can be a live rolling day bucket.
      additionalProperties: false
      properties:
        type:
          type: string
          description: Message discriminator. Always HAN_MONTHLY_HISTORY_MESSAGE.
          enum:
            - HAN_MONTHLY_HISTORY_MESSAGE
          example: HAN_MONTHLY_HISTORY_MESSAGE
        x:
          type: integer
          format: int64
          description: Unix timestamp in seconds for the newest comparison point in this response.
          example: 1735682400
        t:
          type: array
          description: Sample timestamps as Unix timestamps in seconds, ordered oldest to newest.
          items:
            type: integer
            format: int64
          example:
            - 1735596000
            - 1735682400
        f:
          type: array
          items:
            $ref: '#/components/schemas/HanHistoryFlags'
          example:
            - 0
            - 0
        i:
          type: array
          description: Active import energy delta in kWh. The value is null when the previous sample is missing or cannot be trusted.
          items:
            type:
              - number
              - 'null'
            format: double
          example:
            - 12.345
            - null
        e:
          type: array
          description: Active export energy delta in kWh. The value is null when the previous sample is missing or cannot be trusted.
          items:
            type:
              - number
              - 'null'
            format: double
          example:
            - 0
            - null
        c_min:
          type: array
          description: Minimum current by phase in A for each sample.
          items:
            $ref: '#/components/schemas/HanHistoryPhaseCurrents'
        c_max:
          type: array
          description: Maximum current by phase in A for each sample.
          items:
            $ref: '#/components/schemas/HanHistoryPhaseCurrents'
        c_avg:
          type: array
          description: Average current by phase in A for each sample.
          items:
            $ref: '#/components/schemas/HanHistoryPhaseCurrents'
      required:
        - type
        - x
        - t
        - f
        - i
        - e
        - c_min
        - c_max
        - c_avg
    HanYearlyHistoryMessage:
      type: object
      description: Yearly history data with monthly samples. The newest sample can be a live rolling month bucket.
      additionalProperties: false
      properties:
        type:
          type: string
          description: Message discriminator. Always HAN_YEARLY_HISTORY_MESSAGE.
          enum:
            - HAN_YEARLY_HISTORY_MESSAGE
          example: HAN_YEARLY_HISTORY_MESSAGE
        x:
          type: integer
          format: int64
          description: Unix timestamp in seconds for the newest comparison point in this response.
          example: 1735682400
        t:
          type: array
          description: Sample timestamps as Unix timestamps in seconds, ordered oldest to newest.
          items:
            type: integer
            format: int64
          example:
            - 1733004000
            - 1735682400
        f:
          type: array
          items:
            $ref: '#/components/schemas/HanHistoryFlags'
          example:
            - 0
            - 0
        i:
          type: array
          description: Active import energy in kWh for each monthly sample.
          items:
            type: number
            format: double
          example:
            - 321.123
            - 298.456
        e:
          type: array
          description: Active export energy in kWh for each monthly sample.
          items:
            type: number
            format: double
          example:
            - 0
            - 0
        c_min:
          type: array
          description: Minimum current by phase in A for each monthly sample.
          items:
            $ref: '#/components/schemas/HanHistoryPhaseCurrents'
        c_max:
          type: array
          description: Maximum current by phase in A for each monthly sample.
          items:
            $ref: '#/components/schemas/HanHistoryPhaseCurrents'
        c_avg:
          type: array
          description: Average current by phase in A for each monthly sample.
          items:
            $ref: '#/components/schemas/HanHistoryPhaseCurrents'
      required:
        - type
        - x
        - t
        - f
        - i
        - e
        - c_min
        - c_max
        - c_avg
    HanHealthMessage:
      type: object
      description: HAN reader health and runtime diagnostics message.
      additionalProperties: false
      properties:
        type:
          type: string
          description: Message discriminator. Always HAN_HEALTH_MESSAGE.
          enum:
            - HAN_HEALTH_MESSAGE
          example: HAN_HEALTH_MESSAGE
        ts:
          type: integer
          format: int64
          description: Unix timestamp in milliseconds since epoch (UTC), according to the HAN reader clock.
          example: 1735682400000
        uuid:
          type: string
          description: Unique stable identifier of the HAN reader.
          example: 7f2f8c1e-2c6d-4a25-b2b1-6f6a27d5f5de
        hostname:
          type: string
          description: HAN reader hostname used on the local network.
          example: Cozify-HAN-123-456
        heap_size:
          type: integer
          description: Total heap memory in bytes.
          example: 327680
        free_heap_size:
          type: integer
          description: Current free heap memory in bytes.
          example: 182144
        free_heap_percent:
          type: number
          format: double
          description: Current free heap memory as a percentage of total heap memory.
          minimum: 0
          maximum: 100
          example: 55.6
        minimum_free_heap_size:
          type: integer
          description: Lowest observed free heap memory since boot in bytes.
          example: 176320
        tlsReady:
          type: boolean
          description: True when device-wide TLS allocation and global CA store initialization completed.
          example: true
        wifi_rssi:
          type:
            - integer
            - 'null'
          description: Current Wi-Fi station RSSI in dBm, or null when Wi-Fi STA is not connected.
          example: -58
        cpu_reset_reason:
          type: string
          description: Most recent reset cause reported by the ESP32 runtime.
          enum:
            - RST_UNKNOWN
            - RST_POWERON
            - RST_EXT
            - RST_SW
            - RST_PANIC
            - RST_INT_WDT
            - RST_TASK_WDT
            - RST_WDT
            - RST_DEEPSLEEP
            - RST_BROWNOUT
            - RST_SDIO
          example: RST_POWERON
        connections:
          type: object
          description: Compact connection counters and configured limits by transport.
          additionalProperties: false
          properties:
            ws:
              type: object
              description: WebSocket connection count and limit.
              additionalProperties: false
              properties:
                c:
                  type: integer
                  description: Current connected client count.
                  minimum: 0
                  example: 0
                m:
                  type: integer
                  description: Maximum allowed connected clients.
                  minimum: 0
                  example: 3
              required:
                - c
                - m
            sse:
              type: object
              description: Server-Sent Events connection count and limit.
              additionalProperties: false
              properties:
                c:
                  type: integer
                  description: Current connected client count.
                  minimum: 0
                  example: 1
                m:
                  type: integer
                  description: Maximum allowed connected clients.
                  minimum: 0
                  example: 5
              required:
                - c
                - m
          required:
            - ws
            - sse
        uptime_ms:
          type: integer
          format: int64
          description: Monotonic milliseconds elapsed since the HAN reader booted.
          example: 1234567
        services:
          type: array
          description: Generic service health summaries shown by diagnostics clients.
          items:
            type: object
            additionalProperties: false
            properties:
              name:
                type: string
                description: Stable service identifier.
                example: spotprices
              status:
                type: string
                description: Service lifecycle status.
                enum:
                  - starting
                  - started
                  - stopping
                  - stopped
                  - failed
                example: stopped
              health:
                type: string
                description: Current service health.
                enum:
                  - disabled
                  - healthy
                  - degraded
                  - error
                  - fail
                example: disabled
              error:
                type:
                  - string
                  - 'null'
                description: Last error details, when available.
                example: null
            required:
              - name
              - status
              - health
      required:
        - type
        - ts
        - uuid
        - hostname
        - heap_size
        - free_heap_size
        - free_heap_percent
        - minimum_free_heap_size
        - tlsReady
        - wifi_rssi
        - cpu_reset_reason
        - connections
        - uptime_ms
        - services
    HanFirmwareMessage:
      type: object
      description: Firmware version metadata for the HAN reader.
      additionalProperties: false
      properties:
        type:
          type: string
          description: Message discriminator. Always HAN_FIRMWARE_MESSAGE.
          enum:
            - HAN_FIRMWARE_MESSAGE
          example: HAN_FIRMWARE_MESSAGE
        version:
          type: string
          description: Firmware version.
          example: 1.0.1.9
        commit:
          type: string
          description: Firmware source commit identifier.
          example: abcdef1
        time:
          type: string
          description: Firmware build timestamp.
          example: '2025-01-01T12:00:00Z'
        activeSlot:
          type: string
          description: Currently running firmware update slot.
          enum:
            - slot1
            - slot2
            - unknown
          example: slot1
        assetsVersion:
          type: string
          description: Bundled asset version marker used to detect when a browser reload should fetch the newest reader interface.
          pattern: ^[0-9a-f]{12}$
          example: 0123456789ab
      required:
        - type
        - version
        - commit
        - time
        - activeSlot
    FirmwareUpdateState:
      type: object
      description: Legacy local firmware upload state returned by `/update`.
      additionalProperties: false
      properties:
        status:
          type: string
          description: Current firmware update status.
          example: INSTALL_IN_PROGRESS
        updating:
          type: boolean
          description: True while the reader is writing a firmware image.
          example: true
        rebootPending:
          type: boolean
          description: True when the uploaded image has been activated and the reader is waiting to reboot.
          example: false
        bytesWritten:
          type: integer
          format: int64
          minimum: 0
          description: Number of uploaded firmware bytes written by the reader.
          example: 262144
        bytesExpected:
          type: integer
          format: int64
          minimum: 0
          description: Expected upload size when known.
          example: 1048576
        progress:
          type: integer
          minimum: 0
          maximum: 100
          description: Firmware write progress percentage.
          example: 25
        error:
          type: string
          description: Public error code for the last firmware upload failure, when present.
          example: invalid_firmware
      required:
        - status
        - updating
        - rebootPending
        - bytesWritten
        - bytesExpected
        - progress
    HanOtaMessage:
      type: object
      description: Unified OTA state message for update availability, upload/download progress, staging, activation, and failures.
      additionalProperties: false
      properties:
        type:
          type: string
          description: Message discriminator. Always HAN_OTA_MESSAGE.
          enum:
            - HAN_OTA_MESSAGE
          example: HAN_OTA_MESSAGE
        ts:
          type: integer
          format: int64
          description: Unix timestamp in milliseconds since epoch (UTC), according to the HAN reader clock.
          example: 1735682400000
        uuid:
          type: string
          description: Unique stable identifier of the HAN reader.
          example: 7f2f8c1e-2c6d-4a25-b2b1-6f6a27d5f5de
        status:
          type: string
          description: Current OTA lifecycle status.
          enum:
            - IDLE
            - AVAILABLE
            - DOWNLOAD_STARTED
            - DOWNLOADED
            - INSTALL_STARTED
            - INSTALL_IN_PROGRESS
            - INSTALL_FINALIZING
            - READY_TO_REBOOT
            - FAILED
          example: INSTALL_IN_PROGRESS
        version:
          type: string
          description: Desired firmware version when known.
          example: 1.0.2.0
        releaseNotes:
          type: string
          description: Short release notes or summary provided by the update service, when available.
        releaseNotesUrl:
          type: string
          format: uri
          description: URL for detailed release notes, when available.
        source:
          type: string
          description: Where the OTA state originated.
          enum:
            - none
            - cloud
            - local
          example: local
        critical:
          type: boolean
          description: True when the update service marks the available firmware as critical.
          example: false
        forced:
          type: boolean
          description: True when the update service marks the available firmware as forced.
          example: false
        allowDownload:
          type: boolean
          description: True when the currently available cloud version is approved for download.
          example: false
        progress:
          type: integer
          minimum: 0
          maximum: 100
          description: Percent of the update image written to the OTA firmware slot. Local uploads and cloud downloads use the same 0-100 scale when the image size is known.
          example: 42
        activeSlot:
          type: string
          description: Active OTA partition label.
          enum:
            - slot1
            - slot2
            - unknown
          example: slot1
        stagedSlot:
          type: string
          description: OTA partition containing a staged update, or unknown when no image is staged.
          enum:
            - slot1
            - slot2
            - unknown
          example: slot2
        stagedVersion:
          type: string
          description: Firmware version read from the staged image, when available.
          example: 1.0.2.0
        error:
          type: string
          description: Public error code for the last OTA failure, when status is FAILED.
          example: invalid_firmware
      required:
        - type
        - ts
        - uuid
        - status
        - version
        - releaseNotes
        - releaseNotesUrl
        - source
        - critical
        - forced
        - allowDownload
        - progress
        - activeSlot
        - stagedSlot
        - stagedVersion
    ScanStatus:
      type: string
      enum:
        - SCAN_RUNNING
        - SCAN_FAILED
        - SCAN_SUCCESS
    WifiAuthType:
      type: string
      enum:
        - OPEN
        - WEP
        - WPA_PSK
        - WPA2_PSK
        - WPA_WPA2_PSK
        - WPA2_ENTERPRISE
        - WPA3_PSK
        - WPA2_WPA3_PSK
        - WAPI_PSK
