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"]