Skip to content

Commit

Permalink
Prevent spamming devices with the same request (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
alec-pinson authored Mar 22, 2023
1 parent dd9a101 commit 615a0a8
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 9 deletions.
58 changes: 57 additions & 1 deletion cmd/meross-lan-api/apiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"strconv"
"strings"
"sync"
"time"
)

type APIServer struct{}
Expand All @@ -29,7 +30,10 @@ func (apiServer APIServer) Start() {
wg.Wait()
}

var mu sync.RWMutex

func (apiServer APIServer) Endpoint(w http.ResponseWriter, r *http.Request) {
mu.Lock()
switch path := r.URL.Path[1:]; {
case path == "deviceList" || path == "":
apiServer.deviceList(w)
Expand All @@ -42,6 +46,7 @@ func (apiServer APIServer) Endpoint(w http.ResponseWriter, r *http.Request) {
case path == "health/live" || path == "health/ready":
fmt.Fprintf(w, "ok")
}
mu.Unlock()
}

func getDevice(name string) (Device, ErrorResponse) {
Expand All @@ -52,10 +57,34 @@ func getDevice(name string) (Device, ErrorResponse) {
return d, err
}
}
err.Error = "Device not found."
err.Error = "Device '" + name + "' not found."
return device, err
}

func SetLastOn(deviceName string) {
for idx, d := range config.Devices {
if d.Name == deviceName {
config.Devices[idx].LastOn = time.Now()
}
}
}

func SetLastOff(deviceName string) {
for idx, d := range config.Devices {
if d.Name == deviceName {
config.Devices[idx].LastOff = time.Now()
}
}
}

func SetLastStatus(deviceName string) {
for idx, d := range config.Devices {
if d.Name == deviceName {
config.Devices[idx].LastStatus = time.Now()
}
}
}

func getStatusString(device Device) string {
status := getStatus(device.IP, device.Channel, config.Key)
if status == 1 {
Expand All @@ -82,8 +111,17 @@ func (apiServer APIServer) deviceStatus(w http.ResponseWriter, deviceName string
writeResponse(w, err)
return
}

if !time.Now().After(device.LastStatus.Add(config.AntiSpam)) {
log.Println("Already turned got status in the last 5 seconds")
writeResponse(w, device)
return
}

device.Status = getStatusString(device)
writeResponse(w, device)

SetLastStatus(deviceName)
}

func (apiServer APIServer) turnOn(w http.ResponseWriter, deviceName string) {
Expand All @@ -92,8 +130,17 @@ func (apiServer APIServer) turnOn(w http.ResponseWriter, deviceName string) {
writeResponse(w, err)
return
}

if !time.Now().After(device.LastOn.Add(config.AntiSpam)) {
log.Println("Already turned on in the last 5 seconds")
writeResponse(w, device)
return
}

turnOn(device.IP, device.Channel, config.Key)
apiServer.deviceStatus(w, device.Name)

SetLastOn(deviceName)
}

func (apiServer APIServer) turnOff(w http.ResponseWriter, deviceName string) {
Expand All @@ -102,6 +149,15 @@ func (apiServer APIServer) turnOff(w http.ResponseWriter, deviceName string) {
writeResponse(w, err)
return
}

if !time.Now().After(device.LastOff.Add(config.AntiSpam)) {
log.Println("Already turned off in the last 5 seconds")
writeResponse(w, device)
return
}

turnOff(device.IP, device.Channel, config.Key)
apiServer.deviceStatus(w, device.Name)

SetLastOff(deviceName)
}
24 changes: 16 additions & 8 deletions cmd/meross-lan-api/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,27 @@ import (
"log"
"os"
"strings"
"time"

"gopkg.in/yaml.v2"
)

type Config struct {
Debug bool
File string
Devices []Device `yaml:"devices"`
Key string
Debug bool
File string
Devices []Device `yaml:"devices"`
AntiSpam time.Duration `yaml:"antiDeviceSpam"`
Key string
}

type Device struct {
Name string `yaml:"name"`
IP string `yaml:"ip"`
Channel int `yaml:"channel"`
Status string
Name string `yaml:"name"`
IP string `yaml:"ip"`
Channel int `yaml:"channel"`
Status string
LastOn time.Time
LastOff time.Time
LastStatus time.Time
}

func (config Config) Load() Config {
Expand All @@ -45,6 +50,9 @@ func (config Config) Load() Config {
log.Fatal("Environment variable 'KEY' must be specified.")
}

// set default anti spam
config.AntiSpam = 5 * time.Second

// load yaml from file
yamlFile, err := ioutil.ReadFile(config.File)
if err != nil {
Expand Down
4 changes: 4 additions & 0 deletions cmd/meross-lan-api/meross.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ func sendRequest(deviceIp string, body Request) (Response, error) {
// convert body to bytes
bodyJSON, _ := json.Marshal(body)

if config.Debug {
return result, nil
}

// create and send request
req, err := http.NewRequest("POST", "http://"+deviceIp+"/config", bytes.NewBuffer(bodyJSON))
req.Header.Add("Content-Type", "application/json; charset=UTF-8")
Expand Down

0 comments on commit 615a0a8

Please sign in to comment.