7 Commits

Author SHA1 Message Date
aac191521f 📱 Fixes responsive index
All checks were successful
Gitea Build Action / build-go (push) Successful in 1m57s
Gitea Build Action / build-nuxt (push) Successful in 11m26s
2025-07-02 10:34:13 +02:00
96fe2d04cb 💄 Adds favicon
All checks were successful
Gitea Build Action / build-go (push) Successful in 3m13s
Gitea Build Action / build-nuxt (push) Successful in 12m30s
2025-07-02 10:10:06 +02:00
d858b9e7c8 🔨 Replaces websockets with interval fetches
All checks were successful
Gitea Build Action / build-go (push) Successful in 36s
Gitea Build Action / build-nuxt (push) Successful in 10m7s
2025-06-16 11:56:20 +02:00
4b2248a6d7 🔨 Refactors imports 2025-06-07 17:06:21 +02:00
Daniel Svitan
53722bdf18 🐛 Fixes server requests
All checks were successful
Gitea Build Action / build-go (push) Successful in 27s
Gitea Build Action / build-nuxt (push) Successful in 10m40s
2025-06-07 15:49:34 +02:00
Daniel Svitan
cf35326bf2 🔧 Renames commands
All checks were successful
Gitea Build Action / build-go (push) Successful in 51s
Gitea Build Action / build-nuxt (push) Successful in 10m13s
2025-06-07 09:51:30 +02:00
Daniel Svitan
cb397b7cd3 🔨 Updates routes to new version 2025-06-07 09:21:57 +02:00
8 changed files with 97 additions and 107 deletions

View File

@@ -27,7 +27,7 @@ type ChangeLockReq struct {
} }
type ChangeAlertReq struct { type ChangeAlertReq struct {
Alert bool `json:"alert"` Alert bool `json:"alerts"`
For int `json:"for"` For int `json:"for"`
} }
@@ -64,7 +64,7 @@ func makeGetReq(url string) ([]byte, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer func() { res.Body.Close() }() defer func() { _ = res.Body.Close() }()
if res.StatusCode != 200 { if res.StatusCode != 200 {
fmt.Printf("<-- %d\n", res.StatusCode) fmt.Printf("<-- %d\n", res.StatusCode)
@@ -120,7 +120,7 @@ func main() {
Action: test, Action: test,
}, },
{ {
Name: "open", Name: "opened",
Usage: "get door state (opened or closed)", Usage: "get door state (opened or closed)",
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.BoolFlag{ &cli.BoolFlag{
@@ -131,34 +131,38 @@ func main() {
Action: getOpened, Action: getOpened,
}, },
{ {
Name: "lock", Name: "locked",
Usage: "change lock status", Usage: "change lock status",
Commands: []*cli.Command{ Commands: []*cli.Command{
{ {
Name: "on", Name: "yes",
Usage: "lock the door", Usage: "lock the door",
Action: createManageLock(true), Aliases: []string{"y", "on", "1", "do"},
Action: createManageLock(true),
}, },
{ {
Name: "off", Name: "no",
Usage: "unlock the door", Usage: "unlock the door",
Action: createManageLock(false), Aliases: []string{"n", "off", "0", "undo"},
Action: createManageLock(false),
}, },
}, },
Action: getLocked, Action: getLocked,
}, },
{ {
Name: "alert", Name: "alerts",
Usage: "change alert status", Usage: "change alert status",
Commands: []*cli.Command{ Commands: []*cli.Command{
{ {
Name: "on", Name: "yes",
Usage: "resume alerts", Usage: "resume alerts",
Action: createManageAlert(true), Aliases: []string{"y", "on", "1", "do"},
Action: createManageAlert(true),
}, },
{ {
Name: "off", Name: "no",
Usage: "pause alerts", Usage: "pause alerts",
Aliases: []string{"n", "off", "0", "undo"},
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "for", Name: "for",
@@ -195,13 +199,13 @@ func test(context.Context, *cli.Command) error {
return nil return nil
} }
func getOpened(ctx context.Context, cmd *cli.Command) error { func getOpened(_ context.Context, cmd *cli.Command) error {
err := load() err := load()
if err != nil { if err != nil {
return err return err
} }
opened, err := makeGetReq(fmt.Sprintf("%s/open", server)) opened, err := makeGetReq(fmt.Sprintf("%s/opened", server))
if err != nil { if err != nil {
return err return err
} }
@@ -225,7 +229,7 @@ func getLocked(context.Context, *cli.Command) error {
return err return err
} }
locked, err := makeGetReq(fmt.Sprintf("%s/lock", server)) locked, err := makeGetReq(fmt.Sprintf("%s/locked", server))
if err != nil { if err != nil {
return err return err
} }
@@ -245,7 +249,7 @@ func getAlert(context.Context, *cli.Command) error {
return err return err
} }
alert, err := makeGetReq(fmt.Sprintf("%s/alert", server)) alert, err := makeGetReq(fmt.Sprintf("%s/alerts", server))
if err != nil { if err != nil {
return err return err
} }
@@ -269,7 +273,7 @@ func createManageLock(locked bool) func(context.Context, *cli.Command) error {
var data ChangeLockReq var data ChangeLockReq
data.Locked = locked data.Locked = locked
_, err = makePostReq(fmt.Sprintf("%s/lock", server), data) _, err = makePostReq(fmt.Sprintf("%s/locked", server), data)
if err != nil { if err != nil {
return err return err
} }
@@ -306,7 +310,7 @@ func createManageAlert(alert bool) func(context.Context, *cli.Command) error {
data.For = secs data.For = secs
} }
_, err = makePostReq(fmt.Sprintf("%s/alert", server), data) _, err = makePostReq(fmt.Sprintf("%s/alerts", server), data)
if err != nil { if err != nil {
return err return err
} }
@@ -322,7 +326,7 @@ func createManageAlert(alert bool) func(context.Context, *cli.Command) error {
action = "paused" action = "paused"
} }
fmt.Printf("alerts were %sd%s\n", action, rest) fmt.Printf("alerts were %s%s\n", action, rest)
return nil return nil
} }
} }

View File

@@ -97,8 +97,8 @@ class App:
try: try:
r = urequests.post( r = urequests.post(
f"{self.server}/open", f"{self.server}/opened",
headers={"Authorization": self.token, "Content-Type": "application/json"}, headers={"Authorization": f"Bearer {self.token}", "Content-Type": "application/json"},
data=raw data=raw
) )
print(f"State updated [{r.status_code}] {r.content.decode()}") print(f"State updated [{r.status_code}] {r.content.decode()}")

View File

@@ -23,14 +23,13 @@ const TimeFormat = "2006-01-02 15:04:05"
var token string var token string
// the condition is: distance >= threshold (is door open) // the condition is: distance >= threshold (is door open)
var opened bool var opened = false
var openedChange = make(chan any)
// is door locked // is door locked
var locked bool = false var locked = false
// alerts the user when door locked and but open? // alerts the user when door locked and but open?
var alerts bool = false var alerts = false
var gotifyToken string var gotifyToken string
var gotifyURL string var gotifyURL string
@@ -118,7 +117,6 @@ func main() {
app.GET("/", helloWorld) app.GET("/", helloWorld)
app.GET("/opened", authed(getOpened)) app.GET("/opened", authed(getOpened))
app.GET("/opened/ws", authed(getOpenedWs))
app.POST("/opened", authed(setOpened)) app.POST("/opened", authed(setOpened))
app.GET("/locked", authed(getLocked)) app.GET("/locked", authed(getLocked))

View File

@@ -1,14 +1,12 @@
package main package main
import ( import (
"github.com/gorilla/websocket"
"github.com/labstack/echo/v4"
"github.com/labstack/gommon/log"
"net/http" "net/http"
"time" "time"
)
var upgrader = websocket.Upgrader{} "github.com/labstack/echo/v4"
"github.com/labstack/gommon/log"
)
func helloWorld(c echo.Context) error { func helloWorld(c echo.Context) error {
return c.JSON(http.StatusOK, "Hello world!") return c.JSON(http.StatusOK, "Hello world!")
@@ -22,39 +20,6 @@ func getOpened(c echo.Context) error {
return c.JSON(http.StatusOK, o) return c.JSON(http.StatusOK, o)
} }
func getOpenedWs(c echo.Context) error {
ws, err := upgrader.Upgrade(c.Response(), c.Request(), nil)
if err != nil {
return err
}
defer func() { _ = ws.Close() }()
// empty the channel
emptied := false
for !emptied {
select {
case <-openedChange:
default:
emptied = true
}
}
for {
<-openedChange
mut.Lock()
err = ws.WriteJSON(opened)
mut.Unlock()
if err != nil {
log.Error(err)
break
}
}
return err
}
func setOpened(c echo.Context) error { func setOpened(c echo.Context) error {
var data ChangeOpenReq var data ChangeOpenReq
err := c.Bind(&data) err := c.Bind(&data)
@@ -79,7 +44,6 @@ func setOpened(c echo.Context) error {
go sendAlert(action) go sendAlert(action)
} }
openedChange <- 0
mut.Unlock() mut.Unlock()
return c.NoContent(http.StatusOK) return c.NoContent(http.StatusOK)
} }
@@ -126,6 +90,7 @@ func setAlerts(c echo.Context) error {
} }
if data.For > 0 { if data.For > 0 {
log.Infof("pausing alerts for %d seconds", data.For)
go func() { go func() {
time.Sleep(time.Duration(data.For) * time.Second) time.Sleep(time.Duration(data.For) * time.Second)

View File

@@ -7,3 +7,10 @@
<style> <style>
@import "assets/css/main.css"; @import "assets/css/main.css";
</style> </style>
<script lang="ts" setup>
useHead({
title: "Door alarm",
link: [{ rel: "icon", type: "image/x-icon", href: "/favicon.ico" }],
})
</script>

View File

@@ -1,5 +1,4 @@
import type { FetchResponse } from "ofetch" import { type AxiosResponse } from "axios"
import { AxiosError, type AxiosResponse } from "axios"
export function useAPI(route: string = "/") { export function useAPI(route: string = "/") {
return `https://api.door.svitan.dev${route}` return `https://api.door.svitan.dev${route}`

View File

@@ -1,14 +1,14 @@
<template> <template>
<main <main
class="flex items-center justify-center min-w-screen min-h-screen gap-4 flex-wrap" class="flex items-center justify-center min-w-screen min-h-screen gap-4 flex-wrap flex-col md:flex-row"
> >
<div class="flex items-center justify-center flex-col gap-y-2 w-32"> <div class="flex items-center justify-center flex-col gap-y-2 w-32">
<div <div
:class="`flex items-center justify-center border-solid border-2 border-${open ? (locked ? 'error' : 'secondary') : 'primary'} rounded-lg w-full h-32`" :class="`flex items-center justify-center border-solid border-2 border-${opened ? (locked ? 'error' : 'secondary') : 'primary'} rounded-lg w-full h-32`"
> >
<UIcon <UIcon
:name=" :name="
open opened
? 'material-symbols:door-open' ? 'material-symbols:door-open'
: 'material-symbols:door-front' : 'material-symbols:door-front'
" "
@@ -17,9 +17,9 @@
</div> </div>
<p <p
:class="`flex items-center justify-center text-${open ? (locked ? 'error' : 'secondary') : 'primary'} text-xl w-full h-10`" :class="`flex items-center justify-center text-${opened ? (locked ? 'error' : 'secondary') : 'primary'} text-xl w-full h-10 border-${opened ? (locked ? 'error' : 'secondary') : 'primary'} border-solid border-2 rounded-md`"
> >
{{ open ? "Opened" : "Closed" }} {{ opened ? "Opened" : "Closed" }}
</p> </p>
</div> </div>
@@ -55,11 +55,11 @@
<div class="flex items-center justify-center flex-col gap-y-2 w-32"> <div class="flex items-center justify-center flex-col gap-y-2 w-32">
<div <div
:class="`flex items-center justify-center border-solid border-2 border-${alert ? 'primary' : 'secondary'} rounded-lg w-full h-32`" :class="`flex items-center justify-center border-solid border-2 border-${alerts ? 'primary' : 'secondary'} rounded-lg w-full h-32`"
> >
<UIcon <UIcon
:name=" :name="
alert alerts
? 'material-symbols:volume-up' ? 'material-symbols:volume-up'
: 'material-symbols:volume-off' : 'material-symbols:volume-off'
" "
@@ -69,17 +69,17 @@
<UButton <UButton
:icon=" :icon="
alert alerts
? 'material-symbols:volume-off' ? 'material-symbols:volume-off'
: 'material-symbols:volume-up' : 'material-symbols:volume-up'
" "
size="xl" size="xl"
:color="alert ? 'primary' : 'secondary'" :color="alerts ? 'primary' : 'secondary'"
variant="solid" variant="solid"
class="flex items-center justify-center w-full h-10" class="flex items-center justify-center w-full h-10"
@click="toggleAlert" @click="toggleAlert"
> >
{{ alert ? "Turn off" : "Turn on" }} {{ alerts ? "Turn off" : "Turn on" }}
</UButton> </UButton>
</div> </div>
</main> </main>
@@ -90,9 +90,9 @@ import axios from "axios"
const token = useToken() const token = useToken()
const open = ref(true) const opened = ref(false)
const locked = ref(true) const locked = ref(false)
const alert = ref(false) const alerts = ref(false)
const toast = useToast() const toast = useToast()
@@ -100,7 +100,7 @@ async function toggleLock() {
const desired = !locked.value const desired = !locked.value
axios axios
.post( .post(
useAPI("/lock"), useAPI("/locked"),
{ {
locked: desired, locked: desired,
}, },
@@ -120,15 +120,15 @@ async function toggleLock() {
} }
async function toggleAlert() { async function toggleAlert() {
const desired = !alert.value const desired = !alerts.value
axios axios
.post(useAPI("/alert"), { alert: desired }, { headers: useHeaders() }) .post(useAPI("/alerts"), { alert: desired }, { headers: useHeaders() })
.then((res) => { .then((res) => {
handleResponse(res, () => { handleResponse(res, () => {
toast.add({ toast.add({
title: `Alerts were turned ${desired ? "on" : "off"}`, title: `Alerts were turned ${desired ? "on" : "off"}`,
}) })
alert.value = desired alerts.value = desired
}) })
}) })
.catch(handleRequestError) .catch(handleRequestError)
@@ -139,26 +139,43 @@ onMounted(() => {
return navigateTo("/token") return navigateTo("/token")
} }
axios setInterval(() => {
.get<boolean>(useAPI("/lock"), { axios
headers: useHeaders(), .get<boolean>(useAPI("/opened"), {
}) headers: useHeaders(),
.then((res) => {
handleResponse(res, (response) => {
locked.value = response.data
}) })
}) .then((res) => {
.catch(handleRequestError) handleResponse(res, (res) => {
opened.value = res.data
})
})
.catch(handleRequestError)
}, 1000)
axios setInterval(() => {
.get<boolean>(useAPI("/alert"), { axios
headers: useHeaders(), .get<boolean>(useAPI("/locked"), {
}) headers: useHeaders(),
.then((res) => {
handleResponse(res, (response) => {
alert.value = response.data
}) })
}) .then((res) => {
.catch(handleRequestError) handleResponse(res, (res) => {
locked.value = res.data
})
})
.catch(handleRequestError)
}, 1000)
setInterval(() => {
axios
.get<boolean>(useAPI("/alerts"), {
headers: useHeaders(),
})
.then((res) => {
handleResponse(res, (res) => {
alerts.value = res.data
})
})
.catch(handleRequestError)
}, 1000)
}) })
</script> </script>

View File

@@ -39,7 +39,7 @@ const toast = useToast()
function onSubmit(event: FormSubmitEvent<Schema>) { function onSubmit(event: FormSubmitEvent<Schema>) {
const received = event.data.token const received = event.data.token
axios axios
.get<boolean>(useAPI("/open"), { .get<boolean>(useAPI("/opened"), {
headers: useHeaders(received), headers: useHeaders(received),
}) })
.then((res) => { .then((res) => {