HTTP endpoints to get and set codes

This commit is contained in:
Finn 2024-04-09 16:18:43 -07:00
parent 47a45b8000
commit 7fdd02bd33
17 changed files with 914 additions and 141 deletions

53
httpserver/server.go Normal file
View file

@ -0,0 +1,53 @@
package httpserver
import (
"context"
"database/sql"
"errors"
"net/http"
echo "github.com/labstack/echo/v4"
"github.com/sirupsen/logrus"
"git.janky.solutions/finn/lockserver/config"
"git.janky.solutions/finn/lockserver/openapi"
"git.janky.solutions/finn/lockserver/zwavejs"
)
var server *echo.Echo
type lockserver struct {
ZWaveJS *zwavejs.Client
}
func ListenAndServe(client *zwavejs.Client) {
server = echo.New()
server.HideBanner = true
server.HidePort = true
server.HTTPErrorHandler = handleError
openapi.RegisterHandlersWithBaseURL(server, lockserver{ZWaveJS: client}, "/api")
err := server.Start(config.C.HTTPBind)
if err != http.ErrServerClosed {
logrus.WithError(err).Fatal("error starting http server")
}
}
func handleError(err error, c echo.Context) {
if errors.Is(err, sql.ErrNoRows) {
_ = c.JSON(http.StatusNotFound, map[string]string{"error": "not found"})
return
}
logrus.WithError(err).Error("error handling request")
_ = c.JSON(http.StatusInternalServerError, map[string]string{"error": "internal server error"})
}
func Shutdown(ctx context.Context) error {
if server == nil {
return nil
}
return server.Shutdown(ctx)
}

112
httpserver/slots.go Normal file
View file

@ -0,0 +1,112 @@
package httpserver
import (
"net/http"
echo "github.com/labstack/echo/v4"
"git.janky.solutions/finn/lockserver/db"
"git.janky.solutions/finn/lockserver/openapi"
"git.janky.solutions/finn/lockserver/zwavejs"
)
func (lockserver) GetLockCodeSlot(c echo.Context, lock int, slot int) error {
queries, dbc, err := db.Get()
if err != nil {
return err
}
defer dbc.Close()
slotData, err := queries.GetLockCodeBySlot(c.Request().Context(), db.GetLockCodeBySlotParams{
Lock: int64(lock),
Slot: int64(slot),
})
if err != nil {
return err
}
return c.JSON(http.StatusFound, slotData.OpenAPI())
}
func (l lockserver) PutLockCodeSlot(c echo.Context, lockID int, slot int) error {
queries, dbc, err := db.Get()
if err != nil {
return err
}
defer dbc.Close()
var body openapi.LockCodeSlot
if err := c.Bind(&body); err != nil {
return err
}
ctx := c.Request().Context()
lock, err := queries.GetLock(ctx, int64(lockID))
if err != nil {
return err
}
// sample from https://github.com/FutureTense/keymaster/blob/f4f1046bddb7901cbd3ce7820886be1ff7895fe7/tests/test_services.py#L88
//
// {
// "ccVersion": 1,
// "commandClassName": "User Code",
// "commandClass": 99,
// "endpoint": 0,
// "property": "userCode",
// "propertyName": "userCode",
// "propertyKey": 1,
// "propertyKeyName": "1",
// "metadata": {
// "type": "string",
// "readable": True,
// "writeable": True,
// "minLength": 4,
// "maxLength": 10,
// "label": "User Code (1)",
// },
// "value": "123456",
// }
err = l.ZWaveJS.SetNodeValue(ctx, int(lock.ZwaveDeviceID), zwavejs.NodeValue{
CCVersion: 1,
CommandClassName: zwavejs.CommandClassNameUserCode,
CommandClass: zwavejs.CommandClassUserCode,
Endpoint: 0,
Property: zwavejs.AnyType{Type: zwavejs.AnyTypeString, String: string(zwavejs.PropertyUserCode)},
PropertyName: zwavejs.AnyType{Type: zwavejs.AnyTypeString, String: string(zwavejs.PropertyUserCode)},
PropertyKey: zwavejs.AnyType{Type: zwavejs.AnyTypeInt, Int: slot},
}, zwavejs.AnyType{Type: zwavejs.AnyTypeString, String: body.Code})
if err != nil {
return err
}
enabled := 0
if body.Enabled {
enabled = 1
}
err = l.ZWaveJS.SetNodeValue(ctx, int(lock.ZwaveDeviceID), zwavejs.NodeValue{
CCVersion: 1,
CommandClassName: zwavejs.CommandClassNameUserCode,
CommandClass: zwavejs.CommandClassUserCode,
Endpoint: 0,
Property: zwavejs.AnyType{Type: zwavejs.AnyTypeString, String: string(zwavejs.PropertyUserIDStatus)},
PropertyName: zwavejs.AnyType{Type: zwavejs.AnyTypeString, String: string(zwavejs.PropertyUserIDStatus)},
PropertyKey: zwavejs.AnyType{Type: zwavejs.AnyTypeInt, Int: slot},
}, zwavejs.AnyType{Type: zwavejs.AnyTypeInt, Int: enabled})
if err != nil {
return err
}
err = queries.UpsertCodeSlot(ctx, db.UpsertCodeSlotParams{
Lock: lock.ID,
Slot: int64(slot),
Code: body.Code,
Enabled: body.Enabled,
})
if err != nil {
return err
}
return c.JSON(http.StatusOK, body)
}