dooris/api/src/dooris_api/models.py

79 lines
2.2 KiB
Python

from typing import Optional, Self, Literal
from datetime import datetime
from pydantic import BaseModel, HttpUrl
from enum import Enum
from simple_openid_connect.data import IdToken
from starlette.datastructures import URL
from fastapi import status
class HttpProblemType(Enum):
"""
Statically known HTTP problem types using the [type URI scheme](https://datatracker.ietf.org/doc/rfc4151/)
"""
UNAUTHORIZED = "type:noc@hamburg.ccc.de,2026:UNAUTHORIZED"
LOCK_NOT_FOUND = "type:noc@hamburg.ccc.de,2026:LOCK_NOT_FOUND"
class HttpProblemDetail(BaseModel):
"""
API Error modeled after [RFC9475](https://www.rfc-editor.org/rfc/rfc9457.html).
"""
status: int
type: HttpProblemType
title: str
detail: str
instance: Optional[HttpUrl]
@classmethod
def new_unauthorized(cls, request_uri: str | URL) -> Self:
return cls(
type=HttpProblemType.UNAUTHORIZED,
status=status.HTTP_401_UNAUTHORIZED,
title="Unauthorized",
detail="You tried to access a ressource which requires authentication but you are not authenticated",
instance=HttpUrl(str(request_uri)),
)
@classmethod
def new_lock_not_found(cls, requested_lock: str, request_uri: str | URL) -> Self:
return cls(
type=HttpProblemType.LOCK_NOT_FOUND,
status=status.HTTP_404_NOT_FOUND,
title="Lock not found",
detail=f"You tried to interact with lock {requested_lock!r} that is not known to dooris",
instance=str(request_uri),
)
class CurrentUser(BaseModel):
id_token: IdToken
raw_id_token: str
class UserStatus(BaseModel):
is_logged_in: bool
is_authorized: bool
guaranteed_session_until: Optional[datetime]
username: Optional[str]
class LockStatus(BaseModel):
is_unreachable: bool
is_low_battery: bool
is_error_jammed: bool
lock_target_level: Literal["locked", "unlocked", "open"]
lock_state: Literal["unknown", "locked", "unlocked"]
activity_state: Literal["unknown", "locking", "unlocking", "stable"]
class Lock(BaseModel):
name: str
id: str
status: LockStatus
class LockOperation(BaseModel):
desired_state: Literal["open", "closed"]