mirror of
https://github.com/RichieCahill/dotfiles.git
synced 2026-04-17 13:08:19 -04:00
86 lines
2.7 KiB
Python
86 lines
2.7 KiB
Python
"""FastAPI heater control service."""
|
|
|
|
import logging
|
|
from collections.abc import AsyncIterator
|
|
from contextlib import asynccontextmanager
|
|
from typing import Annotated
|
|
|
|
import typer
|
|
import uvicorn
|
|
from fastapi import FastAPI, HTTPException
|
|
|
|
from python.common import configure_logger
|
|
from python.heater.controller import HeaterController
|
|
from python.heater.models import ActionResult, DeviceConfig, HeaterStatus
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def create_app(config: DeviceConfig) -> FastAPI:
|
|
"""Create FastAPI application."""
|
|
|
|
@asynccontextmanager
|
|
async def lifespan(app: FastAPI) -> AsyncIterator[None]:
|
|
app.state.controller = HeaterController(config)
|
|
yield
|
|
|
|
app = FastAPI(
|
|
title="Heater Control API",
|
|
description="Fast local control for Tuya heater",
|
|
lifespan=lifespan,
|
|
)
|
|
|
|
@app.get("/status")
|
|
def get_status() -> HeaterStatus:
|
|
return app.state.controller.status()
|
|
|
|
@app.post("/on")
|
|
def heater_on() -> ActionResult:
|
|
result = app.state.controller.turn_on()
|
|
if not result.success:
|
|
raise HTTPException(status_code=500, detail=result.error)
|
|
return result
|
|
|
|
@app.post("/off")
|
|
def heater_off() -> ActionResult:
|
|
result = app.state.controller.turn_off()
|
|
if not result.success:
|
|
raise HTTPException(status_code=500, detail=result.error)
|
|
return result
|
|
|
|
@app.post("/toggle")
|
|
def heater_toggle() -> ActionResult:
|
|
result = app.state.controller.toggle()
|
|
if not result.success:
|
|
raise HTTPException(status_code=500, detail=result.error)
|
|
return result
|
|
|
|
return app
|
|
|
|
|
|
def serve(
|
|
host: Annotated[str, typer.Option("--host", "-h", help="Host to bind to")],
|
|
port: Annotated[int, typer.Option("--port", "-p", help="Port to bind to")] = 8124,
|
|
log_level: Annotated[str, typer.Option("--log-level", "-l", help="Log level")] = "INFO",
|
|
device_id: Annotated[str | None, typer.Option("--device-id", envvar="TUYA_DEVICE_ID")] = None,
|
|
device_ip: Annotated[str | None, typer.Option("--device-ip", envvar="TUYA_DEVICE_IP")] = None,
|
|
local_key: Annotated[str | None, typer.Option("--local-key", envvar="TUYA_LOCAL_KEY")] = None,
|
|
) -> None:
|
|
"""Start the heater control API server."""
|
|
configure_logger(log_level)
|
|
|
|
logger.info("Starting heater control API server")
|
|
|
|
if not device_id or not device_ip or not local_key:
|
|
error = "Must provide device ID, IP, and local key"
|
|
raise typer.Exit(error)
|
|
|
|
config = DeviceConfig(device_id=device_id, ip=device_ip, local_key=local_key)
|
|
|
|
app = create_app(config)
|
|
uvicorn.run(app, host=host, port=port)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
typer.run(serve)
|