api: implement operating locks

This commit is contained in:
lilly 2026-05-14 15:47:59 +02:00
commit 60c4770280
Signed by: lilly
SSH key fingerprint: SHA256:y9T5GFw2A20WVklhetIxG1+kcg/Ce0shnQmbu1LQ37g
3 changed files with 45 additions and 12 deletions

View file

@ -240,9 +240,37 @@ async def list_locks(
@app.patch( @app.patch(
"/api/locks/{name}", "/api/locks/{lock_id}",
tags=["locks"], tags=["locks"],
responses={status.HTTP_401_UNAUTHORIZED: {"model": models.HttpProblemDetail}}, responses={
status.HTTP_401_UNAUTHORIZED: {"model": models.HttpProblemDetail},
status.HTTP_404_NOT_FOUND: {"model": models.HttpProblemDetail},
},
)
async def operate_lock(req: Request, lock_id: str, requested_op: models.LockOperation, ccujack: deps.CCUJackClient, _current_user: deps.CurrentUser) -> None:
# TODO: Validate that the user is authorized
# find appropriate lock from ccujack
for i_lock, lock_channels in ccujack.locks:
if i_lock.identifier == lock_id:
for i_channel, channel_params in lock_channels:
if i_channel.type == "DOOR_LOCK_STATE_TRANSMITTER":
for i_param in channel_params:
if i_param.id == "LOCK_TARGET_LEVEL":
addr = f"{i_lock.address}/{i_channel.index}/{i_param.id}"
# match readable request parameter to ccujack value
match requested_op.desired_state:
case "closed":
ccujack_value = 0
case "open":
ccujack_value = 1
# write to ccujack
await ccujack.set_param_value(addr, ccujack_value)
return
else:
raise exceptions.HttpProblemException(
models.HttpProblemDetail.new_lock_not_found(lock_id, req.url)
) )
async def operate_lock(name: str):
pass

View file

@ -83,13 +83,14 @@ class CCUJackClient:
] ]
async def query_param_value(self, address): async def query_param_value(self, address: str):
logger.debug("Querying parameter value from '%s'", address) logger.debug("Querying parameter value from '%s'", address)
async with self.http.get(f"/device/{address}/~pv") as resp: async with self.http.get(f"/device/{address}/~pv") as resp:
return CCUValue.model_validate(await resp.json()) return CCUValue.model_validate(await resp.json())
# async def toggle_lock(self, lock_id, new_state): async def set_param_value(self, address: str, value: Any):
# pass logger.debug("Writing parameter value '%s' to '%s'", value, address)
await self.http.put(f"/device/{address}/~pv", json={"v": value})
async def _inspect_ccu_device(self, device_ref: CCURef) -> Tuple[CCUDeviceInfo, List[Tuple[CCUChannelInfo, List[CCUParamInfo]]]]: async def _inspect_ccu_device(self, device_ref: CCURef) -> Tuple[CCUDeviceInfo, List[Tuple[CCUChannelInfo, List[CCUParamInfo]]]]:
logger.debug("Inspecting device '%s' (%s)", device_ref.href, device_ref.title) logger.debug("Inspecting device '%s' (%s)", device_ref.href, device_ref.title)

View file

@ -13,7 +13,7 @@ class HttpProblemType(Enum):
""" """
UNAUTHORIZED = "type:noc@hamburg.ccc.de,2026:UNAUTHORIZED" UNAUTHORIZED = "type:noc@hamburg.ccc.de,2026:UNAUTHORIZED"
DOOR_NOT_FOUND = "type:noc@hamburg.ccc.de,2026:DOOR_NOT_FOUND" LOCK_NOT_FOUND = "type:noc@hamburg.ccc.de,2026:LOCK_NOT_FOUND"
class HttpProblemDetail(BaseModel): class HttpProblemDetail(BaseModel):
@ -38,12 +38,12 @@ class HttpProblemDetail(BaseModel):
) )
@classmethod @classmethod
def new_door_not_found(cls, requested_door: str, request_uri: str | URL) -> Self: def new_lock_not_found(cls, requested_lock: str, request_uri: str | URL) -> Self:
return cls( return cls(
type=HttpProblemType.DOOR_NOT_FOUND, type=HttpProblemType.LOCK_NOT_FOUND,
status=status.HTTP_404_NOT_FOUND, status=status.HTTP_404_NOT_FOUND,
title="Door not found", title="Lock not found",
detail=f"You tried to interact with door {requested_door!r} that is not known to dooris", detail=f"You tried to interact with lock {requested_lock!r} that is not known to dooris",
instance=str(request_uri), instance=str(request_uri),
) )
@ -73,3 +73,7 @@ class Lock(BaseModel):
name: str name: str
id: str id: str
status: LockStatus status: LockStatus
class LockOperation(BaseModel):
desired_state: Literal["open", "closed"]