183 lines
5.4 KiB
Python
183 lines
5.4 KiB
Python
from fastapi import APIRouter, Depends, HTTPException, status
|
|
from sqlalchemy.orm import Session
|
|
from typing import List, Optional
|
|
from datetime import datetime
|
|
import secrets
|
|
|
|
from database import get_db
|
|
from schemas import Device as DeviceSchema, DeviceCreate, DeviceUpdate, BootstrapResponse
|
|
from models import Device as DeviceModel
|
|
from database import Content as ContentModel
|
|
from mqtt_manager import mqtt_manager
|
|
|
|
router = APIRouter(
|
|
prefix="/api/devices",
|
|
tags=["devices"]
|
|
)
|
|
|
|
@router.post("/", response_model=DeviceSchema, status_code=status.HTTP_201_CREATED)
|
|
async def create_device(device: DeviceCreate, db: Session = Depends(get_db)):
|
|
"""
|
|
注册新设备
|
|
"""
|
|
# 检查设备ID是否已存在
|
|
db_device = db.query(DeviceModel).filter(DeviceModel.device_id == device.device_id).first()
|
|
if db_device:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="设备ID已存在"
|
|
)
|
|
|
|
# 创建新设备
|
|
secret = device.secret if device.secret else secrets.token_urlsafe(32)
|
|
db_device = DeviceModel(
|
|
device_id=device.device_id,
|
|
secret=secret,
|
|
name=device.name,
|
|
scene=device.scene
|
|
)
|
|
|
|
db.add(db_device)
|
|
db.commit()
|
|
db.refresh(db_device)
|
|
|
|
# 订阅设备状态
|
|
mqtt_manager.subscribe_to_device_status(device.device_id)
|
|
|
|
return db_device
|
|
|
|
@router.get("/", response_model=List[DeviceSchema])
|
|
async def list_devices(
|
|
skip: int = 0,
|
|
limit: int = 100,
|
|
scene: Optional[str] = None,
|
|
is_active: Optional[bool] = None,
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""
|
|
获取设备列表
|
|
"""
|
|
query = db.query(DeviceModel)
|
|
|
|
if scene:
|
|
query = query.filter(DeviceModel.scene == scene)
|
|
|
|
if is_active is not None:
|
|
query = query.filter(DeviceModel.is_active == is_active)
|
|
|
|
devices = query.offset(skip).limit(limit).all()
|
|
return devices
|
|
|
|
@router.get("/{device_id}", response_model=DeviceSchema)
|
|
async def get_device(device_id: str, db: Session = Depends(get_db)):
|
|
"""
|
|
获取设备详情
|
|
"""
|
|
device = db.query(DeviceModel).filter(DeviceModel.device_id == device_id).first()
|
|
if not device:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail="设备不存在"
|
|
)
|
|
return device
|
|
|
|
@router.put("/{device_id}", response_model=DeviceSchema)
|
|
async def update_device(
|
|
device_id: str,
|
|
device_update: DeviceUpdate,
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""
|
|
更新设备信息
|
|
"""
|
|
device = db.query(DeviceModel).filter(DeviceModel.device_id == device_id).first()
|
|
if not device:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail="设备不存在"
|
|
)
|
|
|
|
# 更新设备信息
|
|
update_data = device_update.model_dump(exclude_unset=True)
|
|
for field, value in update_data.items():
|
|
setattr(device, field, value)
|
|
|
|
device.updated_at = datetime.utcnow()
|
|
db.commit()
|
|
db.refresh(device)
|
|
|
|
return device
|
|
|
|
@router.delete("/{device_id}", status_code=status.HTTP_204_NO_CONTENT)
|
|
async def delete_device(device_id: str, db: Session = Depends(get_db)):
|
|
"""
|
|
删除设备
|
|
"""
|
|
device = db.query(DeviceModel).filter(DeviceModel.device_id == device_id).first()
|
|
if not device:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail="设备不存在"
|
|
)
|
|
|
|
# 取消订阅设备状态
|
|
mqtt_manager.unsubscribe_from_device_status(device_id)
|
|
|
|
db.delete(device)
|
|
db.commit()
|
|
|
|
@router.get("/{device_id}/bootstrap", response_model=BootstrapResponse)
|
|
async def device_bootstrap(device_id: str, db: Session = Depends(get_db)):
|
|
"""
|
|
设备启动获取当前版本信息
|
|
"""
|
|
device = db.query(DeviceModel).filter(DeviceModel.device_id == device_id).first()
|
|
if not device:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail="设备不存在"
|
|
)
|
|
|
|
# 更新设备最后在线时间
|
|
device.last_online = datetime.utcnow()
|
|
db.commit()
|
|
|
|
# 获取最新的活跃内容
|
|
latest_content = db.query(ContentModel).filter(
|
|
ContentModel.device_id == device_id,
|
|
ContentModel.is_active == True
|
|
).order_by(ContentModel.version.desc()).first()
|
|
|
|
response = BootstrapResponse(
|
|
device_id=device_id,
|
|
timezone=latest_content.timezone if latest_content else "Asia/Shanghai",
|
|
time_format=latest_content.time_format if latest_content else "%Y-%m-%d %H:%M"
|
|
)
|
|
|
|
if latest_content:
|
|
response.content_version = latest_content.version
|
|
response.last_updated = latest_content.created_at
|
|
|
|
return response
|
|
|
|
@router.get("/{device_id}/status")
|
|
async def get_device_status(device_id: str, db: Session = Depends(get_db)):
|
|
"""
|
|
获取设备状态
|
|
"""
|
|
device = db.query(DeviceModel).filter(DeviceModel.device_id == device_id).first()
|
|
if not device:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail="设备不存在"
|
|
)
|
|
|
|
return {
|
|
"device_id": device_id,
|
|
"name": device.name,
|
|
"scene": device.scene,
|
|
"is_active": device.is_active,
|
|
"last_online": device.last_online,
|
|
"created_at": device.created_at,
|
|
"updated_at": device.updated_at
|
|
} |