329 lines
5.8 KiB
Go
329 lines
5.8 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"crypto/tls"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"strconv"
|
|
|
|
"github.com/spf13/viper"
|
|
"github.com/urfave/cli/v3"
|
|
)
|
|
|
|
var (
|
|
server string
|
|
token string
|
|
)
|
|
|
|
type ChangeLockReq struct {
|
|
Locked bool `json:"locked"`
|
|
}
|
|
|
|
type ChangeAlertReq struct {
|
|
Alert bool `json:"alert"`
|
|
For int `json:"for"`
|
|
}
|
|
|
|
func load() error {
|
|
viper.SetConfigName("config")
|
|
viper.SetConfigType("toml")
|
|
viper.AddConfigPath("$HOME/.local/share/door-alarm")
|
|
err := viper.ReadInConfig()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
server = viper.GetString("server")
|
|
if server == "" {
|
|
return errors.New("'server' property is required")
|
|
}
|
|
|
|
token = viper.GetString("token")
|
|
if token == "" {
|
|
return errors.New("'token' property is required")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func makeGetReq(url string) ([]byte, error) {
|
|
req, err := http.NewRequest(http.MethodGet, url, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
req.Header.Set("Authorization", token)
|
|
res, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer func() { res.Body.Close() }()
|
|
|
|
if res.StatusCode != 200 {
|
|
fmt.Printf("<-- %d\n", res.StatusCode)
|
|
}
|
|
|
|
return io.ReadAll(res.Body)
|
|
}
|
|
|
|
func makePostReq(url string, body any) ([]byte, error) {
|
|
bodyRaw, err := json.Marshal(body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(bodyRaw))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
req.Header.Set("Authorization", token)
|
|
req.Header.Set("Content-Type", "application/json")
|
|
res, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer func() { _ = res.Body.Close() }()
|
|
|
|
if res.StatusCode != 200 {
|
|
fmt.Printf("<-- %d\n", res.StatusCode)
|
|
}
|
|
|
|
return io.ReadAll(res.Body)
|
|
}
|
|
|
|
func main() {
|
|
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
|
|
|
|
cmd := &cli.Command{
|
|
Name: "doorctl",
|
|
Usage: "door-alarm server administration tool",
|
|
Commands: []*cli.Command{
|
|
{
|
|
Name: "version",
|
|
Usage: "print version and exit",
|
|
Action: func(context.Context, *cli.Command) error {
|
|
fmt.Println("version 1.1.0")
|
|
return nil
|
|
},
|
|
},
|
|
{
|
|
Name: "test",
|
|
Usage: "test server connection",
|
|
Action: test,
|
|
},
|
|
{
|
|
Name: "open",
|
|
Usage: "get door state (opened or closed)",
|
|
Flags: []cli.Flag{
|
|
&cli.BoolFlag{
|
|
Name: "raw",
|
|
Usage: "print raw server output",
|
|
},
|
|
},
|
|
Action: getOpened,
|
|
},
|
|
{
|
|
Name: "lock",
|
|
Usage: "change lock status",
|
|
Commands: []*cli.Command{
|
|
{
|
|
Name: "on",
|
|
Usage: "lock the door",
|
|
Action: createManageLock(true),
|
|
},
|
|
{
|
|
Name: "off",
|
|
Usage: "unlock the door",
|
|
Action: createManageLock(false),
|
|
},
|
|
},
|
|
Action: getLocked,
|
|
},
|
|
{
|
|
Name: "alert",
|
|
Usage: "change alert status",
|
|
Commands: []*cli.Command{
|
|
{
|
|
Name: "on",
|
|
Usage: "resume alerts",
|
|
Action: createManageAlert(true),
|
|
},
|
|
{
|
|
Name: "off",
|
|
Usage: "pause alerts",
|
|
Flags: []cli.Flag{
|
|
&cli.StringFlag{
|
|
Name: "for",
|
|
Usage: "pause for x seconds",
|
|
},
|
|
},
|
|
Action: createManageAlert(false),
|
|
},
|
|
},
|
|
Action: getAlert,
|
|
},
|
|
},
|
|
}
|
|
|
|
err := cmd.Run(context.Background(), os.Args)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func test(context.Context, *cli.Command) error {
|
|
err := load()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
url := fmt.Sprintf("%s/", server)
|
|
data, err := makeGetReq(url)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fmt.Printf("%s", data)
|
|
return nil
|
|
}
|
|
|
|
func getOpened(ctx context.Context, cmd *cli.Command) error {
|
|
err := load()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
opened, err := makeGetReq(fmt.Sprintf("%s/open", server))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if cmd.Bool("raw") {
|
|
fmt.Println(opened)
|
|
} else {
|
|
if string(opened) == "true\n" {
|
|
fmt.Println("door is open")
|
|
} else {
|
|
fmt.Println("door is closed")
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func getLocked(context.Context, *cli.Command) error {
|
|
err := load()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
locked, err := makeGetReq(fmt.Sprintf("%s/lock", server))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if string(locked) == "true\n" {
|
|
fmt.Println("door is locked")
|
|
} else {
|
|
fmt.Println("door is unlocked")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func getAlert(context.Context, *cli.Command) error {
|
|
err := load()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
alert, err := makeGetReq(fmt.Sprintf("%s/alert", server))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if string(alert) == "true\n" {
|
|
fmt.Println("alerts are active")
|
|
} else {
|
|
fmt.Println("alerts are paused")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func createManageLock(locked bool) func(context.Context, *cli.Command) error {
|
|
return func(ctx context.Context, cmd *cli.Command) error {
|
|
err := load()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var data ChangeLockReq
|
|
data.Locked = locked
|
|
|
|
_, err = makePostReq(fmt.Sprintf("%s/lock", server), data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var action string
|
|
if locked {
|
|
action = "locked"
|
|
} else {
|
|
action = "unlocked"
|
|
}
|
|
|
|
fmt.Printf("door was %s\n", action)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func createManageAlert(alert bool) func(context.Context, *cli.Command) error {
|
|
return func(ctx context.Context, cmd *cli.Command) error {
|
|
err := load()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var data ChangeAlertReq
|
|
data.Alert = alert
|
|
|
|
var secs int
|
|
secsRaw := cmd.String("for")
|
|
if secsRaw != "" {
|
|
secs, err = strconv.Atoi(secsRaw)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
data.For = secs
|
|
}
|
|
|
|
_, err = makePostReq(fmt.Sprintf("%s/alert", server), data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var rest string
|
|
if secsRaw != "" {
|
|
rest = fmt.Sprintf(" for %d seconds", secs)
|
|
}
|
|
var action string
|
|
if alert {
|
|
action = "resumed"
|
|
} else {
|
|
action = "paused"
|
|
}
|
|
|
|
fmt.Printf("alerts were %sd%s\n", action, rest)
|
|
return nil
|
|
}
|
|
}
|