{
  "openapi": "3.1.0",
  "info": {
    "title": "franklinwh-modbus-bridge",
    "description": "**Unofficial software.** `franklinwh-modbus-bridge` is not endorsed, supported, or\naffiliated with FranklinWH in any way. Provided **\"AS IS\"**, for educational\nand informational purposes only, without warranty of any kind, express or\nimplied. The authors and contributors accept no responsibility or liability\nfor any consequences of its use.\n\nThis API issues **write commands to grid-connected battery hardware**. You\nassume all risk, including risk to your hardware, your warranty and your\nelectricity supply.\n\n**Compliance.** Keep your system compliant with the settings your official\nFranklinWH app or installer configured \u2014 they may enforce battery and gateway\nsafety, your local grid profile and import/export limits, or incentive/VPP\nprogramme conditions. Using this API to bypass them is entirely at your own\nrisk and is neither condoned nor encouraged.\n\n**Do not contact FranklinWH support about this software.** Report bugs and\nrequest features at [https://github.com/david2069/franklinwh-modbus-bridge/issues](https://github.com/david2069/franklinwh-modbus-bridge/issues).\n\nFull terms: [LICENSE](https://github.com/david2069/franklinwh-modbus-bridge/blob/main/LICENSE) \u00b7 SunSpec conformance:\n[PICS SM-000028](https://sunspec.org/wp-content/uploads/2009/03/UPDATED_FranklinWH_Modbus_PICS_SM-000028.xlsx)\n",
    "version": "0.2.11"
  },
  "paths": {
    "/api/health": {
      "get": {
        "tags": [
          "health"
        ],
        "summary": "Health",
        "operationId": "health_api_health_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      }
    },
    "/api/health/connectivity": {
      "get": {
        "tags": [
          "health"
        ],
        "summary": "Connectivity",
        "description": "Outage-aware connectivity: per-gateway connected/last-good-poll state plus\nrecent outages. Powers the scheduler's outage view (WiFi\u21924G drops etc.).",
        "operationId": "connectivity_api_health_connectivity_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      }
    },
    "/api/status": {
      "get": {
        "tags": [
          "health"
        ],
        "summary": "Status",
        "operationId": "status_api_status_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      }
    },
    "/api/auth/login": {
      "post": {
        "tags": [
          "auth"
        ],
        "summary": "Login",
        "description": "Verify credentials, set the session cookie. Accepts form or JSON.",
        "operationId": "login_api_auth_login_post",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      }
    },
    "/api/auth/logout": {
      "post": {
        "tags": [
          "auth"
        ],
        "summary": "Logout",
        "operationId": "logout_api_auth_logout_post",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      }
    },
    "/api/auth/session-status": {
      "get": {
        "tags": [
          "auth"
        ],
        "summary": "Session Status",
        "description": "Why a login ended. No auth: it's asked by a page that just got a 401.\n\n``sid`` is the login id the page saved at login time; it is a random\nsecret only that browser holds. If the record says the login is still\nopen but the browser has stopped presenting it, it is ended here with the\nreason that fits (idle past the cookie lifetime = expired, else the\nbrowser dropped it), so the audit trail has it too.",
        "operationId": "session_status_api_auth_session_status_get",
        "parameters": [
          {
            "name": "sid",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "",
              "title": "Sid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "title": "Response Session Status Api Auth Session Status Get"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/auth/password": {
      "post": {
        "tags": [
          "auth"
        ],
        "summary": "Change Own Password",
        "description": "Change your own password. Your other logins end; this one stays.",
        "operationId": "change_own_password_api_auth_password_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PasswordChange"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/auth/me": {
      "get": {
        "tags": [
          "auth"
        ],
        "summary": "Me",
        "operationId": "me_api_auth_me_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "type": "object",
                  "title": "Response Me Api Auth Me Get"
                }
              }
            }
          }
        }
      }
    },
    "/api/auth/sessions": {
      "get": {
        "tags": [
          "auth"
        ],
        "summary": "List Sessions",
        "description": "Logins, most recently active first. ``active=true`` = still open.",
        "operationId": "list_sessions_api_auth_sessions_get",
        "parameters": [
          {
            "name": "active",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false,
              "title": "Active"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 200,
              "title": "Limit"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/auth/sessions/{sid}/revoke": {
      "post": {
        "tags": [
          "auth"
        ],
        "summary": "Revoke Session",
        "description": "End one login now; its page shows \"An administrator ended this login\".",
        "operationId": "revoke_session_api_auth_sessions__sid__revoke_post",
        "parameters": [
          {
            "name": "sid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Sid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/auth/events": {
      "get": {
        "tags": [
          "auth"
        ],
        "summary": "List Auth Events",
        "description": "Audit trail: logins, failed logins, logouts, logins ended (newest first).\n\n``username`` (as typed at sign-in, any case) and ``event`` narrow it.",
        "operationId": "list_auth_events_api_auth_events_get",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 200,
              "title": "Limit"
            }
          },
          {
            "name": "username",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "",
              "title": "Username"
            }
          },
          {
            "name": "event",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "",
              "title": "Event"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/config/{section}": {
      "get": {
        "tags": [
          "admin"
        ],
        "summary": "Get Config",
        "operationId": "get_config_api_config__section__get",
        "parameters": [
          {
            "name": "section",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Section"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/config/{key}": {
      "put": {
        "tags": [
          "admin"
        ],
        "summary": "Set Config",
        "operationId": "set_config_api_config__key__put",
        "parameters": [
          {
            "name": "key",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Key"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConfigUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/gateway": {
      "get": {
        "tags": [
          "admin"
        ],
        "summary": "Get Gateway",
        "description": "The address of one gateway (the primary one unless ``gateway_id``).",
        "operationId": "get_gateway_api_gateway_get",
        "parameters": [
          {
            "name": "gateway_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "default",
              "title": "Gateway Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/gateway/test": {
      "get": {
        "tags": [
          "admin"
        ],
        "summary": "Test Gateway",
        "description": "Test TCP connectivity to a gateway's configured host:port.",
        "operationId": "test_gateway_api_gateway_test_get",
        "parameters": [
          {
            "name": "gateway_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "default",
              "title": "Gateway Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "title": "Response Test Gateway Api Gateway Test Get"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/models": {
      "get": {
        "tags": [
          "admin"
        ],
        "summary": "Get Models",
        "description": "The SunSpec catalog captured for a gateway (the primary one unless named).",
        "operationId": "get_models_api_models_get",
        "parameters": [
          {
            "name": "gateway_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "default",
              "title": "Gateway Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/catalog/points": {
      "get": {
        "tags": [
          "admin"
        ],
        "summary": "List Catalog Points",
        "description": "Flat list of catalog points with metadata + whether each is published\n(promoted into an enabled group). Powers the Explorer \u2192 group picker.",
        "operationId": "list_catalog_points_api_catalog_points_get",
        "parameters": [
          {
            "name": "gateway_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "default",
              "title": "Gateway Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/models/export": {
      "get": {
        "tags": [
          "admin"
        ],
        "summary": "Export Models",
        "description": "Export a gateway's SunSpec models with their current values (#228).\n\n``models``: comma-separated model ids; empty for every model.\n``format``: ``json`` or ``csv``. ``redact_serial`` (default true) replaces\nthe serial number, in model 1's ``SN`` and anywhere else it appears, with\n``<serial>``. The gateway's address and name are never included.",
        "operationId": "export_models_api_models_export_get",
        "parameters": [
          {
            "name": "gateway_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "default",
              "title": "Gateway Id"
            }
          },
          {
            "name": "models",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "",
              "title": "Models"
            }
          },
          {
            "name": "format",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "json",
              "title": "Format"
            }
          },
          {
            "name": "redact_serial",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": true,
              "title": "Redact Serial"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SunSpecExport"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/models/{model_id}/read": {
      "post": {
        "tags": [
          "admin"
        ],
        "summary": "Read Model",
        "description": "On-demand read of a single SunSpec model from the device.\n\nConnects to the controller, calls model.read() to fetch live register\nvalues, then returns the point name\u2192value map.  Also injects the values\ninto the sample bus so the Explorer sees them immediately.",
        "operationId": "read_model_api_models__model_id__read_post",
        "parameters": [
          {
            "name": "model_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "title": "Model Id"
            }
          },
          {
            "name": "gateway_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "default",
              "title": "Gateway Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/models/refresh": {
      "post": {
        "tags": [
          "admin"
        ],
        "summary": "Refresh Models",
        "description": "Re-read a gateway's SunSpec model chain into its catalog.",
        "operationId": "refresh_models_api_models_refresh_post",
        "parameters": [
          {
            "name": "gateway_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "default",
              "title": "Gateway Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/points": {
      "get": {
        "tags": [
          "admin"
        ],
        "summary": "Get Points",
        "operationId": "get_points_api_points_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      }
    },
    "/api/points/{point_id}": {
      "get": {
        "tags": [
          "admin"
        ],
        "summary": "Get Point",
        "operationId": "get_point_api_points__point_id__get",
        "parameters": [
          {
            "name": "point_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Point Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/metrics": {
      "get": {
        "tags": [
          "admin"
        ],
        "summary": "Get Metrics",
        "description": "Return power time-series for the dashboard chart.\n\nSupports two modes:\n- **Relative range**: ``?range=30m`` (default) \u2014 last N from now.\n- **Absolute date range**: ``?start=<epoch>&end=<epoch>`` \u2014 historical query.\n\nOptional ``?bucket=5m`` downsamples to the given interval.\nValid buckets: 1m, 5m, 10m, 15m, 30m, 1h.",
        "operationId": "get_metrics_api_metrics_get",
        "parameters": [
          {
            "name": "range",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Range"
            }
          },
          {
            "name": "start",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "number"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Start"
            }
          },
          {
            "name": "end",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "number"
                },
                {
                  "type": "null"
                }
              ],
              "title": "End"
            }
          },
          {
            "name": "bucket",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Bucket"
            }
          },
          {
            "name": "gateway_id",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Gateway Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/stats/storage": {
      "get": {
        "tags": [
          "admin"
        ],
        "summary": "Get Storage Info",
        "description": "Return DB storage statistics (row counts, sizes, time ranges).",
        "operationId": "get_storage_info_api_stats_storage_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      }
    },
    "/api/metrics/archive": {
      "post": {
        "tags": [
          "admin"
        ],
        "summary": "Run Archive",
        "description": "Manually trigger metrics archival (downsample old raw data).",
        "operationId": "run_archive_api_metrics_archive_post",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      }
    },
    "/api/alarm-events": {
      "get": {
        "tags": [
          "admin"
        ],
        "summary": "Get Alarm Events",
        "description": "Return alarm events in a time window.\n\nProvide either ``start``+``end`` (Unix timestamps) or ``range``\n(e.g. ``30m``, ``1h``, ``24h``).  Events are sparse \u2014 only rows\nwritten on register state changes are returned.",
        "operationId": "get_alarm_events_api_alarm_events_get",
        "parameters": [
          {
            "name": "start",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "number"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Start"
            }
          },
          {
            "name": "end",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "number"
                },
                {
                  "type": "null"
                }
              ],
              "title": "End"
            }
          },
          {
            "name": "range",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Range"
            }
          },
          {
            "name": "gateway_id",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Gateway Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/events": {
      "get": {
        "tags": [
          "admin"
        ],
        "summary": "Get Events",
        "description": "Return durationed alarm + PV-Clipping events in a time window.\n\nUnlike ``/alarm-events`` (raw SET/CLR transitions), this pairs each\ntransition into a single row with a start time, end time, and\nduration, and merges in PV-Clipping/Grid-Forming intervals derived\nfrom ``metrics.grid_mode``. Same ``start``+``end`` / ``range`` params\nas ``/alarm-events``.",
        "operationId": "get_events_api_events_get",
        "parameters": [
          {
            "name": "start",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "number"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Start"
            }
          },
          {
            "name": "end",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "number"
                },
                {
                  "type": "null"
                }
              ],
              "title": "End"
            }
          },
          {
            "name": "range",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Range"
            }
          },
          {
            "name": "gateway_id",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Gateway Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/settings/metrics": {
      "get": {
        "tags": [
          "admin"
        ],
        "summary": "Get Metrics Settings",
        "description": "Read metrics retention + raw-resolution config.",
        "operationId": "get_metrics_settings_api_settings_metrics_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "admin"
        ],
        "summary": "Put Metrics Settings",
        "description": "Update metrics retention and/or the raw full-resolution window.\n\nraw_age_days is how long full-resolution samples are kept before being\ndownsampled to 5-min buckets; it must be >= 1 and <= retention_days\n(keeping raw longer than total retention is meaningless).",
        "operationId": "put_metrics_settings_api_settings_metrics_put",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MetricsSettingsUpdate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/stats": {
      "get": {
        "tags": [
          "admin"
        ],
        "summary": "Get Stats",
        "description": "Return operational statistics (uptime, polls, errors, data quality).",
        "operationId": "get_stats_api_stats_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      }
    },
    "/api/logs": {
      "get": {
        "tags": [
          "admin"
        ],
        "summary": "Get Logs",
        "description": "Persisted log history, filterable by time span (start_ts/end_ts) + level/\ngateway/source/search. Returned oldest-first (the UI reverses for display).\nFalls back to the in-memory buffer on a fresh DB (default, no-range view).",
        "operationId": "get_logs_api_logs_get",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 2000,
              "title": "Limit"
            }
          },
          {
            "name": "start_ts",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "number"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Start Ts"
            }
          },
          {
            "name": "end_ts",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "number"
                },
                {
                  "type": "null"
                }
              ],
              "title": "End Ts"
            }
          },
          {
            "name": "level",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Level"
            }
          },
          {
            "name": "gateway",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Gateway"
            }
          },
          {
            "name": "source",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Source"
            }
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Search"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/sequence/execute": {
      "post": {
        "tags": [
          "admin"
        ],
        "summary": "Execute Sequence",
        "description": "Execute a SunSpec Modbus sequence against one or all gateways.\n\nIt writes real registers (WSetEna/WSetPct included, i.e. a battery\ndispatch), so it needs the ``control`` capability, and every run is\nrecorded in the control log with who ran it and what it wrote.",
        "operationId": "execute_sequence_api_sequence_execute_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SequenceExecRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/sequences": {
      "get": {
        "tags": [
          "admin"
        ],
        "summary": "List Sequences",
        "description": "List saved sequence files.",
        "operationId": "list_sequences_api_sequences_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      }
    },
    "/api/sequences/{name}": {
      "get": {
        "tags": [
          "admin"
        ],
        "summary": "Get Sequence",
        "description": "Get a saved sequence file's content.",
        "operationId": "get_sequence_api_sequences__name__get",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Name"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "admin"
        ],
        "summary": "Save Sequence",
        "description": "Save or update a sequence file.",
        "operationId": "save_sequence_api_sequences__name__put",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Name"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SequenceSaveRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "admin"
        ],
        "summary": "Delete Sequence",
        "description": "Delete a sequence file.",
        "operationId": "delete_sequence_api_sequences__name__delete",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Name"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/sequences/{name}/rename": {
      "post": {
        "tags": [
          "admin"
        ],
        "summary": "Rename Sequence",
        "description": "Rename a sequence file.",
        "operationId": "rename_sequence_api_sequences__name__rename_post",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Name"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SequenceRenameRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/pics": {
      "get": {
        "tags": [
          "admin"
        ],
        "summary": "Get Pics",
        "description": "Return all PICS compliance statuses.",
        "operationId": "get_pics_api_pics_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "admin"
        ],
        "summary": "Put Pics",
        "description": "Upsert a PICS compliance status for a model point.",
        "operationId": "put_pics_api_pics_put",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PicsUpdateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/battery/limits": {
      "get": {
        "tags": [
          "admin"
        ],
        "summary": "Get Battery Limits",
        "description": "Battery power limits a force command may use: trusted ratings only (#133).",
        "operationId": "get_battery_limits_api_battery_limits_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      }
    },
    "/api/backup/create": {
      "post": {
        "tags": [
          "admin"
        ],
        "summary": "Create Backup",
        "description": "Create a new database backup.",
        "operationId": "create_backup_api_backup_create_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "anyOf": [
                  {
                    "$ref": "#/components/schemas/BackupCreateRequest"
                  },
                  {
                    "type": "null"
                  }
                ],
                "title": "Body"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/backup/list": {
      "get": {
        "tags": [
          "admin"
        ],
        "summary": "List Backups",
        "description": "List available backups, newest first.",
        "operationId": "list_backups_api_backup_list_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      }
    },
    "/api/backup/restore": {
      "post": {
        "tags": [
          "admin"
        ],
        "summary": "Restore Backup",
        "description": "Restore the database from a named backup.\n\nCreates a pre-restore snapshot automatically.  The bridge should be\nrestarted after restore to reload state.",
        "operationId": "restore_backup_api_backup_restore_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BackupRestoreRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/backup/download/{name}": {
      "get": {
        "tags": [
          "admin"
        ],
        "summary": "Download Backup",
        "description": "Download a backup archive as a ZIP file.",
        "operationId": "download_backup_api_backup_download__name__get",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Name"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/metrics/export": {
      "get": {
        "tags": [
          "admin"
        ],
        "summary": "Export Metrics Endpoint",
        "description": "Export metrics as CSV or JSON for the given time range.\n\nUnlike the chart endpoint, this returns every stored row (no\ndownsampling) so exports are lossless.",
        "operationId": "export_metrics_endpoint_api_metrics_export_get",
        "parameters": [
          {
            "name": "range",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "24h",
              "title": "Range"
            }
          },
          {
            "name": "format",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "csv",
              "title": "Format"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/metrics/import": {
      "post": {
        "tags": [
          "admin"
        ],
        "summary": "Import Metrics Endpoint",
        "description": "Import metrics from a CSV file.\n\nExpects columns: timestamp, battery_w, grid_w, solar_w, home_w, soc.\nTimestamp can be ISO-8601 or Unix epoch float.",
        "operationId": "import_metrics_endpoint_api_metrics_import_post",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/Body_import_metrics_endpoint_api_metrics_import_post"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/mqtt/config": {
      "get": {
        "tags": [
          "mqtt"
        ],
        "summary": "Get Config",
        "description": "The stored config, plus where each broker field in use comes from (#103).\n\n``sources`` maps host/port/username/password to ``environment`` /\n``settings`` / ``supervisor`` / ``default``; ``env_vars`` names the variable\nfor each field the environment sets, so the form can say why it's locked.",
        "operationId": "get_config_api_mqtt_config_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "mqtt"
        ],
        "summary": "Update Config",
        "operationId": "update_config_api_mqtt_config_patch",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MqttConfigUpdate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/mqtt/namespace": {
      "get": {
        "tags": [
          "mqtt"
        ],
        "summary": "Get Namespace",
        "description": "The namespace in use, the choices, and whether a change needs confirming.",
        "operationId": "get_namespace_api_mqtt_namespace_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "mqtt"
        ],
        "summary": "Post Namespace",
        "description": "Change this bridge's MQTT namespace (#87). Admin-only (router gate).\n\nBefore anything is published it simply switches. After, ``confirm`` is\nrequired: the old namespace's retained discovery configs that THIS bridge\nwrote are removed (another producer's are left), then everything is\nrepublished under the new one. The answer carries the counts.",
        "operationId": "post_namespace_api_mqtt_namespace_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NamespaceChange"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/mqtt/status": {
      "get": {
        "tags": [
          "mqtt"
        ],
        "summary": "Get Status",
        "operationId": "get_status_api_mqtt_status_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      }
    },
    "/api/mqtt/reconnect": {
      "post": {
        "tags": [
          "mqtt"
        ],
        "summary": "Reconnect",
        "operationId": "reconnect_api_mqtt_reconnect_post",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      }
    },
    "/api/mqtt/publish": {
      "post": {
        "tags": [
          "mqtt"
        ],
        "summary": "Republish Discovery",
        "operationId": "republish_discovery_api_mqtt_publish_post",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      }
    },
    "/api/mqtt/unpublish": {
      "post": {
        "tags": [
          "mqtt"
        ],
        "summary": "Unpublish Discovery",
        "operationId": "unpublish_discovery_api_mqtt_unpublish_post",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      }
    },
    "/api/mqtt/detect-broker": {
      "get": {
        "tags": [
          "mqtt"
        ],
        "summary": "Detect Broker",
        "operationId": "detect_broker_api_mqtt_detect_broker_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      }
    },
    "/api/mqtt/test": {
      "post": {
        "tags": [
          "mqtt"
        ],
        "summary": "Test Connection",
        "description": "Test TCP connectivity.  Accepts optional {host, port} in the body\nto test *before* saving; falls back to the stored config.",
        "operationId": "test_connection_api_mqtt_test_post",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      }
    },
    "/api/mqtt/topics": {
      "get": {
        "tags": [
          "mqtt"
        ],
        "summary": "Get Topics",
        "operationId": "get_topics_api_mqtt_topics_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      }
    },
    "/api/groups": {
      "get": {
        "tags": [
          "groups"
        ],
        "summary": "List Groups",
        "description": "List all publishing groups with member counts.",
        "operationId": "list_groups_api_groups_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "groups"
        ],
        "summary": "Create Group",
        "description": "Create a new custom publishing group.",
        "operationId": "create_group_api_groups_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GroupCreate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/groups/entities": {
      "get": {
        "tags": [
          "groups"
        ],
        "summary": "List Entities",
        "description": "Return the full entity catalog \u2014 all available entity definitions.\n\nUsed by the Settings UI to populate group member pickers.  Each entry\nincludes slug, name, ha_type, state_group, and whether it's a control\nentity.",
        "operationId": "list_entities_api_groups_entities_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      }
    },
    "/api/groups/disabled": {
      "get": {
        "tags": [
          "groups"
        ],
        "summary": "List Disabled",
        "description": "Return slugs of entities disabled by group settings.",
        "operationId": "list_disabled_api_groups_disabled_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      }
    },
    "/api/groups/{slug}": {
      "get": {
        "tags": [
          "groups"
        ],
        "summary": "Get Group",
        "description": "Get a single group with its member list.",
        "operationId": "get_group_api_groups__slug__get",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Slug"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "groups"
        ],
        "summary": "Patch Group",
        "description": "Update name, description, or enabled state of a group.",
        "operationId": "patch_group_api_groups__slug__patch",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Slug"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GroupUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "groups"
        ],
        "summary": "Delete Group",
        "description": "Delete a custom group. Default groups cannot be deleted.",
        "operationId": "delete_group_api_groups__slug__delete",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Slug"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/groups/{slug}/members": {
      "get": {
        "tags": [
          "groups"
        ],
        "summary": "List Members",
        "description": "List entity slugs in a group.",
        "operationId": "list_members_api_groups__slug__members_get",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Slug"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "groups"
        ],
        "summary": "Replace Members",
        "description": "Replace all members of a group.",
        "operationId": "replace_members_api_groups__slug__members_put",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Slug"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MemberUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "groups"
        ],
        "summary": "Add Member",
        "description": "Add an entity to a group.",
        "operationId": "add_member_api_groups__slug__members_post",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Slug"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SingleMember"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/groups/{slug}/members/{entity_slug}": {
      "delete": {
        "tags": [
          "groups"
        ],
        "summary": "Delete Member",
        "description": "Remove an entity from a group.",
        "operationId": "delete_member_api_groups__slug__members__entity_slug__delete",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Slug"
            }
          },
          {
            "name": "entity_slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Entity Slug"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/groups/{slug}/points": {
      "get": {
        "tags": [
          "groups"
        ],
        "summary": "List Group Points",
        "description": "List the catalog points promoted into a group.",
        "operationId": "list_group_points_api_groups__slug__points_get",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Slug"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "groups"
        ],
        "summary": "Add Group Point",
        "description": "Promote a catalog point (\"model.point\") into a group.",
        "operationId": "add_group_point_api_groups__slug__points_post",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Slug"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PointMember"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "groups"
        ],
        "summary": "Remove Group Point",
        "description": "Remove a promoted point from a group (ref as ?ref=model.point).",
        "operationId": "remove_group_point_api_groups__slug__points_delete",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Slug"
            }
          },
          {
            "name": "ref",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Ref"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/site": {
      "get": {
        "tags": [
          "gateways"
        ],
        "summary": "Get Site",
        "description": "Return site configuration.",
        "operationId": "get_site_api_site_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "gateways"
        ],
        "summary": "Patch Site",
        "description": "Update site configuration.",
        "operationId": "patch_site_api_site_patch",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SiteConfigUpdate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/site/status": {
      "get": {
        "tags": [
          "gateways"
        ],
        "summary": "Get Site Status",
        "description": "Return aggregated site-level status from all gateways.",
        "operationId": "get_site_status_api_site_status_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      }
    },
    "/api/system/timezone": {
      "get": {
        "tags": [
          "gateways"
        ],
        "summary": "Get Timezone",
        "description": "Detected timezone, whether a user ever confirmed it, and the local time\nright now \u2014 so the prompt can show what the bridge believes rather than\nasking the user to take it on trust.",
        "operationId": "get_timezone_api_system_timezone_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      }
    },
    "/api/system/timezone/confirm": {
      "post": {
        "tags": [
          "gateways"
        ],
        "summary": "Confirm Timezone Endpoint",
        "description": "Record the user's decision, optionally correcting the timezone.\n\nCorrecting it here only changes what the bridge EXPECTS. The clock itself\ncomes from the container, so a correction must also be applied there (TZ in\ndocker-compose, or the Supervisor's setting) \u2014 otherwise the next start\nreports a mismatch, which is the honest outcome rather than a silent\ndisagreement.",
        "operationId": "confirm_timezone_endpoint_api_system_timezone_confirm_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TimezoneConfirm"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/services/{service_id}/switch-plan": {
      "post": {
        "tags": [
          "gateways"
        ],
        "summary": "Switch Plan",
        "description": "Close the current billing period and begin a new plan.\n\nTotals reset at this boundary deliberately: a period must never span two\nprice sets, or its cost is an average of tariffs that were never both in\nforce. Closed periods keep their own snapshot of retailer/network/version,\nso history reads as what it was.",
        "operationId": "switch_plan_api_services__service_id__switch_plan_post",
        "parameters": [
          {
            "name": "service_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Service Id"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PlanSwitch"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/services/{service_id}/export": {
      "get": {
        "tags": [
          "gateways"
        ],
        "summary": "Export Service",
        "description": "Export a service's tariff as a portable profile.\n\nIdentifying fields are stripped: sharing a plan should not share a meter\nnumber, an account, or a network approval that belongs to one connection.",
        "operationId": "export_service_api_services__service_id__export_get",
        "parameters": [
          {
            "name": "service_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Service Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/services/import": {
      "post": {
        "tags": [
          "gateways"
        ],
        "summary": "Import Service",
        "description": "Validate a tariff profile, and optionally apply it.\n\n``dry_run=true`` (the default) reports what would happen without touching\nanything \u2014 importing a tariff overwrites rates that price real money, so it\nshould never be a single unconfirmed click.\n\nWith ``service_id`` the profile is applied to that service, leaving its\nidentity and network grant intact. Without one, a new service is created.",
        "operationId": "import_service_api_services_import_post",
        "parameters": [
          {
            "name": "dry_run",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": true,
              "title": "Dry Run"
            }
          },
          {
            "name": "service_id",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Service Id"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TariffImportBundle"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/tariff/validate-rates": {
      "post": {
        "tags": [
          "gateways"
        ],
        "summary": "Validate Rates",
        "description": "Check a season/wave plan before it is saved.\n\nServer-side because the resolver is the authority on what it will actually\nprice \u2014 a UI that validated separately would drift from it. Returns\n``{ok, problems: [...]}`` where problems are plain sentences.",
        "operationId": "validate_rates_api_tariff_validate_rates_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RateValidateBody"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/services/{service_id}/history-count": {
      "get": {
        "tags": [
          "gateways"
        ],
        "summary": "Service History Count",
        "description": "How many closed billing periods this service produced \u2014 the UI uses it\nto offer 'retire' instead of 'delete'.",
        "operationId": "service_history_count_api_services__service_id__history_count_get",
        "parameters": [
          {
            "name": "service_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Service Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/services": {
      "get": {
        "tags": [
          "gateways"
        ],
        "summary": "List Services",
        "description": "List all electricity utility services.",
        "operationId": "list_services_api_services_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "gateways"
        ],
        "summary": "Add Service",
        "description": "Create a new utility service.",
        "operationId": "add_service_api_services_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ServiceCreate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/services/{service_id}": {
      "patch": {
        "tags": [
          "gateways"
        ],
        "summary": "Patch Service",
        "description": "Update a utility service. exclude_unset so booleans/windows can be set to\nfalse/null explicitly (e.g. clearing a tariff window).",
        "operationId": "patch_service_api_services__service_id__patch",
        "parameters": [
          {
            "name": "service_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Service Id"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ServiceUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "gateways"
        ],
        "summary": "Remove Service",
        "description": "Delete a utility service \u2014 refused once it has produced billing history.\n\nDeleting would strand closed periods against a service that no longer\nexists, which is exactly the continuity the history view is for. Retiring\n(``enabled: false``) stops it pricing anything while keeping its rows.",
        "operationId": "remove_service_api_services__service_id__delete",
        "parameters": [
          {
            "name": "service_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Service Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/gateways": {
      "get": {
        "tags": [
          "gateways"
        ],
        "summary": "List Gateways",
        "description": "List all gateways with live status from the registry.",
        "operationId": "list_gateways_api_gateways_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "gateways"
        ],
        "summary": "Add Gateway",
        "description": "Add a new gateway.",
        "operationId": "add_gateway_api_gateways_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GatewayCreate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/gateways/{gw_id}": {
      "get": {
        "tags": [
          "gateways"
        ],
        "summary": "Get Single Gateway",
        "description": "Get a single gateway with live status.",
        "operationId": "get_single_gateway_api_gateways__gw_id__get",
        "parameters": [
          {
            "name": "gw_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Gw Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "gateways"
        ],
        "summary": "Patch Gateway",
        "description": "Update gateway configuration.",
        "operationId": "patch_gateway_api_gateways__gw_id__patch",
        "parameters": [
          {
            "name": "gw_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Gw Id"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GatewayUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "gateways"
        ],
        "summary": "Remove Gateway",
        "description": "Remove a gateway. Default gateway cannot be removed.",
        "operationId": "remove_gateway_api_gateways__gw_id__delete",
        "parameters": [
          {
            "name": "gw_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Gw Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/gateways/{gw_id}/start": {
      "post": {
        "tags": [
          "gateways"
        ],
        "summary": "Start Gateway Endpoint",
        "description": "Start polling a gateway.",
        "operationId": "start_gateway_endpoint_api_gateways__gw_id__start_post",
        "parameters": [
          {
            "name": "gw_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Gw Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/gateways/{gw_id}/stop": {
      "post": {
        "tags": [
          "gateways"
        ],
        "summary": "Stop Gateway Endpoint",
        "description": "Stop polling a gateway and remember the stop across restarts.\n\nSets ``autostart=0`` so the gateway is not re-started on the next app\nboot.  ``enabled`` stays 1 \u2014 this is a user pause, not an admin disable,\nso the gateway remains configured and can be started again.  This is what\nkeeps a stopped (esp. mock) gateway from self-restarting on reboot.",
        "operationId": "stop_gateway_endpoint_api_gateways__gw_id__stop_post",
        "parameters": [
          {
            "name": "gw_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Gw Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/gateways/{gw_id}/restart": {
      "post": {
        "tags": [
          "gateways"
        ],
        "summary": "Restart Gateway Endpoint",
        "description": "Tear down and recreate a gateway's Modbus connection.\n\nUnlike /diagnose (read-only), this is the actual fix for a wedged\nsession: it discards the current GatewayInstance (closing whatever\nsocket state it had, good or bad) and reconnects from scratch, without\nneeding a full bridge process restart. ``registry.start_gateway``\nalready stops any existing instance for this ID before starting a new\none, so this is mechanically identical to /start \u2014 this endpoint exists\nfor a clear, explicit \"restart\" action in the UI/CLI regardless of\nwhether the gateway looked like it was already running.",
        "operationId": "restart_gateway_endpoint_api_gateways__gw_id__restart_post",
        "parameters": [
          {
            "name": "gw_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Gw Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/gateways/{gw_id}/healthcheck": {
      "post": {
        "tags": [
          "gateways"
        ],
        "summary": "Force Healthcheck Endpoint",
        "description": "Force an immediate TCP health probe, instead of waiting for the\nperiodic HealthChecker (default: every 60s) to get to this gateway.\n\nUpdates the same ``status.health`` field the topbar dot reads, so the\nUI reflects the result on its next refresh.",
        "operationId": "force_healthcheck_endpoint_api_gateways__gw_id__healthcheck_post",
        "parameters": [
          {
            "name": "gw_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Gw Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/gateways/{gw_id}/detect-phases": {
      "get": {
        "tags": [
          "gateways"
        ],
        "summary": "Detect Gateway Phases",
        "description": "Auto-detect the gateway's wired phase(s) from its 701 registers.\n\nReads the gateway's latest cached points (no extra Modbus traffic) and\ninfers connected/utilised phases. Also reports whether the result matches\nthe gateway's currently declared ``phase``.",
        "operationId": "detect_gateway_phases_api_gateways__gw_id__detect_phases_get",
        "parameters": [
          {
            "name": "gw_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Gw Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/gateways/{gw_id}/test": {
      "post": {
        "tags": [
          "gateways"
        ],
        "summary": "Test Gateway Tcp",
        "description": "Test TCP connectivity to a gateway's host:port.",
        "operationId": "test_gateway_tcp_api_gateways__gw_id__test_post",
        "parameters": [
          {
            "name": "gw_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Gw Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/gateways/{gw_id}/diagnose": {
      "post": {
        "tags": [
          "gateways"
        ],
        "summary": "Diagnose Gateway",
        "description": "Root-cause a Modbus connectivity problem for a gateway.\n\nRuns TCP reachability probes (Modbus port + Local API port), a live\nModbus protocol-level read reusing the poller's existing session (never\nopens a second concurrent Modbus session \u2014 see docs/vendor-issues.md on\nconcurrent-access corruption), and cross-checks the result against the\npoller's own reported state to catch cases where the poller thinks it's\nfine but isn't.",
        "operationId": "diagnose_gateway_api_gateways__gw_id__diagnose_post",
        "parameters": [
          {
            "name": "gw_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Gw Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/gateways/{gw_id}/points": {
      "get": {
        "tags": [
          "gateways"
        ],
        "summary": "Get Gateway Points",
        "description": "Get current points for a specific gateway.",
        "operationId": "get_gateway_points_api_gateways__gw_id__points_get",
        "parameters": [
          {
            "name": "gw_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Gw Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/gateways/{gw_id}/command": {
      "post": {
        "tags": [
          "gateways"
        ],
        "summary": "Send Gateway Command",
        "description": "Dispatch a control command to a specific gateway.",
        "operationId": "send_gateway_command_api_gateways__gw_id__command_post",
        "parameters": [
          {
            "name": "gw_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Gw Id"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GatewayCommandRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/gateways/{gw_id}/battery/limits": {
      "get": {
        "tags": [
          "gateways"
        ],
        "summary": "Get Gateway Battery Limits",
        "description": "Battery power limits a force command may use: trusted ratings only (#133).\n\n``max_charge_w`` / ``max_discharge_w`` are None until the rating is\ncalibrated or Model 702 is confirmed by an observed peak; no 5000 W default.",
        "operationId": "get_gateway_battery_limits_api_gateways__gw_id__battery_limits_get",
        "parameters": [
          {
            "name": "gw_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Gw Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/gateways/{gw_id}/battery-rating": {
      "get": {
        "tags": [
          "gateways"
        ],
        "summary": "Get Battery Rating",
        "description": "The evidence behind this gateway's battery power rating, per direction (#133).\n\n``trusted`` says whether force commands are allowed; ``reason`` says why\nnot and how to fix it. ``calibration`` is the window being recorded, if any.",
        "operationId": "get_battery_rating_api_gateways__gw_id__battery_rating_get",
        "parameters": [
          {
            "name": "gw_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Gw Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/gateways/{gw_id}/battery-rating/calibration": {
      "post": {
        "tags": [
          "gateways"
        ],
        "summary": "Calibrate Battery Rating",
        "description": "Record the battery's full-rate power while the owner runs a dispatch in the app.\n\nStart, then in the FranklinWH app run a TOU battery dispatch (force charge\nfrom grid, or force discharge to grid) with import and export unlimited, at\nmid SoC. Finish after at least a minute: the peak becomes the calibrated\nrating for that direction. The bridge sends nothing to the aGate here.",
        "operationId": "calibrate_battery_rating_api_gateways__gw_id__battery_rating_calibration_post",
        "parameters": [
          {
            "name": "gw_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Gw Id"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CalibrationRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/gateways/{gw_id}/dispatch-outcomes": {
      "get": {
        "tags": [
          "gateways"
        ],
        "summary": "List Dispatch Outcomes",
        "description": "Every battery command sent to this gateway and how it ended, newest first.\n\nStates: sent / failed / verified / not_effective / reasserted / released /\nrelease_unverified / superseded (design PR #57 \u00a75.1). ``requester`` says\nwho asked: ``schedule:<entry id>`` or ``manual``.",
        "operationId": "list_dispatch_outcomes_api_gateways__gw_id__dispatch_outcomes_get",
        "parameters": [
          {
            "name": "gw_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Gw Id"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 100,
              "title": "Limit"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/gateways/{gw_id}/link-stats": {
      "get": {
        "tags": [
          "gateways"
        ],
        "summary": "Get Link Stats",
        "description": "Hourly link health for one gateway: requests by kind and code path\n(outcomes, latency), connections, reconnects, rescans, overlap, sockets.\nRead-only measurement; the baseline the connection work is judged by.",
        "operationId": "get_link_stats_api_gateways__gw_id__link_stats_get",
        "parameters": [
          {
            "name": "gw_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Gw Id"
            }
          },
          {
            "name": "hours",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 2160,
              "minimum": 1,
              "default": 24,
              "title": "Hours"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LinkStatsResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/setup/state": {
      "get": {
        "tags": [
          "setup"
        ],
        "summary": "Get Setup State",
        "description": "Whether the wizard should open. Any signed-in user may ask; only admins\nare shown the wizard (the UI checks the role).",
        "operationId": "get_setup_state_api_setup_state_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "setup"
        ],
        "summary": "Post Setup State",
        "description": "Record a choice \u2014 typically ``skipped``, or ``pending`` to run it again.",
        "operationId": "post_setup_state_api_setup_state_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetupStateUpdate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/setup/subnets": {
      "get": {
        "tags": [
          "setup"
        ],
        "summary": "Get Subnets",
        "description": "Subnets to search, each with where it came from (see environment.py).\n\nAlso the networks of aGates already set up here: an aGate reached over\nTailscale or a VPN lives on a network the bridge's host isn't on, so the\nhost's own interfaces never suggest it \u2014 but a second aGate is usually on\nthe same network as the first.",
        "operationId": "get_subnets_api_setup_subnets_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      }
    },
    "/api/setup/scan": {
      "post": {
        "tags": [
          "setup"
        ],
        "summary": "Start Scan",
        "description": "Search subnets for aGates in the background; poll ``GET /scan/{id}``.",
        "operationId": "start_scan_api_setup_scan_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ScanRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/setup/scan/{scan_id}": {
      "get": {
        "tags": [
          "setup"
        ],
        "summary": "Get Scan",
        "operationId": "get_scan_api_setup_scan__scan_id__get",
        "parameters": [
          {
            "name": "scan_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Scan Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/setup/probe": {
      "post": {
        "tags": [
          "setup"
        ],
        "summary": "Probe Host",
        "description": "Identify one address (the wizard's *Enter address \u2192 Test*).",
        "operationId": "probe_host_api_setup_probe_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProbeRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/setup/connect": {
      "post": {
        "tags": [
          "setup"
        ],
        "summary": "Connect Gateway",
        "description": "Set up a real gateway at this address and start it.\n\nThe first one fills the primary gateway (internally still id ``default``,\nwhich owns the unprefixed MQTT topics existing HA setups rely on); any\nfurther ones are added alongside.",
        "operationId": "connect_gateway_api_setup_connect_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConnectRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/setup/demo": {
      "post": {
        "tags": [
          "setup"
        ],
        "summary": "Create Demo",
        "description": "Create a demo (mock) gateway.\n\nAlways a separate gateway \u2014 never the primary: the primary owns the\nunprefixed MQTT topics, and synthetic data there would land in the same HA\nentities (and long-term statistics) a real aGate uses later.\n\nNot published to Home Assistant until the user says so (#96): as an add-on\na demo used to put a \"Demo aGate\" device into the user's real HA unasked.",
        "operationId": "create_demo_api_setup_demo_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DemoRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/setup/use-ha": {
      "post": {
        "tags": [
          "setup"
        ],
        "summary": "Post Use Ha",
        "description": "Record whether this install is used with Home Assistant (#94).\n\nNot asked as an add-on, which always runs inside Home Assistant.",
        "operationId": "post_use_ha_api_setup_use_ha_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UseHaUpdate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/setup/publish-to-ha": {
      "post": {
        "tags": [
          "setup"
        ],
        "summary": "Post Publish To Ha",
        "description": "The wizard's \"Publish <gateway>'s entities to Home Assistant?\" (#96).\n\nThe same switch as ``PATCH /api/gateways/{id}`` ``publish_to_ha``, and\napplied the same way \u2014 live: off removes the entities from HA, on\npublishes them. Returns the checklist, which reflects the answer.",
        "operationId": "post_publish_to_ha_api_setup_publish_to_ha_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PublishToHaUpdate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/setup/checklist": {
      "get": {
        "tags": [
          "setup"
        ],
        "summary": "Get Checklist",
        "description": "Step 6: is everything between the bridge and Home Assistant working?\n\nEach item is ``ok`` / ``fail`` / ``unknown`` (couldn't tell \u2014 shown as a\nmanual step) / ``skipped`` (not using Home Assistant), with a ``detail``.",
        "operationId": "get_checklist_api_setup_checklist_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      }
    },
    "/api/setup/accounts": {
      "get": {
        "tags": [
          "setup"
        ],
        "summary": "Get Accounts",
        "description": "Logins on this bridge, and which still have a password anyone would\ntry first (\"admin\", the username...). The shipped compose seeds\nadmin/admin, so a new install starts there.\n\nUnder HA ingress the bridge's own logins aren't used: ``applies`` is false.",
        "operationId": "get_accounts_api_setup_accounts_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      }
    },
    "/api/modules": {
      "get": {
        "tags": [
          "modules"
        ],
        "summary": "List Modules",
        "description": "All feature modules with resolved enabled state + ``can_access`` for the\ncurrent user's role. Drives the UI nav/tab gating and the admin Modules\npanel.",
        "operationId": "list_modules_api_modules_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      }
    },
    "/api/modules/{module_id}": {
      "patch": {
        "tags": [
          "modules"
        ],
        "summary": "Toggle Module",
        "description": "Enable/disable a module (admin). Core modules can't be disabled.",
        "operationId": "toggle_module_api_modules__module_id__patch",
        "parameters": [
          {
            "name": "module_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Module Id"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ModuleToggle"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/energy/flow": {
      "get": {
        "tags": [
          "energy"
        ],
        "summary": "Energy Flow",
        "description": "Directional energy flows for a day (default today) or an explicit span.\n\n``day`` is the common case and what the dashboards use. ``start``/``end``\nepoch seconds cover week/month/year views without a second endpoint.",
        "operationId": "energy_flow_api_energy_flow_get",
        "parameters": [
          {
            "name": "day",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Day"
            }
          },
          {
            "name": "start",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "number"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Start"
            }
          },
          {
            "name": "end",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "number"
                },
                {
                  "type": "null"
                }
              ],
              "title": "End"
            }
          },
          {
            "name": "gateway",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "default",
              "title": "Gateway"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/energy/battery-hours": {
      "get": {
        "tags": [
          "energy"
        ],
        "summary": "Battery Hours",
        "description": "Per-hour battery charge/discharge for a local day (default today).\n\nFeeds the Solar Forecast card's charging/discharging band. Integrated from\nthe sampled ``battery_w`` series (sign-split, so a mixed hour reports both\ndirections) \u2014 an estimate from sampled power, not a meter reading.\nHours after now carry ``state: null``.",
        "operationId": "battery_hours_api_energy_battery_hours_get",
        "parameters": [
          {
            "name": "day",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Day"
            }
          },
          {
            "name": "gateway",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "default",
              "title": "Gateway"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BatteryHoursResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/energy/totals": {
      "get": {
        "tags": [
          "energy"
        ],
        "summary": "Energy Totals Endpoint",
        "description": "Lifetime and period kWh, straight from the aGate's own counters.\n\nMeasured, not reconstructed \u2014 these are the figures to trust when they\ndisagree with the Sankey's arcs.\n\nProjected out of the same sensor snapshot the Automation Builder and the\nschedule engine read, rather than re-deriving the counters here. A second\nderivation is a second thing to drift: the whole point of these being the\ntrustworthy numbers is that there is exactly one of them.",
        "operationId": "energy_totals_endpoint_api_energy_totals_get",
        "parameters": [
          {
            "name": "gateway",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "default",
              "title": "Gateway"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/point-history/config": {
      "get": {
        "tags": [
          "point-history"
        ],
        "summary": "Get Config",
        "description": "Current recording settings, plus the catalogue a client can offer.",
        "operationId": "get_config_api_point_history_config_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "point-history"
        ],
        "summary": "Put Config",
        "description": "Update recording settings.\n\nTakes effect on the next poll tick \u2014 the loop re-reads this each pass rather\nthan caching it, so there is nothing to restart. Shrinking retention does\nnot delete anything here; the purge pass applies it on its own schedule.",
        "operationId": "put_config_api_point_history_config_put",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConfigPatch"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/point-history/series": {
      "get": {
        "tags": [
          "point-history"
        ],
        "summary": "Get Series",
        "description": "Historical series for a comma-separated ``points`` list.\n\n``bucket_s`` is chosen automatically from the span unless given. The\nresponse echoes the resolved span and bucket so a caller can label the\naxis with what it actually got rather than what it asked for.",
        "operationId": "get_series_api_point_history_series_get",
        "parameters": [
          {
            "name": "points",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Points"
            }
          },
          {
            "name": "day",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Day"
            }
          },
          {
            "name": "start",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "number"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Start"
            }
          },
          {
            "name": "end",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "number"
                },
                {
                  "type": "null"
                }
              ],
              "title": "End"
            }
          },
          {
            "name": "bucket_s",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "number"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Bucket S"
            }
          },
          {
            "name": "gateway",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "default",
              "title": "Gateway"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/point-history/storage": {
      "get": {
        "tags": [
          "point-history"
        ],
        "summary": "Get Storage",
        "description": "What point history costs now, and what a candidate config would cost.\n\nThe projection query params let Settings show the consequence of a change\nwhile the user is still deciding, without saving it first.",
        "operationId": "get_storage_api_point_history_storage_get",
        "parameters": [
          {
            "name": "points",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "integer"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Points"
            }
          },
          {
            "name": "interval_s",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "number"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Interval S"
            }
          },
          {
            "name": "retention_days",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "number"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Retention Days"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/solar/forecast": {
      "get": {
        "tags": [
          "solar"
        ],
        "summary": "Solar Forecast",
        "description": "Weather + PV production forecast. Needs latitude/longitude set; otherwise\n``{configured: false, reason}``. Cached 15 min; ``force`` refetches.",
        "operationId": "solar_forecast_api_solar_forecast_get",
        "parameters": [
          {
            "name": "force",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false,
              "title": "Force"
            }
          },
          {
            "name": "gateway",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "default",
              "title": "Gateway"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/solar/settings": {
      "get": {
        "tags": [
          "solar"
        ],
        "summary": "Get Solar Settings",
        "description": "Location + PV array settings for the Settings form.",
        "operationId": "get_solar_settings_api_solar_settings_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SolarSettingsResponse"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "solar"
        ],
        "summary": "Put Solar Settings",
        "description": "Update the location / PV array (only the fields sent).",
        "operationId": "put_solar_settings_api_solar_settings_put",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SolarSettingsBody"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SolarSettingsResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/solar/location/from-ha": {
      "post": {
        "tags": [
          "solar"
        ],
        "summary": "Location From Ha",
        "description": "Use a configured Home Assistant's home location (its ``get_config``\nlatitude/longitude) as the forecast location, and save it.",
        "operationId": "location_from_ha_api_solar_location_from_ha_post",
        "parameters": [
          {
            "name": "instance",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Instance"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SolarSettingsResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/tariff/spot": {
      "get": {
        "tags": [
          "tariff"
        ],
        "summary": "Tariff Spot",
        "description": "Live wholesale price: the AEMO NEM spot for ``region`` (default: the\nconfigured one) and the configured HA price entities, plus the ACTIVE\nbuy/sell (HA wins per side over NEM). No region and nothing configured ->\n``spot: null``, and nothing is fetched.",
        "operationId": "tariff_spot_api_tariff_spot_get",
        "parameters": [
          {
            "name": "region",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Region"
            }
          },
          {
            "name": "force",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false,
              "title": "Force"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/tariff/spot/settings": {
      "get": {
        "tags": [
          "tariff"
        ],
        "summary": "Get Spot Settings",
        "description": "NEM region + HA price entities, for the Settings form.",
        "operationId": "get_spot_settings_api_tariff_spot_settings_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SpotSettingsResponse"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "tariff"
        ],
        "summary": "Put Spot Settings",
        "description": "Update the NEM region / HA price entities (only the fields sent).",
        "operationId": "put_spot_settings_api_tariff_spot_settings_put",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SpotSettingsBody"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SpotSettingsResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/tariff/dynamic": {
      "get": {
        "tags": [
          "tariff"
        ],
        "summary": "Tariff Dynamic",
        "description": "The live price a dynamic plan is (or would be) priced at.\n\nWith no ``source``: the ACTIVE plan's provider and the live price billing\nis using now (``live`` is null for a static plan). With ``source`` (``nem``\n+ ``region``, or ``ha`` + ``buy_entity`` [+ ``feedin_entity``]): a preview\nfor the tariff editor, before anything is saved. Invalid input -> 422.",
        "operationId": "tariff_dynamic_api_tariff_dynamic_get",
        "parameters": [
          {
            "name": "source",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Source"
            }
          },
          {
            "name": "region",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Region"
            }
          },
          {
            "name": "buy_entity",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Buy Entity"
            }
          },
          {
            "name": "feedin_entity",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Feedin Entity"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DynamicTariffResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/disclaimer": {
      "get": {
        "tags": [
          "disclaimer"
        ],
        "summary": "Get Disclaimer",
        "description": "The notice text, and whether this user has already accepted this version.\n\nReturns the text as well as the flag so the UI never keeps its own copy of\nthe wording \u2014 one source, and a version bump can't leave a stale paragraph\nrendered next to a fresh version number.",
        "operationId": "get_disclaimer_api_disclaimer_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "disclaimer"
        ],
        "summary": "Ack Disclaimer",
        "description": "Record acceptance. Declining is answered honestly rather than stored.",
        "operationId": "ack_disclaimer_api_disclaimer_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AckRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/disclaimer/acks": {
      "get": {
        "tags": [
          "disclaimer"
        ],
        "summary": "List Acks",
        "description": "The acknowledgement trail. Admin-only \u2014 it is a record of who agreed to\nwhat, which is nobody else's business.",
        "operationId": "list_acks_api_disclaimer_acks_get",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 200,
              "title": "Limit"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/users": {
      "get": {
        "tags": [
          "users"
        ],
        "summary": "List Users",
        "operationId": "list_users_api_users_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "users"
        ],
        "summary": "Create New User",
        "operationId": "create_new_user_api_users_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UserCreate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/users/{user_id}": {
      "patch": {
        "tags": [
          "users"
        ],
        "summary": "Update Existing User",
        "operationId": "update_existing_user_api_users__user_id__patch",
        "parameters": [
          {
            "name": "user_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "User Id"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UserUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "users"
        ],
        "summary": "Remove User",
        "operationId": "remove_user_api_users__user_id__delete",
        "parameters": [
          {
            "name": "user_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "User Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/support/info": {
      "get": {
        "tags": [
          "support"
        ],
        "summary": "Get Support Info",
        "description": "Redacted summary of this install, as JSON and as Markdown.\n\nNo serials, IPs, host names, ports, MAC addresses, location, usernames,\nemails, HA URLs or tokens, passwords, MQTT credentials or client id, or\ndevice names. Log messages are scrubbed by pattern \u2014 review before posting.",
        "operationId": "get_support_info_api_support_info_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SupportInfo"
                }
              }
            }
          }
        }
      }
    },
    "/api/schedules": {
      "get": {
        "tags": [
          "schedules"
        ],
        "summary": "List Schedules",
        "description": "List all schedule entries with active-now / next-fire previews.",
        "operationId": "list_schedules_api_schedules_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "schedules"
        ],
        "summary": "Add Schedule",
        "description": "Create a schedule entry.",
        "operationId": "add_schedule_api_schedules_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ScheduleCreate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/schedules/export": {
      "get": {
        "tags": [
          "schedules"
        ],
        "summary": "Export Schedules",
        "description": "Export schedules as a portable JSON bundle. ``ids`` = comma-separated\nentry ids (default: all).",
        "operationId": "export_schedules_api_schedules_export_get",
        "parameters": [
          {
            "name": "ids",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Ids"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/schedules/import": {
      "post": {
        "tags": [
          "schedules"
        ],
        "summary": "Import Schedules",
        "description": "Validate (and optionally create) an imported bundle. ``dry_run=true``\n(default) returns a per-entry validation report without creating anything;\n``dry_run=false`` creates the valid entries DISABLED (for review), with any\nunknown gateway target falling back to Default.",
        "operationId": "import_schedules_api_schedules_import_post",
        "parameters": [
          {
            "name": "dry_run",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": true,
              "title": "Dry Run"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ImportBundle"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/schedules/log": {
      "get": {
        "tags": [
          "schedules"
        ],
        "summary": "Schedule Log",
        "description": "Recent schedule dispatch/audit events (newest first).\n\nOptional filters: ``schedule_id`` (one entry's runs) and ``status`` (the\naudit result: fired/gated/missed/executed/exit_condition_met/\u2026) \u2014 this backs\nthe FWHAI audit + history views.",
        "operationId": "schedule_log_api_schedules_log_get",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 100,
              "title": "Limit"
            }
          },
          {
            "name": "schedule_id",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Schedule Id"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Status"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/schedules/{schedule_id}/execute": {
      "post": {
        "tags": [
          "schedules"
        ],
        "summary": "Execute Schedule",
        "description": "Fire a schedule entry's action now (respects entry_conditions unless\n``?force=true``). The dispatch auto-releases after the entry's duration.",
        "operationId": "execute_schedule_api_schedules__schedule_id__execute_post",
        "parameters": [
          {
            "name": "schedule_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Schedule Id"
            }
          },
          {
            "name": "force",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false,
              "title": "Force"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/schedules/{schedule_id}/stop": {
      "post": {
        "tags": [
          "schedules"
        ],
        "summary": "Stop Schedule",
        "description": "Gracefully stop a schedule's CURRENT run: release the live dispatch it\nowns and skip re-firing until the next scheduled window. The entry stays\nenabled (disable it to stop permanently).",
        "operationId": "stop_schedule_api_schedules__schedule_id__stop_post",
        "parameters": [
          {
            "name": "schedule_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Schedule Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/schedules/timeline": {
      "get": {
        "tags": [
          "schedules"
        ],
        "summary": "Schedule Timeline",
        "description": "Return the day's windows per entry for the visual timeline.\n\n``day`` is an optional weekday (0=Mon..6=Sun) to preview; defaults to today.\nEach segment is minutes-from-midnight so the UI can lay out a 24h bar.",
        "operationId": "schedule_timeline_api_schedules_timeline_get",
        "parameters": [
          {
            "name": "day",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "integer"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Day"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/schedules/{schedule_id}": {
      "get": {
        "tags": [
          "schedules"
        ],
        "summary": "Get Single Schedule",
        "description": "Get one schedule entry.",
        "operationId": "get_single_schedule_api_schedules__schedule_id__get",
        "parameters": [
          {
            "name": "schedule_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Schedule Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "schedules"
        ],
        "summary": "Patch Schedule",
        "description": "Update a schedule entry.\n\nUses ``exclude_unset`` so we apply exactly the fields the client sent \u2014\nincluding explicit nulls. A previous ``if v is not None`` filter silently\ndropped nulls, which made it impossible to CLEAR a field: deleting all\nexit_conditions (client sends ``exit_conditions: null``) never persisted.\n``update_schedule`` stores None as SQL NULL for the nullable JSON columns.",
        "operationId": "patch_schedule_api_schedules__schedule_id__patch",
        "parameters": [
          {
            "name": "schedule_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Schedule Id"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ScheduleUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "schedules"
        ],
        "summary": "Remove Schedule",
        "description": "Delete a schedule entry.",
        "operationId": "remove_schedule_api_schedules__schedule_id__delete",
        "parameters": [
          {
            "name": "schedule_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Schedule Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/schedule/actions": {
      "get": {
        "tags": [
          "schedules"
        ],
        "summary": "List Schedule Actions",
        "description": "The command vocabulary a schedule entry can dispatch (for the UI form).",
        "operationId": "list_schedule_actions_api_schedule_actions_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      }
    },
    "/api/sensors": {
      "get": {
        "tags": [
          "scheduler"
        ],
        "summary": "List Sensors",
        "description": "Sensor namespace + current values for the condition-builder dropdowns.\n\nIncludes the static Bridge sensors plus any HA entities (``ha:<inst>:<entity>``)\nso the Automation Builder can condition on Home Assistant state.",
        "operationId": "list_sensors_api_sensors_get",
        "parameters": [
          {
            "name": "gateway",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "default",
              "title": "Gateway"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/automation/constants": {
      "get": {
        "tags": [
          "scheduler"
        ],
        "summary": "Get Constants",
        "description": "User-defined automation constants (min/max/demand SoC) + their [min,max]\nbounds for the settings form. Surfaced as ``const.*`` sensors too.",
        "operationId": "get_constants_api_automation_constants_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "scheduler"
        ],
        "summary": "Put Constants",
        "description": "Update automation constants (only sent fields; each clamped to [0,100]).",
        "operationId": "put_constants_api_automation_constants_put",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConstantsBody"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/scheduler/evaluate": {
      "post": {
        "tags": [
          "scheduler"
        ],
        "summary": "Evaluate Conditions",
        "description": "Evaluate a condition tree against the gateway's live snapshot.\n\nReturns ``{result: bool, per_condition: [{sensor, op, value, live_value,\nresult}]}`` \u2014 the Test Verification response.",
        "operationId": "evaluate_conditions_api_scheduler_evaluate_post",
        "parameters": [
          {
            "name": "gateway",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "default",
              "title": "Gateway"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConditionTreeBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/ha/instances": {
      "get": {
        "tags": [
          "home-assistant"
        ],
        "summary": "List Instances",
        "description": "Configured HA instances (tokens redacted) + live connection status.",
        "operationId": "list_instances_api_ha_instances_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "home-assistant"
        ],
        "summary": "Create Instance",
        "operationId": "create_instance_api_ha_instances_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HaInstanceCreate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/ha/instances/{ha_id}": {
      "patch": {
        "tags": [
          "home-assistant"
        ],
        "summary": "Patch Instance",
        "operationId": "patch_instance_api_ha_instances__ha_id__patch",
        "parameters": [
          {
            "name": "ha_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Ha Id"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HaInstanceUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "home-assistant"
        ],
        "summary": "Remove Instance",
        "operationId": "remove_instance_api_ha_instances__ha_id__delete",
        "parameters": [
          {
            "name": "ha_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Ha Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/ha/test": {
      "post": {
        "tags": [
          "home-assistant"
        ],
        "summary": "Test Connection",
        "description": "Probe a base_url/token WITHOUT saving \u2014 powers the config form's Test button.\n\nOn an edit the token field is blank; if ``ha_id`` is given we fall back to\nthat instance's stored token so Test reflects the real (saved) credentials\ninstead of failing 401 with no auth.\n\nReturns ``{connected, entity_count, last_error}``.",
        "operationId": "test_connection_api_ha_test_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HaTestBody"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/ha/notify-targets": {
      "get": {
        "tags": [
          "home-assistant"
        ],
        "summary": "Notify Targets",
        "description": "Available ``notify.*`` services across all HA instances.\n\nNotify targets are services, not entities, so they never appear in the\nentity browser \u2014 the action picker needs them from here.",
        "operationId": "notify_targets_api_ha_notify_targets_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      }
    },
    "/api/ha/entities": {
      "get": {
        "tags": [
          "home-assistant"
        ],
        "summary": "Browse Entities",
        "description": "Browse ALL HA entities across instances (with an ``exposed`` flag),\nfiltered + paginated \u2014 backs the HA Entities tab.\n\nFilters: ``instance`` (id), ``domain``, ``search`` (substring of entity_id or\nfriendly name), ``exposed`` (true/false). Returns\n``{total, page, page_size, exposed_count, entities:[...]}``.\n\n``search`` accepts **comma-separated terms matched as OR** \u2014 real\ninstallations name the same concept a dozen ways (\"pv\", \"solar\",\n\"enphase\", \"envoy\"), so a single substring misses most of a topic. A\nplain term without commas behaves exactly as before.",
        "operationId": "browse_entities_api_ha_entities_get",
        "parameters": [
          {
            "name": "instance",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Instance"
            }
          },
          {
            "name": "domain",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Domain"
            }
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Search"
            }
          },
          {
            "name": "exposed",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "boolean"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Exposed"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 1,
              "title": "Page"
            }
          },
          {
            "name": "page_size",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 50,
              "title": "Page Size"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/ha/domains": {
      "get": {
        "tags": [
          "home-assistant"
        ],
        "summary": "List Domains",
        "description": "Distinct entity domains across all instances (for the filter dropdown).",
        "operationId": "list_domains_api_ha_domains_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      }
    },
    "/api/ha/entities/expose": {
      "post": {
        "tags": [
          "home-assistant"
        ],
        "summary": "Expose Entity",
        "description": "Add/remove an entity from an instance's allowlist (persists + applies live).",
        "operationId": "expose_entity_api_ha_entities_expose_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HaExposeBody"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/ha/dashboard-yaml": {
      "get": {
        "tags": [
          "home-assistant"
        ],
        "summary": "Dashboard Yaml",
        "description": "Copy-paste HA dashboard card YAML for this bridge's entities (#86).\n\nEntity ids come only from the chosen HA instance's device + entity\nregistries \u2014 never predicted. Returns ``{yaml, entity_count, devices,\ninstance, layout}``; 409/503 with a ``detail`` (and no YAML) when the\nregistry can't be read or holds none of our entities.",
        "operationId": "dashboard_yaml_api_ha_dashboard_yaml_get",
        "parameters": [
          {
            "name": "instance",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Instance"
            }
          },
          {
            "name": "layout",
            "in": "query",
            "required": false,
            "schema": {
              "enum": [
                "grouped",
                "single"
              ],
              "type": "string",
              "default": "grouped",
              "title": "Layout"
            }
          },
          {
            "name": "diagnostics",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false,
              "title": "Diagnostics"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/tariff/overview": {
      "get": {
        "tags": [
          "tariff"
        ],
        "summary": "Tariff Overview",
        "description": "Setup config + live period values + linked automations for the tab.",
        "operationId": "tariff_overview_api_tariff_overview_get",
        "parameters": [
          {
            "name": "gateway",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "default",
              "title": "Gateway"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/tariff/history": {
      "get": {
        "tags": [
          "tariff"
        ],
        "summary": "Tariff History",
        "description": "Closed billing periods (reporting/history), most-recent first.\n\nEach period carries the plan(s) in force during it, so a period that\nstraddles a retailer switch can be flagged rather than silently reported\nunder one retailer's name.",
        "operationId": "tariff_history_api_tariff_history_get",
        "parameters": [
          {
            "name": "gateway",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "default",
              "title": "Gateway"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 36,
              "title": "Limit"
            }
          },
          {
            "name": "service",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Service"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/tariff/plans": {
      "get": {
        "tags": [
          "tariff"
        ],
        "summary": "Tariff Plans",
        "description": "Plan history for a service, oldest first.",
        "operationId": "tariff_plans_api_tariff_plans_get",
        "parameters": [
          {
            "name": "service",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Service"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "tariff"
        ],
        "summary": "Create Tariff Plan",
        "description": "Record a plan change, closing the plan that was in force at that date.\n\nInserting *between* two existing plans is supported \u2014 that is what recording\nhistory you did not capture at the time means.",
        "operationId": "create_tariff_plan_api_tariff_plans_post",
        "parameters": [
          {
            "name": "service",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Service"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PlanChange"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/tariff/plans/{plan_id}": {
      "patch": {
        "tags": [
          "tariff"
        ],
        "summary": "Patch Tariff Plan",
        "description": "Correct a plan's start date.\n\nNeeded because a backfilled plan starts at the placeholder migration 45 had\nnothing better to use, leaving no earlier instant at which to insert a\npredecessor. The real start has to be settable *first*, then the earlier\nretailer can be recorded before it.",
        "operationId": "patch_tariff_plan_api_tariff_plans__plan_id__patch",
        "parameters": [
          {
            "name": "plan_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Plan Id"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PlanStartCorrection"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/tariff/history.csv": {
      "get": {
        "tags": [
          "tariff"
        ],
        "summary": "Tariff History Csv",
        "description": "Closed billing periods as a CSV download (chronological).",
        "operationId": "tariff_history_csv_api_tariff_history_csv_get",
        "parameters": [
          {
            "name": "gateway",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "default",
              "title": "Gateway"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/": {
      "get": {
        "tags": [
          "ui"
        ],
        "summary": "Index",
        "description": "Serve the main SPA shell. Unauthenticated \u2192 /login; non-admin \u2192 /user;\nan api account gets the \"REST API only\" page.",
        "operationId": "index__get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/user": {
      "get": {
        "tags": [
          "ui"
        ],
        "summary": "User Dashboard",
        "description": "The simplified, mobile-first end-user dashboard. Any signed-in role may\nview it (admins can preview it here); unauthenticated \u2192 /login.",
        "operationId": "user_dashboard_user_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/login": {
      "get": {
        "tags": [
          "ui"
        ],
        "summary": "Login Page",
        "description": "The login form. Already-authenticated users go to their role's home.",
        "operationId": "login_page_login_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/api/command": {
      "post": {
        "tags": [
          "ui"
        ],
        "summary": "Send Command",
        "description": "Dispatch a control command (same path as MQTT commands).\n\nDefaults to the \"default\" gateway. Use POST /api/gateways/{gw_id}/command\nfor gateway-specific commands (added in MG-3).",
        "operationId": "send_command_api_command_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CommandRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/ui/connection-outage": {
      "post": {
        "tags": [
          "ui"
        ],
        "summary": "Report Connection Outage",
        "description": "Log an outage the browser saw (it couldn't reach the bridge).",
        "operationId": "report_connection_outage_api_ui_connection_outage_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConnectionOutage"
              }
            }
          },
          "required": true
        },
        "responses": {
          "204": {
            "description": "Successful Response"
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "AckRequest": {
        "properties": {
          "agreed": {
            "type": "boolean",
            "title": "Agreed",
            "default": true
          }
        },
        "type": "object",
        "title": "AckRequest",
        "description": "``agreed`` false is a real answer, not a no-op.\n\nThe modal's Continue button is enabled either way: refusing to let someone\nclose a dialog is not consent, it is a hostage situation. Declining simply\nisn't recorded, so the notice comes back next time \u2014 which is the behaviour\nasked for, and is honest about what did and didn't happen."
      },
      "BackupCreateRequest": {
        "properties": {
          "label": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Label"
          }
        },
        "type": "object",
        "title": "BackupCreateRequest"
      },
      "BackupRestoreRequest": {
        "properties": {
          "name": {
            "type": "string",
            "title": "Name"
          }
        },
        "type": "object",
        "required": [
          "name"
        ],
        "title": "BackupRestoreRequest"
      },
      "BatteryHour": {
        "properties": {
          "start": {
            "type": "number",
            "title": "Start",
            "description": "Epoch seconds, inclusive"
          },
          "end": {
            "type": "number",
            "title": "End",
            "description": "Epoch seconds, exclusive"
          },
          "charge_kwh": {
            "type": "number",
            "title": "Charge Kwh"
          },
          "discharge_kwh": {
            "type": "number",
            "title": "Discharge Kwh"
          },
          "covered_s": {
            "type": "number",
            "title": "Covered S",
            "description": "Seconds of the hour that had samples"
          },
          "state": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "charge",
                  "discharge",
                  "idle"
                ]
              },
              {
                "type": "null"
              }
            ],
            "title": "State",
            "description": "Dominant direction; null = no samples (future hour or outage)"
          }
        },
        "type": "object",
        "required": [
          "start",
          "end",
          "charge_kwh",
          "discharge_kwh",
          "covered_s",
          "state"
        ],
        "title": "BatteryHour",
        "description": "One hour of battery activity, integrated from sampled ``battery_w``."
      },
      "BatteryHoursResponse": {
        "properties": {
          "day": {
            "type": "string",
            "title": "Day"
          },
          "start": {
            "type": "number",
            "title": "Start"
          },
          "end": {
            "type": "number",
            "title": "End"
          },
          "gateway_id": {
            "type": "string",
            "title": "Gateway Id"
          },
          "idle_kwh": {
            "type": "number",
            "title": "Idle Kwh",
            "description": "Below this per hour an hour counts as idle"
          },
          "basis": {
            "type": "string",
            "title": "Basis"
          },
          "hours": {
            "items": {
              "$ref": "#/components/schemas/BatteryHour"
            },
            "type": "array",
            "title": "Hours"
          }
        },
        "type": "object",
        "required": [
          "day",
          "start",
          "end",
          "gateway_id",
          "idle_kwh",
          "basis",
          "hours"
        ],
        "title": "BatteryHoursResponse"
      },
      "Body_import_metrics_endpoint_api_metrics_import_post": {
        "properties": {
          "file": {
            "type": "string",
            "contentMediaType": "application/octet-stream",
            "title": "File"
          }
        },
        "type": "object",
        "required": [
          "file"
        ],
        "title": "Body_import_metrics_endpoint_api_metrics_import_post"
      },
      "CalibrationRequest": {
        "properties": {
          "action": {
            "type": "string",
            "pattern": "^(start|finish|cancel)$",
            "title": "Action"
          },
          "direction": {
            "anyOf": [
              {
                "type": "string",
                "pattern": "^(charge|discharge)$"
              },
              {
                "type": "null"
              }
            ],
            "title": "Direction"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "action"
        ],
        "title": "CalibrationRequest"
      },
      "CommandRequest": {
        "properties": {
          "slug": {
            "type": "string",
            "title": "Slug"
          },
          "value": {
            "type": "string",
            "title": "Value"
          }
        },
        "type": "object",
        "required": [
          "slug",
          "value"
        ],
        "title": "CommandRequest"
      },
      "ConditionTreeBody": {
        "properties": {
          "match": {
            "type": "string",
            "pattern": "^(ALL|ANY|all|any)$",
            "title": "Match",
            "default": "ALL"
          },
          "conditions": {
            "items": {
              "additionalProperties": true,
              "type": "object"
            },
            "type": "array",
            "title": "Conditions"
          }
        },
        "type": "object",
        "title": "ConditionTreeBody"
      },
      "ConfigPatch": {
        "properties": {
          "enabled": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Enabled"
          },
          "points": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Points"
          },
          "interval_s": {
            "anyOf": [
              {
                "type": "integer",
                "maximum": 3600.0,
                "minimum": 5.0
              },
              {
                "type": "null"
              }
            ],
            "title": "Interval S"
          },
          "retention_days": {
            "anyOf": [
              {
                "type": "integer",
                "maximum": 365.0,
                "minimum": 1.0
              },
              {
                "type": "null"
              }
            ],
            "title": "Retention Days"
          }
        },
        "type": "object",
        "title": "ConfigPatch",
        "description": "Partial update. Absent fields keep their current value.\n\nBounds are enforced in :func:`point_history.get_config` as well \u2014 these are\nhere so a bad request is a 422 with a field name rather than a silent clamp."
      },
      "ConfigUpdate": {
        "properties": {
          "value": {
            "title": "Value"
          }
        },
        "type": "object",
        "required": [
          "value"
        ],
        "title": "ConfigUpdate"
      },
      "ConnectRequest": {
        "properties": {
          "host": {
            "type": "string",
            "maxLength": 255,
            "minLength": 1,
            "title": "Host"
          },
          "port": {
            "type": "integer",
            "maximum": 65535.0,
            "minimum": 1.0,
            "title": "Port",
            "default": 502
          },
          "unit_id": {
            "type": "integer",
            "maximum": 247.0,
            "minimum": 1.0,
            "title": "Unit Id",
            "default": 1
          },
          "name": {
            "type": "string",
            "maxLength": 120,
            "minLength": 1,
            "title": "Name"
          },
          "device_type": {
            "type": "string",
            "pattern": "^(agate|mac1)$",
            "title": "Device Type",
            "default": "agate"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "host",
          "name"
        ],
        "title": "ConnectRequest"
      },
      "ConnectionOutage": {
        "properties": {
          "started_at": {
            "type": "number",
            "minimum": 0.0,
            "title": "Started At"
          },
          "ended_at": {
            "type": "number",
            "minimum": 0.0,
            "title": "Ended At"
          },
          "failures": {
            "type": "integer",
            "maximum": 100000.0,
            "minimum": 0.0,
            "title": "Failures",
            "default": 0
          },
          "message": {
            "type": "string",
            "maxLength": 300,
            "title": "Message",
            "default": ""
          },
          "url": {
            "type": "string",
            "maxLength": 300,
            "title": "Url",
            "default": ""
          },
          "device_offline": {
            "type": "boolean",
            "title": "Device Offline",
            "default": false
          },
          "page": {
            "type": "string",
            "maxLength": 40,
            "title": "Page",
            "default": ""
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "started_at",
          "ended_at"
        ],
        "title": "ConnectionOutage"
      },
      "ConstantsBody": {
        "properties": {
          "min_discharge_soc": {
            "anyOf": [
              {
                "type": "number",
                "maximum": 100.0,
                "minimum": 0.0
              },
              {
                "type": "null"
              }
            ],
            "title": "Min Discharge Soc"
          },
          "max_charge_soc": {
            "anyOf": [
              {
                "type": "number",
                "maximum": 100.0,
                "minimum": 0.0
              },
              {
                "type": "null"
              }
            ],
            "title": "Max Charge Soc"
          },
          "demand_charge_min_soc": {
            "anyOf": [
              {
                "type": "number",
                "maximum": 100.0,
                "minimum": 0.0
              },
              {
                "type": "null"
              }
            ],
            "title": "Demand Charge Min Soc"
          }
        },
        "type": "object",
        "title": "ConstantsBody"
      },
      "DemoRequest": {
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 120,
            "minLength": 1,
            "title": "Name",
            "default": "Demo aGate"
          },
          "ac_type": {
            "type": "integer",
            "maximum": 2.0,
            "minimum": 0.0,
            "title": "Ac Type",
            "default": 0
          }
        },
        "additionalProperties": false,
        "type": "object",
        "title": "DemoRequest"
      },
      "DynamicLive": {
        "properties": {
          "buy_c_kwh": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Buy C Kwh"
          },
          "sell_c_kwh": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Sell C Kwh"
          },
          "buy": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Buy",
            "description": "$/kWh"
          },
          "sell": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Sell",
            "description": "$/kWh"
          },
          "source": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Source"
          },
          "sell_source": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Sell Source"
          },
          "label": {
            "type": "string",
            "title": "Label",
            "default": ""
          },
          "settlement_ts": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Settlement Ts"
          },
          "stale": {
            "type": "boolean",
            "title": "Stale",
            "default": false
          }
        },
        "type": "object",
        "title": "DynamicLive"
      },
      "DynamicTariffResponse": {
        "properties": {
          "kind": {
            "type": "string",
            "title": "Kind",
            "description": "the ACTIVE plan's pricing kind: static|dynamic"
          },
          "dynamic": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Dynamic",
            "description": "the provider shown (the preview's, else the active plan's)"
          },
          "preview": {
            "type": "boolean",
            "title": "Preview",
            "default": false
          },
          "live": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/DynamicLive"
              },
              {
                "type": "null"
              }
            ]
          },
          "regions": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Regions"
          },
          "ha_options": {
            "items": {
              "additionalProperties": true,
              "type": "object"
            },
            "type": "array",
            "title": "Ha Options"
          },
          "note": {
            "type": "string",
            "title": "Note",
            "default": "Energy is priced at the live rate. Demand, export-bonus, export-charge and standing charges still apply on top. With no live price for a slice, that energy is recorded as unpriced."
          }
        },
        "type": "object",
        "required": [
          "kind",
          "regions",
          "ha_options"
        ],
        "title": "DynamicTariffResponse"
      },
      "GatewayCommandRequest": {
        "properties": {
          "slug": {
            "type": "string",
            "title": "Slug"
          },
          "value": {
            "type": "string",
            "title": "Value"
          }
        },
        "type": "object",
        "required": [
          "slug",
          "value"
        ],
        "title": "GatewayCommandRequest"
      },
      "GatewayCreate": {
        "properties": {
          "gateway_id": {
            "type": "string",
            "maxLength": 63,
            "minLength": 1,
            "title": "Gateway Id"
          },
          "name": {
            "type": "string",
            "maxLength": 120,
            "minLength": 1,
            "title": "Name"
          },
          "host": {
            "type": "string",
            "maxLength": 255,
            "title": "Host",
            "default": ""
          },
          "port": {
            "type": "integer",
            "maximum": 65535.0,
            "minimum": 1.0,
            "title": "Port",
            "default": 502
          },
          "unit_id": {
            "type": "integer",
            "maximum": 247.0,
            "minimum": 1.0,
            "title": "Unit Id",
            "default": 1
          },
          "description": {
            "type": "string",
            "title": "Description",
            "default": ""
          },
          "poll_interval": {
            "type": "integer",
            "maximum": 300.0,
            "minimum": 1.0,
            "title": "Poll Interval",
            "default": 10
          },
          "timeout": {
            "type": "number",
            "maximum": 60.0,
            "minimum": 1.0,
            "title": "Timeout",
            "default": 10.0
          },
          "mock": {
            "type": "boolean",
            "title": "Mock",
            "default": false
          },
          "device_type": {
            "type": "string",
            "pattern": "^(agate|mac1)$",
            "title": "Device Type",
            "default": "agate"
          },
          "home_load_source": {
            "anyOf": [
              {
                "type": "string",
                "pattern": "^(standard|high_res)$"
              },
              {
                "type": "null"
              }
            ],
            "title": "Home Load Source"
          },
          "publish_to_ha": {
            "type": "boolean",
            "title": "Publish To Ha",
            "default": true
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "gateway_id",
          "name"
        ],
        "title": "GatewayCreate"
      },
      "GatewayUpdate": {
        "properties": {
          "name": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 120,
                "minLength": 1
              },
              {
                "type": "null"
              }
            ],
            "title": "Name"
          },
          "host": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Host"
          },
          "port": {
            "anyOf": [
              {
                "type": "integer",
                "maximum": 65535.0,
                "minimum": 1.0
              },
              {
                "type": "null"
              }
            ],
            "title": "Port"
          },
          "unit_id": {
            "anyOf": [
              {
                "type": "integer",
                "maximum": 247.0,
                "minimum": 1.0
              },
              {
                "type": "null"
              }
            ],
            "title": "Unit Id"
          },
          "description": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Description"
          },
          "poll_interval": {
            "anyOf": [
              {
                "type": "integer",
                "maximum": 300.0,
                "minimum": 1.0
              },
              {
                "type": "null"
              }
            ],
            "title": "Poll Interval"
          },
          "timeout": {
            "anyOf": [
              {
                "type": "number",
                "maximum": 60.0,
                "minimum": 1.0
              },
              {
                "type": "null"
              }
            ],
            "title": "Timeout"
          },
          "enabled": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Enabled"
          },
          "service_id": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 63
              },
              {
                "type": "null"
              }
            ],
            "title": "Service Id"
          },
          "phase": {
            "anyOf": [
              {
                "type": "string",
                "pattern": "^(all|L[123](\\+L[123])*)$"
              },
              {
                "type": "null"
              }
            ],
            "title": "Phase"
          },
          "phase_view": {
            "anyOf": [
              {
                "type": "string",
                "pattern": "^(both|aggregate|per_phase)$"
              },
              {
                "type": "null"
              }
            ],
            "title": "Phase View"
          },
          "device_type": {
            "anyOf": [
              {
                "type": "string",
                "pattern": "^(agate|mac1)$"
              },
              {
                "type": "null"
              }
            ],
            "title": "Device Type"
          },
          "ac_type": {
            "anyOf": [
              {
                "type": "integer",
                "maximum": 2.0,
                "minimum": 0.0
              },
              {
                "type": "null"
              }
            ],
            "title": "Ac Type"
          },
          "publish_to_ha": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Publish To Ha"
          },
          "home_load_source": {
            "anyOf": [
              {
                "type": "string",
                "pattern": "^(standard|high_res)$"
              },
              {
                "type": "null"
              }
            ],
            "title": "Home Load Source"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "title": "GatewayUpdate"
      },
      "GroupCreate": {
        "properties": {
          "slug": {
            "type": "string",
            "maxLength": 63,
            "minLength": 2,
            "title": "Slug"
          },
          "name": {
            "type": "string",
            "maxLength": 120,
            "minLength": 1,
            "title": "Name"
          },
          "description": {
            "type": "string",
            "maxLength": 500,
            "title": "Description",
            "default": ""
          },
          "enabled": {
            "type": "boolean",
            "title": "Enabled",
            "default": true
          },
          "members": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Members"
          }
        },
        "type": "object",
        "required": [
          "slug",
          "name"
        ],
        "title": "GroupCreate"
      },
      "GroupUpdate": {
        "properties": {
          "name": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 120,
                "minLength": 1
              },
              {
                "type": "null"
              }
            ],
            "title": "Name"
          },
          "description": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 500
              },
              {
                "type": "null"
              }
            ],
            "title": "Description"
          },
          "enabled": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Enabled"
          }
        },
        "type": "object",
        "title": "GroupUpdate"
      },
      "HTTPValidationError": {
        "properties": {
          "detail": {
            "items": {
              "$ref": "#/components/schemas/ValidationError"
            },
            "type": "array",
            "title": "Detail"
          }
        },
        "type": "object",
        "title": "HTTPValidationError"
      },
      "HaActionItem": {
        "properties": {
          "kind": {
            "type": "string",
            "pattern": "^(entity|notify)$",
            "title": "Kind",
            "default": "entity"
          },
          "instance_id": {
            "type": "string",
            "minLength": 1,
            "title": "Instance Id"
          },
          "entity_id": {
            "type": "string",
            "title": "Entity Id",
            "default": ""
          },
          "service": {
            "type": "string",
            "minLength": 1,
            "title": "Service"
          },
          "data": {
            "additionalProperties": true,
            "type": "object",
            "title": "Data"
          },
          "when": {
            "type": "string",
            "pattern": "^(fire|exit|both)$",
            "title": "When",
            "default": "fire"
          },
          "guard": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Guard"
          },
          "title": {
            "type": "string",
            "maxLength": 200,
            "title": "Title",
            "default": ""
          },
          "message": {
            "type": "string",
            "maxLength": 1000,
            "title": "Message",
            "default": ""
          }
        },
        "type": "object",
        "required": [
          "instance_id",
          "service"
        ],
        "title": "HaActionItem",
        "description": "One one-shot HA action. ``when`` selects the edge it runs on: ``fire``\n(activation) or ``exit`` (when the window/activation ends).\n\nTwo kinds:\n- ``entity`` (default) \u2014 a service call on an entity (turn_on, select_option\u2026).\n- ``notify`` \u2014 a one-way message via an HA ``notify.*`` service. It has no\n  entity: ``service`` is the notify target name and title/message carry the\n  content, so entity_id is not required for it."
      },
      "HaExposeBody": {
        "properties": {
          "instance_id": {
            "type": "string",
            "minLength": 1,
            "title": "Instance Id"
          },
          "entity_id": {
            "type": "string",
            "minLength": 1,
            "title": "Entity Id"
          },
          "exposed": {
            "type": "boolean",
            "title": "Exposed"
          }
        },
        "type": "object",
        "required": [
          "instance_id",
          "entity_id",
          "exposed"
        ],
        "title": "HaExposeBody"
      },
      "HaInstanceCreate": {
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "title": "Name"
          },
          "base_url": {
            "type": "string",
            "minLength": 1,
            "title": "Base Url"
          },
          "token": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Token"
          },
          "is_default": {
            "type": "boolean",
            "title": "Is Default",
            "default": false
          },
          "enabled": {
            "type": "boolean",
            "title": "Enabled",
            "default": true
          }
        },
        "type": "object",
        "required": [
          "name",
          "base_url"
        ],
        "title": "HaInstanceCreate"
      },
      "HaInstanceUpdate": {
        "properties": {
          "name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Name"
          },
          "base_url": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Base Url"
          },
          "token": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Token"
          },
          "is_default": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Is Default"
          },
          "enabled": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Enabled"
          }
        },
        "type": "object",
        "title": "HaInstanceUpdate"
      },
      "HaTestBody": {
        "properties": {
          "base_url": {
            "type": "string",
            "minLength": 1,
            "title": "Base Url"
          },
          "token": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Token"
          },
          "ha_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Ha Id"
          }
        },
        "type": "object",
        "required": [
          "base_url"
        ],
        "title": "HaTestBody"
      },
      "ImportBundle": {
        "properties": {
          "type": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Type"
          },
          "version": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Version"
          },
          "entries": {
            "items": {
              "additionalProperties": true,
              "type": "object"
            },
            "type": "array",
            "title": "Entries"
          }
        },
        "type": "object",
        "title": "ImportBundle"
      },
      "LinkStatsResponse": {
        "properties": {
          "gateway_id": {
            "type": "string",
            "title": "Gateway Id"
          },
          "hours": {
            "type": "integer",
            "title": "Hours"
          },
          "since": {
            "type": "number",
            "title": "Since"
          },
          "rows": {
            "items": {
              "$ref": "#/components/schemas/LinkStatsRow"
            },
            "type": "array",
            "title": "Rows"
          },
          "open_hour": {
            "items": {
              "$ref": "#/components/schemas/LinkStatsRow"
            },
            "type": "array",
            "title": "Open Hour"
          }
        },
        "type": "object",
        "required": [
          "gateway_id",
          "hours",
          "since",
          "rows",
          "open_hour"
        ],
        "title": "LinkStatsResponse"
      },
      "LinkStatsRow": {
        "properties": {
          "gateway_id": {
            "type": "string",
            "title": "Gateway Id"
          },
          "hour_start": {
            "type": "number",
            "title": "Hour Start"
          },
          "kind": {
            "type": "string",
            "title": "Kind"
          },
          "path": {
            "type": "string",
            "title": "Path",
            "default": ""
          },
          "partial": {
            "type": "integer",
            "title": "Partial",
            "default": 0
          },
          "count": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Count"
          },
          "ok": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Ok"
          },
          "empty": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Empty"
          },
          "timeout_no_reply": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Timeout No Reply"
          },
          "timeout_closed": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Timeout Closed"
          },
          "error": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Error"
          },
          "error_classes": {
            "anyOf": [
              {
                "additionalProperties": {
                  "type": "integer"
                },
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Error Classes"
          },
          "lat_p50_ms": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Lat P50 Ms"
          },
          "lat_p95_ms": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Lat P95 Ms"
          },
          "lat_max_ms": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Lat Max Ms"
          },
          "conns_opened": {
            "anyOf": [
              {
                "additionalProperties": {
                  "type": "integer"
                },
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Conns Opened"
          },
          "reconnects": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Reconnects"
          },
          "reconnects_failed": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Reconnects Failed"
          },
          "rescans": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Rescans"
          },
          "rescans_failed": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Rescans Failed"
          },
          "rescan_max_ms": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Rescan Max Ms"
          },
          "lib_retries": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Lib Retries"
          },
          "max_consec_fail": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Max Consec Fail"
          },
          "newest_good_age_s": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Newest Good Age S"
          },
          "polls": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Polls"
          },
          "cache_polls": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Cache Polls"
          },
          "overlaps": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Overlaps"
          },
          "overlap_pairs": {
            "anyOf": [
              {
                "additionalProperties": {
                  "type": "integer"
                },
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Overlap Pairs"
          },
          "max_in_flight": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Max In Flight"
          },
          "max_established": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Max Established"
          }
        },
        "type": "object",
        "required": [
          "gateway_id",
          "hour_start",
          "kind"
        ],
        "title": "LinkStatsRow",
        "description": "One ``link_stats`` row. Request rows fill the request columns; the\n``kind = 'link'`` row fills the connection-level ones. See\n``franklinwh_bridge.gateway.link_stats`` for what each column means."
      },
      "MemberUpdate": {
        "properties": {
          "members": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Members"
          }
        },
        "type": "object",
        "required": [
          "members"
        ],
        "title": "MemberUpdate"
      },
      "MetricsSettingsUpdate": {
        "properties": {
          "retention_days": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Retention Days"
          },
          "raw_age_days": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Raw Age Days"
          }
        },
        "type": "object",
        "title": "MetricsSettingsUpdate"
      },
      "ModuleToggle": {
        "properties": {
          "enabled": {
            "type": "boolean",
            "title": "Enabled"
          }
        },
        "type": "object",
        "required": [
          "enabled"
        ],
        "title": "ModuleToggle"
      },
      "MqttConfigUpdate": {
        "properties": {
          "host": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Host"
          },
          "port": {
            "anyOf": [
              {
                "type": "integer",
                "maximum": 65535.0,
                "minimum": 1.0
              },
              {
                "type": "null"
              }
            ],
            "title": "Port"
          },
          "username": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Username"
          },
          "password": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Password"
          },
          "tls_mode": {
            "anyOf": [
              {
                "type": "string",
                "pattern": "^(off|tls|tls_insecure)$"
              },
              {
                "type": "null"
              }
            ],
            "title": "Tls Mode"
          },
          "enabled": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Enabled"
          },
          "client_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Client Id"
          },
          "qos": {
            "anyOf": [
              {
                "type": "integer",
                "maximum": 2.0,
                "minimum": 0.0
              },
              {
                "type": "null"
              }
            ],
            "title": "Qos"
          },
          "retain_discovery": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Retain Discovery"
          },
          "topic_prefix": {
            "anyOf": [
              {
                "type": "string",
                "pattern": "^[a-z][a-z0-9_]{1,31}$"
              },
              {
                "type": "null"
              }
            ],
            "title": "Topic Prefix"
          },
          "discovery_prefix": {
            "anyOf": [
              {
                "type": "string",
                "pattern": "^[A-Za-z0-9_-]{1,64}$"
              },
              {
                "type": "null"
              }
            ],
            "title": "Discovery Prefix"
          }
        },
        "type": "object",
        "title": "MqttConfigUpdate"
      },
      "NamespaceChange": {
        "properties": {
          "topic_prefix": {
            "type": "string",
            "maxLength": 32,
            "minLength": 1,
            "title": "Topic Prefix"
          },
          "confirm": {
            "type": "boolean",
            "title": "Confirm",
            "default": false
          }
        },
        "type": "object",
        "required": [
          "topic_prefix"
        ],
        "title": "NamespaceChange"
      },
      "PasswordChange": {
        "properties": {
          "current_password": {
            "type": "string",
            "minLength": 1,
            "title": "Current Password"
          },
          "new_password": {
            "type": "string",
            "maxLength": 200,
            "minLength": 8,
            "title": "New Password"
          }
        },
        "type": "object",
        "required": [
          "current_password",
          "new_password"
        ],
        "title": "PasswordChange"
      },
      "PicsUpdateRequest": {
        "properties": {
          "model_id": {
            "type": "integer",
            "title": "Model Id"
          },
          "point_name": {
            "type": "string",
            "title": "Point Name"
          },
          "status": {
            "type": "string",
            "title": "Status"
          },
          "notes": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Notes"
          }
        },
        "type": "object",
        "required": [
          "model_id",
          "point_name",
          "status"
        ],
        "title": "PicsUpdateRequest"
      },
      "PlanChange": {
        "properties": {
          "valid_from": {
            "type": "number",
            "exclusiveMinimum": 0.0,
            "title": "Valid From",
            "description": "epoch seconds; may be in the past"
          },
          "retailer": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Retailer"
          },
          "network": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Network"
          },
          "plan_type": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Plan Type"
          },
          "pricing": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Pricing"
          },
          "note": {
            "type": "string",
            "title": "Note",
            "default": ""
          }
        },
        "type": "object",
        "required": [
          "valid_from"
        ],
        "title": "PlanChange",
        "description": "A retailer/plan switch, recorded at the date it actually happened.\n\n``valid_from`` is almost always in the past: a switch is discovered after\nthe fact, and stamping it \"now\" is what made the owner's own Amber \u2192 AGL\nchange on 09 Sep unrepresentable."
      },
      "PlanStartCorrection": {
        "properties": {
          "valid_from": {
            "type": "number",
            "exclusiveMinimum": 0.0,
            "title": "Valid From"
          }
        },
        "type": "object",
        "required": [
          "valid_from"
        ],
        "title": "PlanStartCorrection",
        "description": "Move a plan's start date without creating a new plan."
      },
      "PlanSwitch": {
        "properties": {
          "retailer": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 120
              },
              {
                "type": "null"
              }
            ],
            "title": "Retailer"
          },
          "network": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 120
              },
              {
                "type": "null"
              }
            ],
            "title": "Network"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "title": "PlanSwitch",
        "description": "Start a new plan on a service. Both fields optional \u2014 switching plan with\nthe same retailer (a re-contract) is as common as changing supplier."
      },
      "PointMember": {
        "properties": {
          "ref": {
            "type": "string",
            "pattern": "^\\d+\\.[A-Za-z0-9_]+$",
            "title": "Ref"
          },
          "disp_name": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 120
              },
              {
                "type": "null"
              }
            ],
            "title": "Disp Name"
          },
          "disp_unit": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 24
              },
              {
                "type": "null"
              }
            ],
            "title": "Disp Unit"
          }
        },
        "type": "object",
        "required": [
          "ref"
        ],
        "title": "PointMember"
      },
      "ProbeRequest": {
        "properties": {
          "host": {
            "type": "string",
            "maxLength": 255,
            "minLength": 1,
            "title": "Host"
          },
          "port": {
            "type": "integer",
            "maximum": 65535.0,
            "minimum": 1.0,
            "title": "Port",
            "default": 502
          },
          "unit_id": {
            "type": "integer",
            "maximum": 247.0,
            "minimum": 1.0,
            "title": "Unit Id",
            "default": 1
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "host"
        ],
        "title": "ProbeRequest"
      },
      "PublishToHaUpdate": {
        "properties": {
          "gateway_id": {
            "type": "string",
            "maxLength": 63,
            "minLength": 1,
            "title": "Gateway Id"
          },
          "publish": {
            "type": "boolean",
            "title": "Publish"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "gateway_id",
          "publish"
        ],
        "title": "PublishToHaUpdate"
      },
      "RateValidateBody": {
        "properties": {
          "seasons": {
            "items": {
              "additionalProperties": true,
              "type": "object"
            },
            "type": "array",
            "title": "Seasons"
          },
          "default_rate": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Default Rate"
          },
          "kind": {
            "anyOf": [
              {
                "type": "string",
                "pattern": "^(static|dynamic)$"
              },
              {
                "type": "null"
              }
            ],
            "title": "Kind"
          },
          "dynamic": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Dynamic"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "title": "RateValidateBody"
      },
      "ScanRequest": {
        "properties": {
          "subnets": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "maxItems": 4,
            "minItems": 1,
            "title": "Subnets"
          },
          "port": {
            "type": "integer",
            "maximum": 65535.0,
            "minimum": 1.0,
            "title": "Port",
            "default": 502
          },
          "unit_id": {
            "type": "integer",
            "maximum": 247.0,
            "minimum": 1.0,
            "title": "Unit Id",
            "default": 1
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "subnets"
        ],
        "title": "ScanRequest"
      },
      "ScheduleCreate": {
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 120,
            "minLength": 1,
            "title": "Name"
          },
          "when_spec": {
            "$ref": "#/components/schemas/WhenSpec"
          },
          "action": {
            "type": "string",
            "title": "Action"
          },
          "params": {
            "additionalProperties": true,
            "type": "object",
            "title": "Params"
          },
          "target_type": {
            "type": "string",
            "pattern": "^(gateway|service|site)$",
            "title": "Target Type",
            "default": "gateway"
          },
          "target_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Target Id"
          },
          "enabled": {
            "type": "boolean",
            "title": "Enabled",
            "default": true
          },
          "release": {
            "type": "string",
            "pattern": "^(release|hold)$",
            "title": "Release",
            "default": "release"
          },
          "conflict": {
            "type": "string",
            "pattern": "^(defer|override|wait)$",
            "title": "Conflict",
            "default": "defer"
          },
          "priority": {
            "type": "integer",
            "maximum": 1000.0,
            "minimum": 0.0,
            "title": "Priority",
            "default": 0
          },
          "trigger_kind": {
            "anyOf": [
              {
                "type": "string",
                "pattern": "^(oneoff|daily|weekly|interval|monthly|cron|always)$"
              },
              {
                "type": "null"
              }
            ],
            "title": "Trigger Kind"
          },
          "trigger_spec": {
            "additionalProperties": true,
            "type": "object",
            "title": "Trigger Spec"
          },
          "entry_conditions": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Entry Conditions"
          },
          "exit_conditions": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Exit Conditions"
          },
          "duration_s": {
            "anyOf": [
              {
                "type": "integer",
                "minimum": 0.0
              },
              {
                "type": "null"
              }
            ],
            "title": "Duration S"
          },
          "release_policy": {
            "type": "string",
            "title": "Release Policy",
            "default": "release"
          },
          "missed_policy": {
            "type": "string",
            "pattern": "^(late_fire_remaining|skip|late_fire_always)$",
            "title": "Missed Policy",
            "default": "late_fire_remaining"
          },
          "entry_hold_s": {
            "type": "integer",
            "maximum": 86400.0,
            "minimum": 0.0,
            "title": "Entry Hold S",
            "default": 0
          },
          "ha_actions": {
            "items": {
              "$ref": "#/components/schemas/HaActionItem"
            },
            "type": "array",
            "title": "Ha Actions"
          }
        },
        "type": "object",
        "required": [
          "name",
          "action"
        ],
        "title": "ScheduleCreate"
      },
      "ScheduleUpdate": {
        "properties": {
          "name": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 120,
                "minLength": 1
              },
              {
                "type": "null"
              }
            ],
            "title": "Name"
          },
          "when_spec": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/WhenSpec"
              },
              {
                "type": "null"
              }
            ]
          },
          "action": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Action"
          },
          "params": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Params"
          },
          "target_type": {
            "anyOf": [
              {
                "type": "string",
                "pattern": "^(gateway|service|site)$"
              },
              {
                "type": "null"
              }
            ],
            "title": "Target Type"
          },
          "target_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Target Id"
          },
          "enabled": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Enabled"
          },
          "release": {
            "anyOf": [
              {
                "type": "string",
                "pattern": "^(release|hold)$"
              },
              {
                "type": "null"
              }
            ],
            "title": "Release"
          },
          "conflict": {
            "anyOf": [
              {
                "type": "string",
                "pattern": "^(defer|override|wait)$"
              },
              {
                "type": "null"
              }
            ],
            "title": "Conflict"
          },
          "priority": {
            "anyOf": [
              {
                "type": "integer",
                "maximum": 1000.0,
                "minimum": 0.0
              },
              {
                "type": "null"
              }
            ],
            "title": "Priority"
          },
          "trigger_kind": {
            "anyOf": [
              {
                "type": "string",
                "pattern": "^(oneoff|daily|weekly|interval|monthly|cron|always)$"
              },
              {
                "type": "null"
              }
            ],
            "title": "Trigger Kind"
          },
          "trigger_spec": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Trigger Spec"
          },
          "entry_conditions": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Entry Conditions"
          },
          "exit_conditions": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Exit Conditions"
          },
          "duration_s": {
            "anyOf": [
              {
                "type": "integer",
                "minimum": 0.0
              },
              {
                "type": "null"
              }
            ],
            "title": "Duration S"
          },
          "release_policy": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Release Policy"
          },
          "missed_policy": {
            "anyOf": [
              {
                "type": "string",
                "pattern": "^(late_fire_remaining|skip|late_fire_always)$"
              },
              {
                "type": "null"
              }
            ],
            "title": "Missed Policy"
          },
          "entry_hold_s": {
            "anyOf": [
              {
                "type": "integer",
                "maximum": 86400.0,
                "minimum": 0.0
              },
              {
                "type": "null"
              }
            ],
            "title": "Entry Hold S"
          },
          "ha_actions": {
            "anyOf": [
              {
                "items": {
                  "$ref": "#/components/schemas/HaActionItem"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Ha Actions"
          }
        },
        "type": "object",
        "title": "ScheduleUpdate"
      },
      "SequenceExecRequest": {
        "properties": {
          "sequence": {
            "anyOf": [
              {
                "items": {
                  "additionalProperties": true,
                  "type": "object"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Sequence"
          },
          "inline": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Inline"
          },
          "dry_run": {
            "type": "boolean",
            "title": "Dry Run",
            "default": false
          },
          "gateway_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Gateway Id"
          }
        },
        "type": "object",
        "title": "SequenceExecRequest"
      },
      "SequenceRenameRequest": {
        "properties": {
          "new_name": {
            "type": "string",
            "title": "New Name"
          }
        },
        "type": "object",
        "required": [
          "new_name"
        ],
        "title": "SequenceRenameRequest"
      },
      "SequenceSaveRequest": {
        "properties": {
          "content": {
            "type": "string",
            "title": "Content"
          }
        },
        "type": "object",
        "required": [
          "content"
        ],
        "title": "SequenceSaveRequest"
      },
      "ServiceCreate": {
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 120,
            "minLength": 1,
            "title": "Name"
          },
          "meter_number": {
            "type": "string",
            "maxLength": 120,
            "title": "Meter Number",
            "default": ""
          },
          "account": {
            "type": "string",
            "maxLength": 120,
            "title": "Account",
            "default": ""
          },
          "ac_service": {
            "type": "integer",
            "maximum": 3.0,
            "minimum": 1.0,
            "title": "Ac Service",
            "default": 1
          },
          "rated_amps": {
            "type": "integer",
            "maximum": 10000.0,
            "minimum": 0.0,
            "title": "Rated Amps",
            "default": 0
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "name"
        ],
        "title": "ServiceCreate"
      },
      "ServiceUpdate": {
        "properties": {
          "name": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 120,
                "minLength": 1
              },
              {
                "type": "null"
              }
            ],
            "title": "Name"
          },
          "meter_number": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 120
              },
              {
                "type": "null"
              }
            ],
            "title": "Meter Number"
          },
          "account": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 120
              },
              {
                "type": "null"
              }
            ],
            "title": "Account"
          },
          "ac_service": {
            "anyOf": [
              {
                "type": "integer",
                "maximum": 3.0,
                "minimum": 1.0
              },
              {
                "type": "null"
              }
            ],
            "title": "Ac Service"
          },
          "rated_amps": {
            "anyOf": [
              {
                "type": "integer",
                "maximum": 10000.0,
                "minimum": 0.0
              },
              {
                "type": "null"
              }
            ],
            "title": "Rated Amps"
          },
          "has_tou": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Has Tou"
          },
          "has_peak_demand": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Has Peak Demand"
          },
          "has_export_bonus": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Has Export Bonus"
          },
          "min_monthly_bill": {
            "anyOf": [
              {
                "type": "number",
                "minimum": 0.0
              },
              {
                "type": "null"
              }
            ],
            "title": "Min Monthly Bill"
          },
          "pricing_api": {
            "anyOf": [
              {
                "type": "string",
                "pattern": "^(none|ha|direct)$"
              },
              {
                "type": "null"
              }
            ],
            "title": "Pricing Api"
          },
          "demand_window": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/TariffWindow"
              },
              {
                "type": "null"
              }
            ]
          },
          "bonus_window": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/TariffWindow"
              },
              {
                "type": "null"
              }
            ]
          },
          "pricing": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Pricing"
          },
          "plan_type": {
            "anyOf": [
              {
                "type": "string",
                "pattern": "^(unknown|fixed|tiered|tou|hybrid|demand|dynamic|other)$"
              },
              {
                "type": "null"
              }
            ],
            "title": "Plan Type"
          },
          "export_allowed": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Export Allowed"
          },
          "solar_export_allowed": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Solar Export Allowed"
          },
          "battery_export_allowed": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Battery Export Allowed"
          },
          "export_restriction_note": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 200
              },
              {
                "type": "null"
              }
            ],
            "title": "Export Restriction Note"
          },
          "export_limit_kw": {
            "anyOf": [
              {
                "type": "number",
                "minimum": 0.0
              },
              {
                "type": "null"
              }
            ],
            "title": "Export Limit Kw"
          },
          "charging_allowed": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Charging Allowed"
          },
          "discharging_allowed": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Discharging Allowed"
          },
          "country": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 2
              },
              {
                "type": "null"
              }
            ],
            "title": "Country"
          },
          "timezone": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 64
              },
              {
                "type": "null"
              }
            ],
            "title": "Timezone"
          },
          "has_export_charge": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Has Export Charge"
          },
          "retailer": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 120
              },
              {
                "type": "null"
              }
            ],
            "title": "Retailer"
          },
          "network": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 120
              },
              {
                "type": "null"
              }
            ],
            "title": "Network"
          },
          "enabled": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Enabled"
          },
          "pto_status": {
            "anyOf": [
              {
                "type": "string",
                "pattern": "^(unknown|none|pending|approved)$"
              },
              {
                "type": "null"
              }
            ],
            "title": "Pto Status"
          },
          "pto_date": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Pto Date"
          },
          "pto_reference": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 120
              },
              {
                "type": "null"
              }
            ],
            "title": "Pto Reference"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "title": "ServiceUpdate"
      },
      "SetupStateUpdate": {
        "properties": {
          "state": {
            "type": "string",
            "enum": [
              "pending",
              "done_real",
              "done_demo",
              "skipped"
            ],
            "title": "State"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "state"
        ],
        "title": "SetupStateUpdate"
      },
      "SingleMember": {
        "properties": {
          "entity_slug": {
            "type": "string",
            "title": "Entity Slug"
          }
        },
        "type": "object",
        "required": [
          "entity_slug"
        ],
        "title": "SingleMember"
      },
      "SiteConfigUpdate": {
        "properties": {
          "name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Name"
          },
          "description": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Description"
          },
          "meter_number": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Meter Number"
          },
          "account_number": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Account Number"
          },
          "ac_service_type": {
            "anyOf": [
              {
                "type": "integer",
                "maximum": 3.0,
                "minimum": 1.0
              },
              {
                "type": "null"
              }
            ],
            "title": "Ac Service Type"
          },
          "aggregate_entities": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Aggregate Entities"
          },
          "full_backup": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Full Backup"
          },
          "grid_forming": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Grid Forming"
          },
          "generator_input": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Generator Input"
          },
          "solar_type": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Solar Type"
          },
          "solar_kwp": {
            "anyOf": [
              {
                "type": "number",
                "minimum": 0.0
              },
              {
                "type": "null"
              }
            ],
            "title": "Solar Kwp"
          },
          "load_shedding": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Load Shedding"
          },
          "nonbackup_loads": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Nonbackup Loads"
          },
          "battery_label": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Battery Label"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "title": "SiteConfigUpdate"
      },
      "SolarSettingsBody": {
        "properties": {
          "pv_latitude": {
            "anyOf": [
              {
                "type": "number",
                "maximum": 90.0,
                "minimum": -90.0
              },
              {
                "type": "null"
              }
            ],
            "title": "Pv Latitude"
          },
          "pv_longitude": {
            "anyOf": [
              {
                "type": "number",
                "maximum": 180.0,
                "minimum": -180.0
              },
              {
                "type": "null"
              }
            ],
            "title": "Pv Longitude"
          },
          "pv_kwp": {
            "type": "number",
            "maximum": 1000.0,
            "exclusiveMinimum": 0.0,
            "title": "Pv Kwp",
            "description": "Array size (kWp)",
            "default": 5.0
          },
          "pv_tilt": {
            "type": "number",
            "maximum": 90.0,
            "minimum": 0.0,
            "title": "Pv Tilt",
            "description": "Panel tilt (deg)",
            "default": 20.0
          },
          "pv_azimuth": {
            "type": "number",
            "maximum": 180.0,
            "minimum": -180.0,
            "title": "Pv Azimuth",
            "description": "Open-Meteo convention: 0 = south, -90 = east, 90 = west, \u00b1180 = north.",
            "default": 0.0
          }
        },
        "type": "object",
        "title": "SolarSettingsBody",
        "description": "Location + PV array. Send ``null`` for latitude/longitude to switch the\nforecast off; omitted fields are left as they are."
      },
      "SolarSettingsResponse": {
        "properties": {
          "values": {
            "additionalProperties": true,
            "type": "object",
            "title": "Values"
          },
          "configured": {
            "type": "boolean",
            "title": "Configured"
          },
          "azimuth_help": {
            "type": "string",
            "title": "Azimuth Help",
            "default": "Open-Meteo convention: 0 = south, -90 = east, 90 = west, \u00b1180 = north."
          },
          "ha_instances": {
            "items": {
              "additionalProperties": {
                "type": "string"
              },
              "type": "object"
            },
            "type": "array",
            "title": "Ha Instances"
          }
        },
        "type": "object",
        "required": [
          "values",
          "configured"
        ],
        "title": "SolarSettingsResponse"
      },
      "SpotSettingsBody": {
        "properties": {
          "nem_region": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Nem Region",
            "description": "NSW1/QLD1/SA1/TAS1/VIC1, '' = off"
          },
          "tariff_price_entity": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Tariff Price Entity",
            "description": "HA buy price, 'ha:<instance>:<entity_id>', '' = none"
          },
          "tariff_feedin_entity": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Tariff Feedin Entity",
            "description": "HA feed-in price, 'ha:<instance>:<entity_id>', '' = none"
          }
        },
        "type": "object",
        "title": "SpotSettingsBody",
        "description": "Omitted fields are left as they are; ``\"\"`` switches a field off."
      },
      "SpotSettingsResponse": {
        "properties": {
          "values": {
            "additionalProperties": true,
            "type": "object",
            "title": "Values"
          },
          "regions": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Regions"
          },
          "configured": {
            "type": "boolean",
            "title": "Configured"
          },
          "help": {
            "type": "string",
            "title": "Help",
            "default": "Australia only: the AEMO National Electricity Market 5-minute wholesale spot price (RRP) for your region, not your retail tariff."
          }
        },
        "type": "object",
        "required": [
          "values",
          "regions",
          "configured"
        ],
        "title": "SpotSettingsResponse"
      },
      "SunSpecExport": {
        "properties": {
          "filename": {
            "type": "string",
            "title": "Filename"
          },
          "media_type": {
            "type": "string",
            "title": "Media Type"
          },
          "content": {
            "type": "string",
            "title": "Content"
          },
          "model_count": {
            "type": "integer",
            "title": "Model Count"
          },
          "point_count": {
            "type": "integer",
            "title": "Point Count"
          },
          "serial_redacted": {
            "type": "boolean",
            "title": "Serial Redacted"
          }
        },
        "type": "object",
        "required": [
          "filename",
          "media_type",
          "content",
          "model_count",
          "point_count",
          "serial_redacted"
        ],
        "title": "SunSpecExport",
        "description": "A SunSpec export, ready to preview, copy, download or put in a Gist."
      },
      "SupportInfo": {
        "properties": {
          "info": {
            "additionalProperties": true,
            "type": "object",
            "title": "Info"
          },
          "markdown": {
            "type": "string",
            "title": "Markdown"
          }
        },
        "type": "object",
        "required": [
          "info",
          "markdown"
        ],
        "title": "SupportInfo",
        "description": "The bundle (``info``) and the same as ready-to-paste Markdown."
      },
      "TariffImportBundle": {
        "properties": {
          "type": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Type"
          },
          "version": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Version"
          },
          "exported_at": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Exported At"
          },
          "profile": {
            "additionalProperties": true,
            "type": "object",
            "title": "Profile"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "title": "TariffImportBundle"
      },
      "TariffWindow": {
        "properties": {
          "months": {
            "items": {
              "type": "integer"
            },
            "type": "array",
            "title": "Months"
          },
          "days": {
            "items": {
              "type": "integer"
            },
            "type": "array",
            "title": "Days"
          },
          "start": {
            "type": "string",
            "pattern": "^\\d{1,2}:\\d{2}$",
            "title": "Start",
            "default": "00:00"
          },
          "end": {
            "type": "string",
            "pattern": "^\\d{1,2}:\\d{2}$",
            "title": "End",
            "default": "23:59"
          }
        },
        "type": "object",
        "title": "TariffWindow",
        "description": "A season/day/time window a tariff period applies in. Empty months/days\nmean \"all\"; start/end are local HH:MM."
      },
      "TimezoneConfirm": {
        "properties": {
          "timezone": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 64
              },
              {
                "type": "null"
              }
            ],
            "title": "Timezone"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "title": "TimezoneConfirm"
      },
      "UseHaUpdate": {
        "properties": {
          "use_ha": {
            "type": "boolean",
            "title": "Use Ha"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "use_ha"
        ],
        "title": "UseHaUpdate"
      },
      "UserCreate": {
        "properties": {
          "username": {
            "type": "string",
            "maxLength": 64,
            "minLength": 1,
            "title": "Username"
          },
          "password": {
            "type": "string",
            "minLength": 1,
            "title": "Password"
          },
          "role": {
            "type": "string",
            "pattern": "^(admin|user|viewer|api)$",
            "title": "Role",
            "default": "viewer"
          },
          "expires_at": {
            "anyOf": [
              {
                "type": "number",
                "minimum": 0.0
              },
              {
                "type": "null"
              }
            ],
            "title": "Expires At"
          }
        },
        "type": "object",
        "required": [
          "username",
          "password"
        ],
        "title": "UserCreate"
      },
      "UserUpdate": {
        "properties": {
          "role": {
            "anyOf": [
              {
                "type": "string",
                "pattern": "^(admin|user|viewer|api)$"
              },
              {
                "type": "null"
              }
            ],
            "title": "Role"
          },
          "enabled": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Enabled"
          },
          "password": {
            "anyOf": [
              {
                "type": "string",
                "minLength": 1
              },
              {
                "type": "null"
              }
            ],
            "title": "Password"
          },
          "expires_at": {
            "anyOf": [
              {
                "type": "number",
                "minimum": 0.0
              },
              {
                "type": "null"
              }
            ],
            "title": "Expires At"
          }
        },
        "type": "object",
        "title": "UserUpdate"
      },
      "ValidationError": {
        "properties": {
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            },
            "type": "array",
            "title": "Location"
          },
          "msg": {
            "type": "string",
            "title": "Message"
          },
          "type": {
            "type": "string",
            "title": "Error Type"
          },
          "input": {
            "title": "Input"
          },
          "ctx": {
            "type": "object",
            "title": "Context"
          }
        },
        "type": "object",
        "required": [
          "loc",
          "msg",
          "type"
        ],
        "title": "ValidationError"
      },
      "WhenSpec": {
        "properties": {
          "days": {
            "items": {
              "type": "integer"
            },
            "type": "array",
            "title": "Days"
          },
          "windows": {
            "items": {
              "$ref": "#/components/schemas/Window"
            },
            "type": "array",
            "title": "Windows"
          },
          "date": {
            "anyOf": [
              {
                "type": "string",
                "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
              },
              {
                "type": "null"
              }
            ],
            "title": "Date"
          }
        },
        "type": "object",
        "title": "WhenSpec"
      },
      "Window": {
        "properties": {
          "start": {
            "type": "string",
            "pattern": "^\\d{1,2}:\\d{2}$",
            "title": "Start"
          },
          "end": {
            "type": "string",
            "pattern": "^\\d{1,2}:\\d{2}$",
            "title": "End"
          }
        },
        "type": "object",
        "required": [
          "start",
          "end"
        ],
        "title": "Window"
      }
    }
  }
}
