Add support for editing individual lock code slots
Some checks failed
/ build-container (push) Has been cancelled

This commit is contained in:
Finn 2024-11-22 22:25:39 -08:00
parent 6ec7434ab6
commit cafbd63f98
7 changed files with 44 additions and 8 deletions

View file

@ -130,7 +130,7 @@ func (q *Queries) GetLockCodesByCode(ctx context.Context, code string) ([]LockCo
} }
const upsertCodeSlot = `-- name: UpsertCodeSlot :exec const upsertCodeSlot = `-- name: UpsertCodeSlot :exec
INSERT INTO lock_code_slots (lock, slot, code, enabled, name) VALUES (?, ?, ?, ?, "") ON CONFLICT (lock, slot) DO UPDATE SET code=excluded.code, enabled=excluded.enabled INSERT INTO lock_code_slots (lock, slot, code, enabled, name) VALUES (?, ?, ?, ?, ?) ON CONFLICT (lock, slot) DO UPDATE SET code=excluded.code, enabled=excluded.enabled, name=excluded.name
` `
type UpsertCodeSlotParams struct { type UpsertCodeSlotParams struct {
@ -138,6 +138,7 @@ type UpsertCodeSlotParams struct {
Slot int64 Slot int64
Code string Code string
Enabled bool Enabled bool
Name string
} }
func (q *Queries) UpsertCodeSlot(ctx context.Context, arg UpsertCodeSlotParams) error { func (q *Queries) UpsertCodeSlot(ctx context.Context, arg UpsertCodeSlotParams) error {
@ -146,6 +147,7 @@ func (q *Queries) UpsertCodeSlot(ctx context.Context, arg UpsertCodeSlotParams)
arg.Slot, arg.Slot,
arg.Code, arg.Code,
arg.Enabled, arg.Enabled,
arg.Name,
) )
return err return err
} }

View file

@ -1,5 +1,5 @@
-- name: UpsertCodeSlot :exec -- name: UpsertCodeSlot :exec
INSERT INTO lock_code_slots (lock, slot, code, enabled, name) VALUES (?, ?, ?, ?, "") ON CONFLICT (lock, slot) DO UPDATE SET code=excluded.code, enabled=excluded.enabled; INSERT INTO lock_code_slots (lock, slot, code, enabled, name) VALUES (?, ?, ?, ?, ?) ON CONFLICT (lock, slot) DO UPDATE SET code=excluded.code, enabled=excluded.enabled, name=excluded.name;
-- name: GetLockCodeBySlot :one -- name: GetLockCodeBySlot :one
SELECT * FROM lock_code_slots WHERE lock = ? AND slot = ?; SELECT * FROM lock_code_slots WHERE lock = ? AND slot = ?;

View file

@ -10,4 +10,5 @@
<body> <body>
<div id="wrapper"> <div id="wrapper">
<a href="/">Home</a>
<div id="main"> <div id="main">

View file

@ -1,7 +1,10 @@
{{ template "header.html" . }} {{ template "header.html" . }}
<header>Rename {{ if eq .Data.Name "" }}Lock #{{ .Data.ID }}{{ else }}{{ .Data.Name }}{{ end }}</header> <header>{{ if eq .Data.Name "" }}Lock #{{ .Data.ID }}{{ else }}{{ .Data.Name }}{{ end }}</header>
<form method="post"> <form method="post">
Code: <input type="text" name="code" value="{{ .Data.Code }}" /><br />
Name: <input type="text" name="name" value="{{ .Data.Name }}" /><br /> Name: <input type="text" name="name" value="{{ .Data.Name }}" /><br />
Enabled: <input type="checkbox" name="enabled" {{ if .Data.Enabled }}checked{{ end }} /><br />
<br />
<input type="submit" value="save" /> <input type="submit" value="save" />
</form> </form>
{{ template "footer.html" }} {{ template "footer.html" }}

View file

@ -1,10 +1,11 @@
{{ template "header.html" . }} {{ template "header.html" . }}
<header>{{ if eq .Data.lock.Name "" }}Lock #{{ .Data.lock.ID }}{{ else }}{{ .Data.lock.Name }}{{ end }}</header> <header>{{ if eq .Data.lock.Name "" }}Lock #{{ .Data.lock.ID }}{{ else }}{{ .Data.lock.Name }}{{ end }}</header>
[ <a href="/locks/{{ .Data.lock.ID }}/edit">rename</a> ] [ <a href="/locks/{{ .Data.lock.ID }}/edit">rename</a> ]<br />
<br /> <br />
<table border="1"> <table border="1">
<tr> <tr>
<td>Slot</td> <td>Slot</td>
<td>Name</td>
<td>Code</td> <td>Code</td>
<td>Enabled?</td> <td>Enabled?</td>
<td>Actions</td> <td>Actions</td>
@ -12,6 +13,7 @@
{{ range $_, $code := .Data.codes }} {{ range $_, $code := .Data.codes }}
<tr class="code-{{ if $code.Enabled }}enabled{{ else }}disabled{{ end }}"> <tr class="code-{{ if $code.Enabled }}enabled{{ else }}disabled{{ end }}">
<td>{{ $code.Slot }}</td> <td>{{ $code.Slot }}</td>
<td>{{ $code.Name }}</td>
<td>{{ $code.Code }}</td> <td>{{ $code.Code }}</td>
<td>{{ if $code.Enabled }}enabled{{ else }}disabled{{ end }}</td> <td>{{ if $code.Enabled }}enabled{{ else }}disabled{{ end }}</td>
<td>[ <a href="/locks/{{ $.Data.lock.ID }}/codes/{{ $code.Slot }}">edit</a> ]</td> <td>[ <a href="/locks/{{ $.Data.lock.ID }}/codes/{{ $code.Slot }}">edit</a> ]</td>

View file

@ -111,9 +111,27 @@ func lockCodeEditHandler(c echo.Context) error {
return err return err
} }
newCode := c.FormValue("code")
zwaveClient := c.Get(contextKeyZWaveClient).(*zwavejs.Client) zwaveClient := c.Get(contextKeyZWaveClient).(*zwavejs.Client)
enabled := c.FormValue("enabled") == "on"
enabledInt := 0
if enabled {
enabledInt = 1
}
err = zwaveClient.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: int(slot)},
}, zwavejs.AnyType{Type: zwavejs.AnyTypeInt, Int: enabledInt})
if err != nil {
return fmt.Errorf("error pushing enabled state to lock %s (ZWaveDeviceID=%d ID=%d): %v", lock.Name, lock.ZwaveDeviceID, lock.ID, err)
}
newCode := c.FormValue("code")
err = zwaveClient.SetNodeValue(ctx, int(lock.ZwaveDeviceID), zwavejs.NodeValue{ err = zwaveClient.SetNodeValue(ctx, int(lock.ZwaveDeviceID), zwavejs.NodeValue{
CCVersion: 1, CCVersion: 1,
CommandClassName: zwavejs.CommandClassNameUserCode, CommandClassName: zwavejs.CommandClassNameUserCode,
@ -131,7 +149,8 @@ func lockCodeEditHandler(c echo.Context) error {
Lock: lockID, Lock: lockID,
Slot: slot, Slot: slot,
Code: newCode, Code: newCode,
Enabled: true, Name: c.FormValue("name"),
Enabled: enabled,
}) })
if err != nil { if err != nil {
return err return err

View file

@ -43,8 +43,10 @@ func (c *Client) DialAndListen(ctx context.Context) {
for { for {
logrus.WithField("server", c.Server).Info("connecting to zwave-js server") logrus.WithField("server", c.Server).Info("connecting to zwave-js server")
ctxWithTimeout, cancel := context.WithTimeout(ctx, time.Second*5)
defer cancel()
conn, err := failsafe.Get(func() (*websocket.Conn, error) { conn, err := failsafe.Get(func() (*websocket.Conn, error) {
conn, _, err := websocket.DefaultDialer.DialContext(ctx, c.Server, nil) conn, _, err := websocket.DefaultDialer.DialContext(ctxWithTimeout, c.Server, nil)
return conn, err return conn, err
}, connectRetryPolicy) }, connectRetryPolicy)
if err != nil { if err != nil {
@ -63,6 +65,7 @@ func (c *Client) DialAndListen(ctx context.Context) {
continue continue
} }
_ = c.conn.Close() _ = c.conn.Close()
cancel()
} }
} }
@ -234,6 +237,8 @@ func (c *Client) handleCallback(messageID string, result Result) error {
c.callbacksLock.Lock() c.callbacksLock.Lock()
defer c.callbacksLock.Unlock() defer c.callbacksLock.Unlock()
logrus.WithField("message_id", messageID).Debug(fmt.Sprintf("%+v", result))
cb, ok := c.callbacks[messageID] cb, ok := c.callbacks[messageID]
if !ok { if !ok {
logrus.WithField("message_id", messageID).Warn("got response to a message we didn't send") logrus.WithField("message_id", messageID).Warn("got response to a message we didn't send")
@ -255,6 +260,10 @@ func (c *Client) sendMessage(message OutgoingMessageIface) (Result, error) {
c.callbacks[messageID] = ch c.callbacks[messageID] = ch
c.callbacksLock.Unlock() c.callbacksLock.Unlock()
// w := logrus.WithField("message_id", messageID).Writer()
// json.NewEncoder(w).Encode(message)
// w.Close()
if err := c.conn.WriteJSON(message); err != nil { if err := c.conn.WriteJSON(message); err != nil {
return Result{}, err return Result{}, err
} }