2024-11-23 03:50:18 +00:00
|
|
|
package httpserver
|
|
|
|
|
|
|
|
import (
|
|
|
|
"database/sql"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"math/rand"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"git.janky.solutions/finn/lockserver/config"
|
|
|
|
"git.janky.solutions/finn/lockserver/db"
|
|
|
|
"git.janky.solutions/finn/lockserver/zwavejs"
|
|
|
|
"github.com/labstack/echo/v4"
|
|
|
|
"github.com/sirupsen/logrus"
|
|
|
|
)
|
|
|
|
|
|
|
|
func addCode(c echo.Context) error {
|
|
|
|
// name := c.FormValue("name")
|
|
|
|
code := c.FormValue("code")
|
|
|
|
|
|
|
|
queries, dbc, err := db.Get()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer dbc.Close()
|
|
|
|
|
|
|
|
ctx := c.Request().Context()
|
|
|
|
|
|
|
|
// generate a code that isn't used on any lock or if the user
|
|
|
|
// supplied a code, check if it's already used on any lock and
|
|
|
|
// error out if it is
|
|
|
|
noCodeSupplied := code == "" // if no code is supplied, we must generate one
|
|
|
|
for {
|
|
|
|
if noCodeSupplied {
|
|
|
|
code = generateCode()
|
|
|
|
logrus.WithField("code", code).Debug("generated code")
|
|
|
|
}
|
|
|
|
|
|
|
|
row, err := queries.GetLockCodesByCode(ctx, code)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error checking if code was already in use: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(row) == 0 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
if noCodeSupplied {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
return fmt.Errorf("requested code (%s) is already in use on some or all locks", code)
|
|
|
|
}
|
|
|
|
|
|
|
|
locks, err := queries.GetLocks(ctx)
|
|
|
|
if err != nil {
|
|
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
|
|
return errors.New("no locks currently registered, cannot add code")
|
|
|
|
}
|
|
|
|
return fmt.Errorf("error getting locks: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
client := c.Get(contextKeyZWaveClient).(*zwavejs.Client)
|
|
|
|
for _, lock := range locks {
|
|
|
|
slot, err := queries.GetEmptySlot(ctx, lock.ID)
|
|
|
|
if err != nil {
|
|
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
|
|
return fmt.Errorf("no empty code slots found on lock %s (ZWaveDeviceID=%d ID=%d)", lock.Name, lock.ZwaveDeviceID, lock.ID)
|
|
|
|
}
|
|
|
|
return fmt.Errorf("error looking for empty code slot on lock %s (ZWaveDeviceID=%d ID=%d): %v", lock.Name, lock.ZwaveDeviceID, lock.ID, err)
|
|
|
|
}
|
|
|
|
|
2024-11-23 05:15:21 +00:00
|
|
|
logrus.WithFields(logrus.Fields{"lock": lock.ID, "code": code}).Debug("pushing code to lock")
|
2024-11-23 03:50:18 +00:00
|
|
|
// send the code to the lock
|
|
|
|
// 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 = client.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: int(slot.ID)},
|
|
|
|
}, zwavejs.AnyType{Type: zwavejs.AnyTypeString, String: code})
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error pushing code to lock %s (ZWaveDeviceID=%d ID=%d): %v", lock.Name, lock.ZwaveDeviceID, lock.ID, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// set the code on the lock
|
|
|
|
// set the code in the db
|
|
|
|
}
|
|
|
|
// add row to user_codes
|
|
|
|
// add row to user_code_slots
|
|
|
|
// redirect to /user-codes/<code id>
|
|
|
|
|
|
|
|
return errors.New("not yet implemented")
|
|
|
|
}
|
|
|
|
|
|
|
|
var codeCharSet = "0123456789"
|
|
|
|
|
|
|
|
func generateCode() string {
|
|
|
|
var builder strings.Builder
|
|
|
|
for i := 0; i < config.C.GeneratedCodeLength; i++ {
|
|
|
|
builder.WriteByte(codeCharSet[rand.Int31n(int32(len(codeCharSet)))])
|
|
|
|
}
|
|
|
|
return builder.String()
|
|
|
|
}
|