update api, operate doors
This commit is contained in:
parent
d1106fd3ed
commit
d19a26e128
4 changed files with 154 additions and 11 deletions
|
|
@ -55,6 +55,23 @@ export interface paths {
|
|||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/auth/logout": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
/** Logout */
|
||||
get: operations["logout_auth_logout_get"];
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/api/locks/": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
|
|
@ -72,6 +89,23 @@ export interface paths {
|
|||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/api/locks/{lock_id}": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get?: never;
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
/** Operate Lock */
|
||||
patch: operations["operate_lock_api_locks__lock_id__patch"];
|
||||
trace?: never;
|
||||
};
|
||||
}
|
||||
export type webhooks = Record<string, never>;
|
||||
export interface components {
|
||||
|
|
@ -101,13 +135,23 @@ export interface components {
|
|||
* @description Statically known HTTP problem types using the [type URI scheme](https://datatracker.ietf.org/doc/rfc4151/)
|
||||
* @enum {string}
|
||||
*/
|
||||
HttpProblemType: "type:noc@hamburg.ccc.de,2026:UNAUTHORIZED" | "type:noc@hamburg.ccc.de,2026:DOOR_NOT_FOUND";
|
||||
HttpProblemType: "type:noc@hamburg.ccc.de,2026:UNAUTHORIZED" | "type:noc@hamburg.ccc.de,2026:LOCK_NOT_FOUND";
|
||||
/** Lock */
|
||||
Lock: {
|
||||
/** Name */
|
||||
name: string;
|
||||
/** Id */
|
||||
id: string;
|
||||
status: components["schemas"]["LockStatus"];
|
||||
};
|
||||
/** LockOperation */
|
||||
LockOperation: {
|
||||
/**
|
||||
* Desired State
|
||||
* @enum {string}
|
||||
*/
|
||||
desired_state: "open" | "closed";
|
||||
};
|
||||
/** LockStatus */
|
||||
LockStatus: {
|
||||
/** Is Unreachable */
|
||||
|
|
@ -241,6 +285,24 @@ export interface operations {
|
|||
};
|
||||
};
|
||||
};
|
||||
logout_auth_logout_get: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
302: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content?: never;
|
||||
};
|
||||
};
|
||||
};
|
||||
list_locks_api_locks__get: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
|
|
@ -270,4 +332,57 @@ export interface operations {
|
|||
};
|
||||
};
|
||||
};
|
||||
operate_lock_api_locks__lock_id__patch: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path: {
|
||||
lock_id: string;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["LockOperation"];
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": unknown;
|
||||
};
|
||||
};
|
||||
/** @description Unauthorized */
|
||||
401: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["HttpProblemDetail"];
|
||||
};
|
||||
};
|
||||
/** @description Not Found */
|
||||
404: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["HttpProblemDetail"];
|
||||
};
|
||||
};
|
||||
/** @description Validation Error */
|
||||
422: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["HTTPValidationError"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ async function checkUser() {
|
|||
const {data: userInfo} = await getUserInfo({})
|
||||
|
||||
apiError.current = null
|
||||
auth.authenticated = userInfo.is_logged_in
|
||||
auth.authenticated = true
|
||||
auth.authorized = userInfo.is_authorized
|
||||
auth.until = userInfo.guaranteed_session_until ? new Date(userInfo.guaranteed_session_until) : null
|
||||
auth.username = userInfo.username ?? ""
|
||||
|
|
@ -98,7 +98,9 @@ async function checkUser() {
|
|||
}
|
||||
|
||||
async function fetchDoors() {
|
||||
loading.doors = true
|
||||
if (doors.length === 0) {
|
||||
loading.doors = true
|
||||
}
|
||||
refresh()
|
||||
const getDoors = fetcher.path("/api/locks/").method("get").create()
|
||||
|
||||
|
|
@ -123,7 +125,7 @@ async function fetchDoors() {
|
|||
}
|
||||
|
||||
doors.push({
|
||||
id: door.name, // TODO: replace by actual ID
|
||||
id: door.id,
|
||||
label: door.name,
|
||||
state: state,
|
||||
batteryLow: door.status.is_low_battery,
|
||||
|
|
@ -157,9 +159,25 @@ async function fetchDoors() {
|
|||
}
|
||||
}
|
||||
|
||||
function setDoorInfo(doorElement: HTMLDivElement, door: DoorType) {
|
||||
async function doorAction(action: "unlock" | "lock", doorId: string) {
|
||||
const operateDoors = fetcher.path("/api/locks/{lock_id}").method("patch").create()
|
||||
|
||||
const stateMap: Record<string, "open" | "closed"> = {
|
||||
unlock: "open",
|
||||
lock: "closed",
|
||||
}
|
||||
|
||||
operateDoors({
|
||||
lock_id: doorId,
|
||||
desired_state: stateMap[action],
|
||||
})
|
||||
}
|
||||
|
||||
function setDoorInfo(doorElement: HTMLDivElement, door: DoorType, initial: boolean = false) {
|
||||
const labelElement: HTMLDivElement = doorElement.querySelector("[data-label]")!
|
||||
const buttonElements: Array<HTMLButtonElement> = Array.from(doorElement.querySelectorAll("button"))
|
||||
const lockButton: HTMLButtonElement = doorElement.querySelector("[data-button='lock']")!
|
||||
const unlockButton: HTMLButtonElement = doorElement.querySelector("[data-button='unlock']")!
|
||||
const buttonElements: Array<HTMLButtonElement> = [lockButton, unlockButton]
|
||||
|
||||
doorElement.dataset.id = door.id
|
||||
doorElement.dataset.state = door.state
|
||||
|
|
@ -190,6 +208,16 @@ function setDoorInfo(doorElement: HTMLDivElement, door: DoorType) {
|
|||
|
||||
labelElement.innerHTML = door.label
|
||||
|
||||
if (initial) {
|
||||
lockButton.addEventListener("click", () => {
|
||||
doorAction("lock", door.id)
|
||||
})
|
||||
|
||||
unlockButton.addEventListener("click", () => {
|
||||
doorAction("unlock", door.id)
|
||||
})
|
||||
}
|
||||
|
||||
buttonElements.forEach(button => {
|
||||
button.disabled = ["unlocking", "locking"].includes(door.state) || apiError.current !== null
|
||||
})
|
||||
|
|
@ -205,7 +233,7 @@ function refresh() {
|
|||
|
||||
const targetElement = document.importNode(template.content, true)
|
||||
doorElement = targetElement.querySelector("[data-state]")!
|
||||
setDoorInfo(doorElement, door)
|
||||
setDoorInfo(doorElement, door, true)
|
||||
list.appendChild(targetElement)
|
||||
}
|
||||
|
||||
|
|
@ -233,9 +261,8 @@ function refresh() {
|
|||
}
|
||||
|
||||
|
||||
|
||||
loadAuthFromLocalStorage()
|
||||
fetchDoors()
|
||||
setInterval(fetchDoors, 250) // TODO: replace with SSE
|
||||
checkUser()
|
||||
|
||||
document.addEventListener("loadeddata", () => {
|
||||
|
|
|
|||
|
|
@ -44,10 +44,10 @@ const t = useTranslations(lang)
|
|||
</div>
|
||||
</div>
|
||||
<div class="hidden group-data-active:grid grid-cols-2 w-full items-center gap-2 mt-4">
|
||||
<button class="btn btn-outline btn-error">
|
||||
<button class="btn btn-outline btn-error" data-button="lock">
|
||||
{t("button.close")}
|
||||
</button>
|
||||
<button class="btn btn-outline btn-success">
|
||||
<button class="btn btn-outline btn-success" data-button="unlock">
|
||||
{t("button.open")}
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue