feat: toggle controller state on/off

This commit is contained in:
Kaaninchen
2026-08-03 18:50:22 +02:00
parent 67162359ed
commit 3f470096ce
7 changed files with 86 additions and 11 deletions
+2 -2
View File
@@ -10,10 +10,10 @@ build:
@echo "🔧 Building binary..." @echo "🔧 Building binary..."
@cp $(CONFIG_FILE) $(BIN_DIR)/ @cp $(CONFIG_FILE) $(BIN_DIR)/
@go build -o $(BIN_DIR)/$(BIN_NAME) $(MAIN_PKG) @go build -o $(BIN_DIR)/$(BIN_NAME) $(MAIN_PKG)
@echo "Build complete: $(BIN_DIR)/$(BIN_NAME)" @echo "Build complete: $(BIN_DIR)/$(BIN_NAME)"
run: run:
@echo "🚀 Running server..." @echo "Running server..."
@go run $(MAIN_PKG) @go run $(MAIN_PKG)
clean: clean:
@echo "🧹 Cleaning up..." @echo "🧹 Cleaning up..."
+14 -2
View File
@@ -24,7 +24,7 @@ Laterna requires a config.json file in the root directory. A [example config](ht
cp config.json.example config.json cp config.json.example config.json
``` ```
You can leave the json as it is or configure it to your liking, although some configuration options (e.g. `verboseLogging`) aren't fully implemented yet. You can leave the config as it is or adjust it to your liking.
Make sure that your firewall supports connections to the configured port for laterna (default: `8080`), otherwise clients won't be able to connect to the API. You can do so by using ufw on most linux distributions Make sure that your firewall supports connections to the configured port for laterna (default: `8080`), otherwise clients won't be able to connect to the API. You can do so by using ufw on most linux distributions
@@ -75,8 +75,9 @@ The token is shown once on the first startup. To regenerate it, run the server w
#### 🎛️ Controllers #### 🎛️ Controllers
| Method | Route | Description | Payload | | Method | Route | Description | Payload |
|--------|----------------|-------------------------------|---------------------| |--------|----------------------------|--------------------------------------|------------|
| POST | `/controllers` | Create a new controller | | | POST | `/controllers` | Create a new controller | |
| POST | `/controllers/toggle/{id}` | Toggle an existing controller on/off | |
| DELETE | `/controllers` | Delete an existing controller | { "ID": 1} | | DELETE | `/controllers` | Delete an existing controller | { "ID": 1} |
@@ -126,6 +127,10 @@ $ http GET localhost:8080/api/v1/colors/1 "Authorization: $TOKEN"
$ http PUT localhost:8080/api/v1/colors/1 "Authorization: $TOKEN" Color="#C2C342" $ http PUT localhost:8080/api/v1/colors/1 "Authorization: $TOKEN" Color="#C2C342"
``` ```
#### Toggle controller on/off
```bash
$ http PUT localhost:8080/api/v1/colors/1 "Authorization: $TOKEN"
```
</details> </details>
@@ -170,6 +175,13 @@ $ curl -X PUT localhost:8080/api/v1/colors/1 \
-d '{"Color": "#C2C342"}' -d '{"Color": "#C2C342"}'
``` ```
#### Toggle controller on/off
```bash
$ curl -X POST localhost:8080/api/v1/colors/1 \
-H "Authorization:$TOKEN"
```
</details> </details>
+1
View File
@@ -34,6 +34,7 @@ func InitDB() {
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY,
token_hash TEXT, token_hash TEXT,
color TEXT, color TEXT,
active INTEGER DEFAULT 0,
updated_at DATETIME updated_at DATETIME
); );
`) `)
+26 -3
View File
@@ -71,7 +71,7 @@ func ControllerExists(id int) bool {
} }
func GetAllColors() ([]models.LampState, error) { func GetAllColors() ([]models.LampState, error) {
rows, err := DB.Query("SELECT id, color, updated_at FROM controllers WHERE id > 0") rows, err := DB.Query("SELECT id, color, active, updated_at FROM controllers WHERE id > 0")
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -81,7 +81,7 @@ func GetAllColors() ([]models.LampState, error) {
for rows.Next() { for rows.Next() {
var state models.LampState var state models.LampState
if err := rows.Scan(&state.ID, &state.Color, &state.UpdatedAt); err != nil { if err := rows.Scan(&state.ID, &state.Color, &state.Active, &state.UpdatedAt); err != nil {
return nil, err return nil, err
} }
controllers = append(controllers, state) controllers = append(controllers, state)
@@ -90,7 +90,6 @@ func GetAllColors() ([]models.LampState, error) {
if err := rows.Err(); err != nil { if err := rows.Err(); err != nil {
return nil, err return nil, err
} }
return controllers, nil return controllers, nil
} }
@@ -129,3 +128,27 @@ func SetColor(id int, color string) error {
logger.DBLogger.Printf("Lamp %d color updated to %s", id, color) logger.DBLogger.Printf("Lamp %d color updated to %s", id, color)
return nil return nil
} }
func GetControllerState(id int) (bool, error) {
row := DB.QueryRow("SELECT active FROM controllers WHERE id = ?", id)
var state bool
err := row.Scan(&state)
if err != nil {
logger.DBLogger.Print(err)
return false, err
}
return state, nil
}
func SetControllerstate(active bool, id int) error {
_, err := DB.Exec("UPDATE controllers SET active = ? WHERE id = ?", active, id)
if err != nil {
logger.DBLogger.Printf("Failed to update active state for controller %d: %v", id, err)
return err
}
logger.DBLogger.Printf("Set active state for %d to %t", id, active)
return nil
}
+6
View File
@@ -27,6 +27,7 @@ type HTTPResponse struct {
type LampState struct { type LampState struct {
ID int `json:"id"` ID int `json:"id"`
Color string `json:"color"` Color string `json:"color"`
Active bool `json:"active"`
UpdatedAt time.Time `json:"updated_at"` UpdatedAt time.Time `json:"updated_at"`
} }
@@ -45,3 +46,8 @@ type DeleteData struct {
type CreateData struct { type CreateData struct {
Created int `json:"created"` Created int `json:"created"`
} }
type ToggleController struct {
ID int `json:"id"`
Active bool `json:"active"`
}
+1
View File
@@ -24,6 +24,7 @@ func listColors(w http.ResponseWriter, r *http.Request) {
colors, err := db.GetAllColors() colors, err := db.GetAllColors()
if err != nil { if err != nil {
utils.ErrorResponse(w, http.StatusInternalServerError, err.Error()) utils.ErrorResponse(w, http.StatusInternalServerError, err.Error())
return
} }
utils.SuccessResponse(w, http.StatusOK, colors) utils.SuccessResponse(w, http.StatusOK, colors)
} }
+32
View File
@@ -13,6 +13,8 @@ import (
func RegisterControllerRoutes(mux *http.ServeMux) { func RegisterControllerRoutes(mux *http.ServeMux) {
mux.HandleFunc("POST /api/v1/controllers", middleware.WithAdminAuth(createController)) mux.HandleFunc("POST /api/v1/controllers", middleware.WithAdminAuth(createController))
mux.HandleFunc("DELETE /api/v1/controllers", middleware.WithAdminAuth(deleteController)) mux.HandleFunc("DELETE /api/v1/controllers", middleware.WithAdminAuth(deleteController))
mux.HandleFunc("POST /api/v1/controllers/toggle/{ID}", middleware.WithAdminAuth(toggleControllerState))
} }
func createController(w http.ResponseWriter, r *http.Request) { func createController(w http.ResponseWriter, r *http.Request) {
@@ -41,3 +43,33 @@ func deleteController(w http.ResponseWriter, r *http.Request) {
} }
utils.SuccessResponse(w, http.StatusOK, models.DeleteData{Deleted: req.ID}) utils.SuccessResponse(w, http.StatusOK, models.DeleteData{Deleted: req.ID})
} }
func toggleControllerState(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
idStr := r.PathValue("ID")
id, err := utils.IDtoInt((idStr))
if err != nil {
utils.ErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
if !db.ControllerExists((id)) {
utils.ErrorResponse(w, http.StatusNotFound, "Controller not found")
return
}
currentState, err := db.GetControllerState(id)
if err != nil {
utils.ErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
err = db.SetControllerstate(!currentState, id)
if err != nil {
utils.ErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
utils.SuccessResponse(w, http.StatusOK, models.ToggleController{ID: id, Active: !currentState})
}