diff --git a/cmd/meross-lan-api/apiserver.go b/cmd/meross-lan-api/apiserver.go index 2e6d466..df9a5ee 100644 --- a/cmd/meross-lan-api/apiserver.go +++ b/cmd/meross-lan-api/apiserver.go @@ -7,6 +7,7 @@ import ( "strconv" "strings" "sync" + "time" ) type APIServer struct{} @@ -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) @@ -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) { @@ -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 { @@ -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) { @@ -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) { @@ -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) } diff --git a/cmd/meross-lan-api/config.go b/cmd/meross-lan-api/config.go index 0d6db8a..72e62d4 100644 --- a/cmd/meross-lan-api/config.go +++ b/cmd/meross-lan-api/config.go @@ -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 { @@ -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 { diff --git a/cmd/meross-lan-api/meross.go b/cmd/meross-lan-api/meross.go index 85e2614..2704822 100644 --- a/cmd/meross-lan-api/meross.go +++ b/cmd/meross-lan-api/meross.go @@ -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")