{
  "openapi": "3.0.3",
  "info": {
    "title": "TowerWatch SimCity API",
    "description": "UC Davis campus energy dashboard and tactical map endpoints. These APIs power the SimCity real-time map, building overlays, live telemetry, air quality feeds, and tactical situational awareness layers.",
    "version": "1.0.0",
    "contact": {
      "name": "TowerWatch"
    }
  },
  "servers": [
    {
      "url": "/",
      "description": "Local TowerWatch server"
    }
  ],
  "tags": [
    {"name": "Buildings", "description": "Building geometry and metadata"},
    {"name": "Live Data", "description": "Real-time telemetry from CEED and PI"},
    {"name": "Overlays", "description": "Static GeoJSON infrastructure layers from ArcGIS"},
    {"name": "Realtime Feeds", "description": "Proxied government real-time data feeds"},
    {"name": "AirNow", "description": "Air quality forecasts, history, RSS, and contour maps"},
    {"name": "Backfill", "description": "Data backfill monitoring and control"},
    {"name": "Tactical", "description": "Tactical map and building focus endpoints"},
    {"name": "Campus", "description": "Campus-level aggregations and building lists"},
    {"name": "System", "description": "Server clock and diagnostics"}
  ],
  "paths": {
    "/api/simcity/buildings.geojson": {
      "get": {
        "tags": ["Buildings"],
        "operationId": "getBuildings",
        "summary": "All buildings as GeoJSON points",
        "description": "GeoJSON FeatureCollection of all buildings with metadata (lat/lon, sqft, type, LEED cert, sensor flags). Caches to disk so the map works even when DuckDB is locked by the loader.",
        "responses": {
          "200": {
            "description": "GeoJSON FeatureCollection",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/BuildingPointsGeoJSON" }
              }
            }
          }
        }
      }
    },
    "/api/simcity/buildings-polygons.geojson": {
      "get": {
        "tags": ["Buildings"],
        "operationId": "getBuildingPolygons",
        "summary": "Building footprint polygons from ArcGIS",
        "description": "GeoJSON FeatureCollection of building footprint polygons sourced from UC Davis ArcGIS Facilities layer. Falls back to point-based buildings.geojson if polygon file is unavailable.",
        "responses": {
          "200": {
            "description": "GeoJSON FeatureCollection with Polygon geometries",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/GeoJSONFeatureCollection" }
              }
            }
          }
        }
      }
    },
    "/api/simcity/building-polygon.geojson": {
      "get": {
        "tags": ["Buildings"],
        "operationId": "getSingleBuildingPolygon",
        "summary": "Single building polygon by name",
        "description": "Returns a single GeoJSON Feature with the polygon footprint for the named building. Used for per-building dashboard views.",
        "parameters": [
          {
            "name": "name",
            "in": "query",
            "required": true,
            "description": "Building name to match (e.g. 'Shields Library')",
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "GeoJSON Feature with Polygon geometry",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/GeoJSONFeature" }
              }
            }
          },
          "400": {
            "description": "Missing name parameter",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "404": {
            "description": "No building found matching the name",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          }
        }
      }
    },
    "/api/simcity/solar-arrays": {
      "get": {
        "tags": ["Buildings"],
        "operationId": "getSolarArrays",
        "summary": "Solar array locations as GeoJSON",
        "description": "GeoJSON FeatureCollection of standalone solar arrays (parking lots, solar farm, rooftop) as clickable map points with PI tag references.",
        "responses": {
          "200": {
            "description": "GeoJSON FeatureCollection of solar arrays",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/SolarArraysGeoJSON" }
              }
            }
          }
        }
      }
    },
    "/api/simcity/live/oat": {
      "get": {
        "tags": ["Live Data"],
        "operationId": "getLiveOAT",
        "summary": "Live outside air temperature",
        "description": "Current outside air temperature from CEED interpolatedOat endpoint (no VPN needed). Returns latest value plus 2-hour history at 15-minute intervals.",
        "responses": {
          "200": {
            "description": "Current OAT reading",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/LiveOAT" }
              }
            }
          }
        }
      }
    },
    "/api/simcity/live/campus": {
      "get": {
        "tags": ["Live Data"],
        "operationId": "getLiveCampus",
        "summary": "Live campus demand breakdown",
        "description": "Live campus demand from CEED energyStoryDemand (no VPN needed). Returns per-meter MW values (CHCP gas, PG&E grid, solar farm, rooftop solar) converted from MMBtu/h.",
        "responses": {
          "200": {
            "description": "Campus demand breakdown in MW",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/LiveCampus" }
              }
            }
          }
        }
      }
    },
    "/api/simcity/live/building": {
      "get": {
        "tags": ["Live Data"],
        "operationId": "getLiveBuilding",
        "summary": "Live building electricity demand",
        "description": "Live building demand from CEED afCommodityData (no VPN needed). Returns latest demand in kW plus 4-hour history at 15-minute intervals.",
        "parameters": [
          {
            "name": "name",
            "in": "query",
            "required": true,
            "description": "Building name as known to CEED (e.g. 'Shields Library')",
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "Building demand data",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/LiveBuilding" }
              }
            }
          },
          "400": {
            "description": "Missing name parameter",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          }
        }
      }
    },
    "/api/simcity/live/solar": {
      "get": {
        "tags": ["Live Data"],
        "operationId": "getLiveSolar",
        "summary": "Live solar production for a building",
        "description": "Live solar production from CEED afCommodityData with commodity=solar. Returns latest solar kW plus 4-hour history at 15-minute intervals.",
        "parameters": [
          {
            "name": "name",
            "in": "query",
            "required": true,
            "description": "Building name with solar arrays (e.g. 'Segundo Dining Commons')",
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "Solar production data",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/LiveSolar" }
              }
            }
          },
          "400": {
            "description": "Missing name parameter",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          }
        }
      }
    },
    "/api/simcity/live/wifi": {
      "get": {
        "tags": ["Live Data"],
        "operationId": "getLiveWifi",
        "summary": "Live WiFi occupancy proxy",
        "description": "WiFi device count as occupancy proxy. Tries DuckDB cached data first, falls back to spring break baseline. Requires VPN for live PI data.",
        "parameters": [
          {
            "name": "name",
            "in": "query",
            "required": false,
            "description": "Building name or ICS code to filter (optional; omit for campus total only)",
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "WiFi occupancy counts",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/LiveWifi" }
              }
            }
          }
        }
      }
    },
    "/api/simcity/overlays/{layer_id}.geojson": {
      "get": {
        "tags": ["Overlays"],
        "operationId": "getOverlay",
        "summary": "Static GeoJSON overlay layer",
        "description": "Serves static GeoJSON overlay layers from ArcGIS data. Cached with 1-hour max-age. Available layers: water-pipes, fire-hydrants, water-valves, roads, chilled-water, ev-chargers, solar-pv, campus-boundary, parking-lots.",
        "parameters": [
          {
            "name": "layer_id",
            "in": "path",
            "required": true,
            "description": "Layer identifier",
            "schema": {
              "type": "string",
              "enum": ["water-pipes", "fire-hydrants", "water-valves", "roads", "chilled-water", "ev-chargers", "solar-pv", "campus-boundary", "parking-lots"]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "GeoJSON FeatureCollection for the layer",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/GeoJSONFeatureCollection" }
              }
            }
          },
          "404": {
            "description": "Unknown layer or layer data not available",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          }
        }
      }
    },
    "/api/simcity/realtime/{feed_id}.geojson": {
      "get": {
        "tags": ["Realtime Feeds"],
        "operationId": "getRealtimeFeed",
        "summary": "Proxied real-time government feed",
        "description": "Proxy validated government real-time feeds with TTL caching. Wildfires from NIFC ArcGIS (15-min TTL), earthquakes from USGS (5-min TTL), AirNow current observations (15-min TTL, requires AIRNOW_API_KEY).",
        "parameters": [
          {
            "name": "feed_id",
            "in": "path",
            "required": true,
            "description": "Feed identifier",
            "schema": {
              "type": "string",
              "enum": ["wildfires", "wildfire-perimeters", "earthquakes", "airnow"]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "GeoJSON FeatureCollection from upstream feed",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/GeoJSONFeatureCollection" }
              }
            }
          },
          "404": {
            "description": "Unknown feed",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "502": {
            "description": "Upstream feed unavailable",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "503": {
            "description": "API key not configured (airnow feed only)",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          }
        }
      }
    },
    "/api/simcity/airnow/forecast.json": {
      "get": {
        "tags": ["AirNow"],
        "operationId": "getAirNowForecast",
        "summary": "AQI forecast for Davis area",
        "description": "AQI forecast for today and tomorrow from AirNow API. Cached for 6 hours. Requires AIRNOW_API_KEY.",
        "responses": {
          "200": {
            "description": "AQI forecast data",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/AirNowForecast" }
              }
            }
          },
          "502": {
            "description": "Upstream unavailable",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "503": {
            "description": "AIRNOW_API_KEY not configured",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          }
        }
      }
    },
    "/api/simcity/airnow/history.json": {
      "get": {
        "tags": ["AirNow"],
        "operationId": "getAirNowHistory",
        "summary": "Historical AQI for a specific date",
        "description": "Historical AQI observations for Davis area on a specific date. Cached permanently (historical data does not change). Requires AIRNOW_API_KEY.",
        "parameters": [
          {
            "name": "date",
            "in": "query",
            "required": true,
            "description": "Date in YYYY-MM-DD format",
            "schema": { "type": "string", "format": "date", "example": "2025-09-07" }
          }
        ],
        "responses": {
          "200": {
            "description": "Historical AQI observations",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/AirNowHistory" }
              }
            }
          },
          "400": {
            "description": "Missing date parameter",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "502": {
            "description": "Upstream unavailable",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "503": {
            "description": "AIRNOW_API_KEY not configured",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          }
        }
      }
    },
    "/api/simcity/airnow/rss/{feed_type}.xml": {
      "get": {
        "tags": ["AirNow"],
        "operationId": "getAirNowRSS",
        "summary": "Davis AirNow RSS feed",
        "description": "Proxied Davis-area AirNow RSS feeds (no API key needed). Forecast feeds cached 1 hour; realtime/actionday cached 15 minutes.",
        "parameters": [
          {
            "name": "feed_type",
            "in": "path",
            "required": true,
            "description": "RSS feed type",
            "schema": {
              "type": "string",
              "enum": ["realtime", "forecast", "actionday"]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "RSS XML feed",
            "content": {
              "application/xml": {
                "schema": { "type": "string" }
              }
            }
          },
          "404": {
            "description": "Unknown feed type",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "502": {
            "description": "RSS feed unavailable",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          }
        }
      }
    },
    "/api/simcity/airnow/contour/{pollutant}.kml": {
      "get": {
        "tags": ["AirNow"],
        "operationId": "getAirNowContour",
        "summary": "AirNow contour map KML",
        "description": "AirNow contour map KML for Sacramento Valley area (Davis bbox). Cached 1 hour. Requires AIRNOW_API_KEY.",
        "parameters": [
          {
            "name": "pollutant",
            "in": "path",
            "required": true,
            "description": "Pollutant type",
            "schema": {
              "type": "string",
              "enum": ["PM25", "Ozone", "Combined"]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "KML contour map",
            "content": {
              "application/vnd.google-earth.kml+xml": {
                "schema": { "type": "string" }
              }
            }
          },
          "404": {
            "description": "Unknown pollutant",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "502": {
            "description": "Contour map unavailable",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "503": {
            "description": "AIRNOW_API_KEY not configured",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          }
        }
      }
    },
    "/api/simcity/backfill/status": {
      "get": {
        "tags": ["Backfill"],
        "operationId": "getBackfillStatus",
        "summary": "Current backfill job status",
        "description": "Returns current backfill status from the JSON status file. Includes CEED parallel backfill status if running.",
        "responses": {
          "200": {
            "description": "Backfill status",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/BackfillStatus" }
              }
            }
          }
        }
      }
    },
    "/api/simcity/backfill/stop": {
      "post": {
        "tags": ["Backfill"],
        "operationId": "stopBackfill",
        "summary": "Stop the running backfill job",
        "description": "Writes a stop command to the backfill control file.",
        "responses": {
          "200": {
            "description": "Command acknowledged",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/BackfillCommand" }
              }
            }
          }
        }
      }
    },
    "/api/simcity/backfill/pause": {
      "post": {
        "tags": ["Backfill"],
        "operationId": "pauseBackfill",
        "summary": "Pause the running backfill job",
        "description": "Writes a pause command to the backfill control file.",
        "responses": {
          "200": {
            "description": "Command acknowledged",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/BackfillCommand" }
              }
            }
          }
        }
      }
    },
    "/api/simcity/backfill/resume": {
      "post": {
        "tags": ["Backfill"],
        "operationId": "resumeBackfill",
        "summary": "Resume a paused backfill job",
        "description": "Writes a resume command to the backfill control file.",
        "responses": {
          "200": {
            "description": "Command acknowledged",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/BackfillCommand" }
              }
            }
          }
        }
      }
    },
    "/api/tactical/config": {
      "get": {
        "tags": ["Tactical"],
        "operationId": "getTacticalConfig",
        "summary": "Tactical layer configuration",
        "description": "Returns the tactical sources configuration (ADS-B, ATAK, cameras, radios).",
        "responses": {
          "200": {
            "description": "Tactical configuration object",
            "content": {
              "application/json": {
                "schema": { "type": "object" }
              }
            }
          }
        }
      }
    },
    "/api/tactical/summary": {
      "get": {
        "tags": ["Tactical"],
        "operationId": "getTacticalSummary",
        "summary": "Aggregated tactical situational awareness",
        "description": "Combined tactical payload with ADS-B aircraft, ATAK markers, camera streams, and radio sources. Cached with configurable TTL.",
        "parameters": [
          {
            "name": "ttl_seconds",
            "in": "query",
            "required": false,
            "description": "Cache TTL in seconds (1-120, default 5)",
            "schema": { "type": "number", "minimum": 1, "maximum": 120, "default": 5 }
          },
          {
            "name": "refresh",
            "in": "query",
            "required": false,
            "description": "Force cache refresh (1/true/yes)",
            "schema": { "type": "string", "enum": ["0", "1", "true", "false"] }
          }
        ],
        "responses": {
          "200": {
            "description": "Tactical summary payload",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/TacticalSummary" }
              }
            }
          }
        }
      }
    },
    "/api/tactical/map_style/offline": {
      "get": {
        "tags": ["Tactical"],
        "operationId": "getTacticalOfflineStyle",
        "summary": "Offline MapLibre style JSON",
        "description": "Internet-free fallback MapLibre GL style with dark background. Layers are overlays from local APIs.",
        "responses": {
          "200": {
            "description": "MapLibre style JSON",
            "content": {
              "application/json": {
                "schema": { "type": "object" }
              }
            }
          }
        }
      }
    },
    "/api/tactical/buildings": {
      "get": {
        "tags": ["Tactical"],
        "operationId": "getTacticalBuildings",
        "summary": "Tactical building list (searchable)",
        "description": "Building list from Thermoostat catalog with lat/lon. Supports text search via q parameter.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Search term to filter buildings by name",
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "Building list",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/TacticalBuildingList" }
              }
            }
          }
        }
      }
    },
    "/api/tactical/buildings.geojson": {
      "get": {
        "tags": ["Tactical"],
        "operationId": "getTacticalBuildingsGeoJSON",
        "summary": "Tactical buildings as GeoJSON",
        "description": "GeoJSON FeatureCollection of all buildings from Thermoostat catalog.",
        "responses": {
          "200": {
            "description": "GeoJSON FeatureCollection",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/GeoJSONFeatureCollection" }
              }
            }
          }
        }
      }
    },
    "/api/tactical/building_focus": {
      "get": {
        "tags": ["Tactical"],
        "operationId": "getTacticalBuildingFocus",
        "summary": "Map focus parameters for a building",
        "description": "Returns map center/zoom/pitch/bearing for focusing on a specific building, plus offline mode status.",
        "parameters": [
          {
            "name": "building_key",
            "in": "query",
            "required": false,
            "description": "Thermoostat building key",
            "schema": { "type": "string" }
          },
          {
            "name": "name",
            "in": "query",
            "required": false,
            "description": "Building name (partial match)",
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "Building focus payload with map parameters",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/BuildingFocus" }
              }
            }
          },
          "404": {
            "description": "Building not found",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          }
        }
      }
    },
    "/api/tactical/building_map/{building_key}.json": {
      "get": {
        "tags": ["Tactical"],
        "operationId": "getTacticalBuildingMap",
        "summary": "Per-building map configuration",
        "description": "Returns map style URL, center coordinates, and zoom for a specific building by key.",
        "parameters": [
          {
            "name": "building_key",
            "in": "path",
            "required": true,
            "description": "Thermoostat building key",
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "Building map configuration",
            "content": {
              "application/json": {
                "schema": { "type": "object" }
              }
            }
          },
          "400": {
            "description": "Missing building key",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "404": {
            "description": "Building not found",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          }
        }
      }
    },
    "/api/tactical/building_map_manifest": {
      "get": {
        "tags": ["Tactical"],
        "operationId": "getTacticalBuildingMapManifest",
        "summary": "Manifest of all building map files",
        "description": "Returns a list of all buildings with URLs to their individual map configuration files and focus endpoints.",
        "responses": {
          "200": {
            "description": "Building map manifest",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": { "type": "integer" },
                    "items": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "building_key": { "type": "string" },
                          "name": { "type": "string" },
                          "map_file_url": { "type": "string" },
                          "focus_url": { "type": "string" }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/campus/buildings": {
      "get": {
        "tags": ["Campus"],
        "operationId": "getCampusBuildings",
        "summary": "Building list for selector dropdown",
        "description": "Returns all buildings from the building registry, used for dropdown selectors in the dashboard.",
        "responses": {
          "200": {
            "description": "Building list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "buildings": { "type": "array", "items": { "type": "object" } },
                    "count": { "type": "integer" }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/time": {
      "get": {
        "tags": ["System"],
        "operationId": "getTime",
        "summary": "NTP-synced server clock",
        "description": "Returns NTP-synchronized UTC and Pacific timestamps for frontend clock sync. Includes NTP offset and drift warnings.",
        "responses": {
          "200": {
            "description": "Server time with NTP diagnostics",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ServerTime" }
              }
            }
          }
        }
      }
    },
    "/api/docs": {
      "get": {
        "tags": ["System"],
        "operationId": "apiDocs",
        "summary": "Redirect to Swagger UI",
        "description": "Redirects to the interactive API documentation page.",
        "responses": {
          "302": {
            "description": "Redirect to /static/api-docs.html"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": { "type": "string", "description": "Error message" },
          "available": {
            "type": "array",
            "items": { "type": "string" },
            "description": "Available options (included on 404 for enumerated resources)"
          }
        },
        "required": ["error"]
      },
      "GeoJSONFeatureCollection": {
        "type": "object",
        "properties": {
          "type": { "type": "string", "enum": ["FeatureCollection"] },
          "features": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/GeoJSONFeature" }
          }
        },
        "required": ["type", "features"]
      },
      "GeoJSONFeature": {
        "type": "object",
        "properties": {
          "type": { "type": "string", "enum": ["Feature"] },
          "geometry": {
            "type": "object",
            "properties": {
              "type": { "type": "string", "description": "Point, Polygon, MultiPolygon, etc." },
              "coordinates": { "description": "GeoJSON coordinates array" }
            }
          },
          "properties": { "type": "object" }
        },
        "required": ["type", "geometry", "properties"]
      },
      "BuildingPointsGeoJSON": {
        "type": "object",
        "properties": {
          "type": { "type": "string", "enum": ["FeatureCollection"] },
          "features": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "type": { "type": "string", "enum": ["Feature"] },
                "geometry": {
                  "type": "object",
                  "properties": {
                    "type": { "type": "string", "enum": ["Point"] },
                    "coordinates": {
                      "type": "array",
                      "items": { "type": "number" },
                      "minItems": 2,
                      "maxItems": 2,
                      "description": "[longitude, latitude]"
                    }
                  }
                },
                "properties": {
                  "type": "object",
                  "properties": {
                    "name": { "type": "string" },
                    "af_name": { "type": "string", "nullable": true },
                    "sqft": { "type": "number", "nullable": true },
                    "construction_year": { "type": "integer", "nullable": true },
                    "building_type": { "type": "string", "nullable": true },
                    "primary_use": { "type": "string", "nullable": true },
                    "provider": { "type": "string", "nullable": true },
                    "leed_cert": { "type": "string", "nullable": true },
                    "has_wifi": { "type": "boolean", "nullable": true },
                    "has_solar": { "type": "boolean", "nullable": true },
                    "has_co2": { "type": "boolean", "nullable": true },
                    "has_zone_temps": { "type": "boolean", "nullable": true },
                    "remote_site": { "type": "boolean", "nullable": true },
                    "ics_code": { "type": "string", "nullable": true }
                  }
                }
              }
            }
          }
        }
      },
      "SolarArraysGeoJSON": {
        "type": "object",
        "properties": {
          "type": { "type": "string", "enum": ["FeatureCollection"] },
          "features": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "type": { "type": "string", "enum": ["Feature"] },
                "geometry": {
                  "type": "object",
                  "properties": {
                    "type": { "type": "string", "enum": ["Point"] },
                    "coordinates": {
                      "type": "array",
                      "items": { "type": "number" },
                      "minItems": 2,
                      "maxItems": 2
                    }
                  }
                },
                "properties": {
                  "type": "object",
                  "properties": {
                    "name": { "type": "string" },
                    "array_type": { "type": "string", "enum": ["parking", "farm", "rooftop"] },
                    "tags": { "type": "array", "items": { "type": "string" } },
                    "parent_building": { "type": "string", "nullable": true }
                  }
                }
              }
            }
          }
        }
      },
      "TimestampValue": {
        "type": "object",
        "properties": {
          "Timestamp": { "type": "string", "description": "ISO 8601 timestamp" },
          "Value": { "type": "number", "nullable": true }
        }
      },
      "LiveOAT": {
        "type": "object",
        "properties": {
          "oat_f": { "type": "number", "nullable": true, "description": "Outside air temperature in Fahrenheit" },
          "timestamp": { "type": "string", "nullable": true },
          "history": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/TimestampValue" },
            "description": "2-hour history at 15-min intervals"
          },
          "error": { "type": "string", "description": "Present if data unavailable" }
        }
      },
      "LiveCampus": {
        "type": "object",
        "properties": {
          "total_mw": { "type": "number", "nullable": true, "description": "Total campus demand (CHCP + PG&E) in MW" },
          "chcp_mw": { "type": "number", "nullable": true, "description": "CHCP gas plant demand in MW" },
          "pge_mw": { "type": "number", "nullable": true, "description": "PG&E grid import in MW" },
          "solar_farm_mw": { "type": "number", "nullable": true, "description": "Solar farm production in MW" },
          "rooftop_solar_mw": { "type": "number", "nullable": true, "description": "Rooftop solar production in MW" },
          "solar_mw": { "type": "number", "nullable": true, "description": "Total solar (farm + rooftop) in MW" },
          "timestamp": { "type": "string", "nullable": true },
          "error": { "type": "string", "description": "Present if data unavailable" }
        }
      },
      "LiveBuilding": {
        "type": "object",
        "properties": {
          "building": { "type": "string" },
          "demand_kw": { "type": "number", "nullable": true, "description": "Current electricity demand in kW" },
          "timestamp": { "type": "string", "nullable": true },
          "history": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/TimestampValue" },
            "description": "4-hour history at 15-min intervals"
          },
          "error": { "type": "string", "description": "Present if data unavailable" }
        }
      },
      "LiveSolar": {
        "type": "object",
        "properties": {
          "building": { "type": "string" },
          "solar_kw": { "type": "number", "nullable": true, "description": "Current solar production in kW" },
          "timestamp": { "type": "string", "nullable": true },
          "history": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/TimestampValue" },
            "description": "4-hour history at 15-min intervals"
          }
        }
      },
      "LiveWifi": {
        "type": "object",
        "properties": {
          "campus_total": { "type": "integer", "nullable": true, "description": "Total WiFi devices across campus" },
          "building_count": { "type": "integer", "nullable": true, "description": "WiFi device count for requested building" },
          "building": { "type": "string", "nullable": true },
          "source": { "type": "string", "description": "Data source: duckdb or spring_break_baseline" }
        }
      },
      "AirNowForecast": {
        "type": "object",
        "properties": {
          "forecasts": {
            "type": "array",
            "items": { "type": "object" },
            "description": "Array of AirNow forecast objects"
          },
          "retrieved_at": { "type": "string", "description": "ISO 8601 UTC timestamp of retrieval" }
        }
      },
      "AirNowHistory": {
        "type": "object",
        "properties": {
          "date": { "type": "string", "description": "Requested date (YYYY-MM-DD)" },
          "observations": {
            "type": "array",
            "items": { "type": "object" },
            "description": "Array of AirNow observation objects"
          }
        }
      },
      "BackfillStatus": {
        "type": "object",
        "properties": {
          "status": { "type": "string", "description": "Current status: idle, running, paused, etc." },
          "ceed_parallel": { "type": "object", "description": "CEED parallel backfill status if running" }
        }
      },
      "BackfillCommand": {
        "type": "object",
        "properties": {
          "ok": { "type": "boolean" },
          "command": { "type": "string", "enum": ["stop", "pause", "resume"] }
        }
      },
      "TacticalSummary": {
        "type": "object",
        "properties": {
          "generated_at_utc": { "type": "string" },
          "enabled": { "type": "boolean" },
          "adsb": {
            "type": "object",
            "properties": {
              "enabled": { "type": "boolean" },
              "protocols": { "type": "array", "items": { "type": "string" } },
              "aircraft": { "type": "array", "items": { "type": "object" } },
              "stats": { "type": "object" }
            }
          },
          "atak": {
            "type": "object",
            "properties": {
              "enabled": { "type": "boolean" },
              "markers": { "type": "array", "items": { "type": "object" } },
              "stats": { "type": "object" }
            }
          },
          "cameras": {
            "type": "object",
            "properties": {
              "enabled": { "type": "boolean" },
              "streams": { "type": "array", "items": { "type": "object" } },
              "stats": { "type": "object" }
            }
          },
          "radios": {
            "type": "object",
            "properties": {
              "enabled": { "type": "boolean" },
              "sources": { "type": "array", "items": { "type": "object" } },
              "stats": { "type": "object" }
            }
          },
          "error": { "type": "string", "nullable": true }
        }
      },
      "TacticalBuildingList": {
        "type": "object",
        "properties": {
          "count": { "type": "integer" },
          "items": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": { "type": "string" },
                "building_key": { "type": "string" },
                "name": { "type": "string" },
                "search_name": { "type": "string" },
                "lat": { "type": "number" },
                "lon": { "type": "number" }
              }
            }
          }
        }
      },
      "BuildingFocus": {
        "type": "object",
        "properties": {
          "ok": { "type": "boolean" },
          "building": { "type": "object" },
          "map": {
            "type": "object",
            "properties": {
              "style_url": { "type": "string" },
              "center": { "type": "array", "items": { "type": "number" }, "minItems": 2, "maxItems": 2 },
              "zoom": { "type": "integer" },
              "pitch": { "type": "integer" },
              "bearing": { "type": "integer" }
            }
          },
          "generated_at_utc": { "type": "string" },
          "offline_mode": { "type": "object" }
        }
      },
      "ServerTime": {
        "type": "object",
        "properties": {
          "utc": { "type": "string", "description": "UTC timestamp (ISO 8601, no timezone)" },
          "pacific": { "type": "string", "description": "Pacific time (ISO 8601, no timezone)" },
          "pacific_display": { "type": "string", "description": "Human-readable Pacific time (e.g. '3:42 PM')" },
          "tz_offset_hours": { "type": "number", "description": "Pacific UTC offset (-7 or -8)" },
          "unix": { "type": "integer", "description": "Unix timestamp (seconds)" },
          "ntp_offset_ms": { "type": "number", "nullable": true, "description": "NTP clock offset in milliseconds" },
          "ntp_server": { "type": "string", "nullable": true, "description": "Last NTP server used" },
          "ntp_drift_warning": { "type": "boolean", "description": "True if clock drift exceeds threshold" }
        }
      }
    }
  }
}
