initial commit
This commit is contained in:
		
							
								
								
									
										41
									
								
								cmd/tpstate/config.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								cmd/tpstate/config.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | |||||||
|  | package main | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"os" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/hashicorp/logutils" | ||||||
|  | 	"github.com/kelvins/sunrisesunset" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type configStructure struct { | ||||||
|  | 	// time configuration | ||||||
|  | 	TimeFormat string         `json:"time_format"` | ||||||
|  | 	TimeZone   *time.Location `json:"time_zone"` | ||||||
|  |  | ||||||
|  | 	// logging | ||||||
|  | 	LogLevel int                   `json:"log_level"` | ||||||
|  | 	Log      *logutils.LevelFilter `json:"log_level_fileter"` | ||||||
|  |  | ||||||
|  | 	//sunriseset configuration | ||||||
|  | 	SunRiseSet *sunrisesunset.Parameters `json:"sun_rise_set_configuration"` | ||||||
|  |  | ||||||
|  | 	// mode of operation | ||||||
|  | 	CalculateDate time.Time `json:"sunrise_sunset_calculation_date"` | ||||||
|  | 	SecondsUntil bool `json:"seconds_until"` | ||||||
|  | 	NextSunriseSunset bool `json:"next_sunrise_sunset"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Set Defaults | ||||||
|  | var config = configStructure{ | ||||||
|  | 	LogLevel:   50, | ||||||
|  | 	TimeFormat: "2006-01-02 15:04:05", | ||||||
|  | 	Log: &logutils.LevelFilter{ | ||||||
|  | 		Levels: []logutils.LogLevel{"TRACE", "DEBUG", "INFO", "WARNING", "ERROR"}, | ||||||
|  | 		Writer: os.Stderr, | ||||||
|  | 	}, | ||||||
|  | 	SunRiseSet: &sunrisesunset.Parameters{ | ||||||
|  | 		Latitude:  38.749020, | ||||||
|  | 		Longitude: -90.521360, | ||||||
|  | 	}, | ||||||
|  | } | ||||||
							
								
								
									
										136
									
								
								cmd/tpstate/init.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								cmd/tpstate/init.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,136 @@ | |||||||
|  | package main | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"flag" | ||||||
|  | 	"log" | ||||||
|  | 	"os" | ||||||
|  | 	"strconv" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/hashicorp/logutils" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func getEnvFloat64(env string, def float64) (val float64) { | ||||||
|  | 	osVal := os.Getenv(env) | ||||||
|  |  | ||||||
|  | 	if osVal == "" { | ||||||
|  | 		return def | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var err error | ||||||
|  | 	if val, err = strconv.ParseFloat(osVal, 64); err != nil { | ||||||
|  | 		log.Fatalf("[ERROR] Unable to parse floating point number from environment variable (%s): %v", env, err) | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func getEnvInt(env string, def int) (ret int) { | ||||||
|  | 	val := os.Getenv(env) | ||||||
|  |  | ||||||
|  | 	if val == "" { | ||||||
|  | 		return def | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ret, err := strconv.Atoi(val) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Fatalf("[ERROR] Environment variable is not of numeric type: %v\n", env) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func getEnvString(env, def string) (val string) { | ||||||
|  | 	val = os.Getenv(env) | ||||||
|  |  | ||||||
|  | 	if val == "" { | ||||||
|  | 		return def | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func getEnvBool(env string, def bool) (val bool) { | ||||||
|  | 	osVal := os.Getenv(env) | ||||||
|  |  | ||||||
|  | 	if osVal == "" { | ||||||
|  | 		return def | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var err error | ||||||
|  | 	if val, err = strconv.ParseBool(osVal); err != nil { | ||||||
|  | 		log.Fatalf("[ERROR] Environment variable is not of boolean type: %v\n", env) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func setLogLevel(l int) { | ||||||
|  | 	switch { | ||||||
|  | 	case l <= 20: | ||||||
|  | 		config.Log.SetMinLevel(logutils.LogLevel("ERROR")) | ||||||
|  | 	case l > 20 && l <= 40: | ||||||
|  | 		config.Log.SetMinLevel(logutils.LogLevel("WARNING")) | ||||||
|  | 	case l > 40 && l <= 60: | ||||||
|  | 		config.Log.SetMinLevel(logutils.LogLevel("INFO")) | ||||||
|  | 	case l > 60 && l <= 80: | ||||||
|  | 		config.Log.SetMinLevel(logutils.LogLevel("DEBUG")) | ||||||
|  | 	case l > 80: | ||||||
|  | 		config.Log.SetMinLevel(logutils.LogLevel("TRACE")) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func initialize() { | ||||||
|  | 	var ( | ||||||
|  | 		tz  string | ||||||
|  | 		dt  string | ||||||
|  | 		err error | ||||||
|  | 	) | ||||||
|  |  | ||||||
|  | 	flag.IntVar(&config.LogLevel, | ||||||
|  | 		"log", | ||||||
|  | 		getEnvInt("LOG_LEVEL", 0), | ||||||
|  | 		"(LOG_LEVEL) Set the log verbosity.", | ||||||
|  | 	) | ||||||
|  | 	flag.Float64Var(&config.SunRiseSet.Latitude, | ||||||
|  | 		"latitude", | ||||||
|  | 		getEnvFloat64("LATITUDE", 38.749020), | ||||||
|  | 		"(LATITUDE) Latitude for the sunset/sunrise calculation.", | ||||||
|  | 	) | ||||||
|  | 	flag.Float64Var(&config.SunRiseSet.Longitude, | ||||||
|  | 		"longitude", | ||||||
|  | 		getEnvFloat64("LONGITUDE", -90.521360), | ||||||
|  | 		"(LONGITUDE) Longitude for the sunrise/sunset calculation.", | ||||||
|  | 	) | ||||||
|  | 	flag.StringVar(&tz, | ||||||
|  | 		"timezone", | ||||||
|  | 		getEnvString("TIMEZONE", "America/Chicago"), | ||||||
|  | 		"(TIMEZONE) Timezone for the sunrise/sunset calculation.", | ||||||
|  | 	) | ||||||
|  | 	flag.StringVar(&dt, | ||||||
|  | 		"date", | ||||||
|  | 		getEnvString("DATE", time.Now().Format(config.TimeFormat)), | ||||||
|  | 		"(DATE) Date to use for sunrise/sunset calculation.", | ||||||
|  | 	) | ||||||
|  | 	flag.BoolVar(&config.NextSunriseSunset, | ||||||
|  | 		"next", | ||||||
|  | 		getEnvBool("NEXT", true), | ||||||
|  | 		"(NEXT) Determine and calculate the next occuring sunrise/sunset date & time.", | ||||||
|  | 	) | ||||||
|  | 	flag.Parse() | ||||||
|  |  | ||||||
|  | 	setLogLevel(config.LogLevel) | ||||||
|  | 	log.SetOutput(config.Log) | ||||||
|  |  | ||||||
|  | 	config.CalculateDate, err = time.Parse(config.TimeFormat, dt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Fatalf("[ERROR] Unable to parse time: %v\n", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	config.TimeZone, err = time.LoadLocation(tz) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Fatalf("[ERROR] Unable to parse time zone: %v\n", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	_, tzOffset := config.CalculateDate.In(config.TimeZone).Zone() | ||||||
|  | 	config.SunRiseSet.UtcOffset = float64(tzOffset / 60 / 60) | ||||||
|  | } | ||||||
							
								
								
									
										14
									
								
								cmd/tpstate/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								cmd/tpstate/main.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | package main | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"log" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func main() { | ||||||
|  | 	initialize() | ||||||
|  |  | ||||||
|  | 	_, _, err := nextSunriseSunsetTime(config.CalculateDate) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Fatalf("[ERROR] Unable to calculate sunrise/sunset: %v\n", err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										44
									
								
								cmd/tpstate/sunCalculations.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								cmd/tpstate/sunCalculations.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | |||||||
|  | package main | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"log" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func nextSunriseSunsetTime(t time.Time) (time.Time, time.Time, error) { | ||||||
|  | 	s := config.SunRiseSet | ||||||
|  |  | ||||||
|  | 	s.Date = t | ||||||
|  | 	currentSR, currentSS, err := s.GetSunriseSunset() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return time.Time{}, time.Time{}, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	s.Date = t.Add(24 * time.Hour) | ||||||
|  | 	nextSR, nextSS, err := s.GetSunriseSunset() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return time.Time{}, time.Time{}, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var ( | ||||||
|  | 		nSR time.Time | ||||||
|  | 		nSS time.Time | ||||||
|  | 	) | ||||||
|  |  | ||||||
|  | 	if currentSR.After(t) { | ||||||
|  | 		nSR = currentSR | ||||||
|  | 	} else { | ||||||
|  | 		nSR = nextSR | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if currentSS.After(t) { | ||||||
|  | 		nSS = currentSS | ||||||
|  | 	} else { | ||||||
|  | 		nSS = nextSS | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	log.Printf("[TRACE] Next calculated sunrise: %s\n", nSR.Format("2006-01-02 15:04:05")) | ||||||
|  | 	log.Printf("[TRACE] Next calculated sunset : %s\n", nSS.Format("2006-01-02 15:04:05")) | ||||||
|  |  | ||||||
|  | 	return nSR, nSS, nil | ||||||
|  | } | ||||||
							
								
								
									
										8
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | module tplink | ||||||
|  |  | ||||||
|  | go 1.17 | ||||||
|  |  | ||||||
|  | require ( | ||||||
|  | 	github.com/hashicorp/logutils v1.0.0 | ||||||
|  | 	github.com/kelvins/sunrisesunset v0.0.0-20210220141756-39fa1bd816d5 | ||||||
|  | ) | ||||||
							
								
								
									
										4
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | |||||||
|  | github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= | ||||||
|  | github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= | ||||||
|  | github.com/kelvins/sunrisesunset v0.0.0-20210220141756-39fa1bd816d5 h1:ouekCqYkMw4QXFCaLyYqjBe99/MUW4Qf3DJhCRh1G18= | ||||||
|  | github.com/kelvins/sunrisesunset v0.0.0-20210220141756-39fa1bd816d5/go.mod h1:3oZ7G+fb8Z8KF+KPHxeDO3GWpEjgvk/f+d/yaxmDRT4= | ||||||
							
								
								
									
										249
									
								
								internal/tplink/tplink.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										249
									
								
								internal/tplink/tplink.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,249 @@ | |||||||
|  | package tplink | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bufio" | ||||||
|  | 	"bytes" | ||||||
|  | 	"fmt" | ||||||
|  | 	"io" | ||||||
|  | 	"log" | ||||||
|  | 	"net" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"encoding/binary" | ||||||
|  | 	"encoding/json" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // This is the key by which all bytes sent/received from tp-link hardware are | ||||||
|  | // obfuscated. | ||||||
|  | const hashKey byte = 0xAB | ||||||
|  |  | ||||||
|  | // SysInfo A type used to return information from tplink devices | ||||||
|  | type SysInfo struct { | ||||||
|  | 	System struct { | ||||||
|  | 		GetSysinfo struct { | ||||||
|  | 			SwVer      string `json:"sw_ver"` | ||||||
|  | 			HwVer      string `json:"hw_ver"` | ||||||
|  | 			Type       string `json:"type"` | ||||||
|  | 			Model      string `json:"model"` | ||||||
|  | 			Mac        string `json:"mac"` | ||||||
|  | 			DevName    string `json:"dev_name"` | ||||||
|  | 			Alias      string `json:"alias"` | ||||||
|  | 			RelayState int    `json:"relay_state"` | ||||||
|  | 			OnTime     int    `json:"on_time"` | ||||||
|  | 			ActiveMode string `json:"active_mode"` | ||||||
|  | 			Feature    string `json:"feature"` | ||||||
|  | 			Updating   int    `json:"updating"` | ||||||
|  | 			IconHash   string `json:"icon_hash"` | ||||||
|  | 			Rssi       int    `json:"rssi"` | ||||||
|  | 			LedOff     int    `json:"led_off"` | ||||||
|  | 			LongitudeI int    `json:"longitude_i"` | ||||||
|  | 			LatitudeI  int    `json:"latitude_i"` | ||||||
|  | 			HwID       string `json:"hwId"` | ||||||
|  | 			FwID       string `json:"fwId"` | ||||||
|  | 			DeviceID   string `json:"deviceId"` | ||||||
|  | 			OemID      string `json:"oemId"` | ||||||
|  | 			Children   []struct { | ||||||
|  | 				ID         string `json:"id"` | ||||||
|  | 				State      int    `json:"state"` | ||||||
|  | 				Alias      string `json:"alias"` | ||||||
|  | 				OnTime     int    `json:"on_time"` | ||||||
|  | 				NextAction struct { | ||||||
|  | 					Type int `json:"type"` | ||||||
|  | 				} `json:"next_action"` | ||||||
|  | 			} `json:"children"` | ||||||
|  | 			ChildNum   int `json:"child_num"` | ||||||
|  | 			NtcState   int `json:"ntc_state"` | ||||||
|  | 			NextAction struct { | ||||||
|  | 				Type int `json:"type"` | ||||||
|  | 			} `json:"next_action"` | ||||||
|  | 			ErrCode   int     `json:"err_code"` | ||||||
|  | 			MicType   string  `json:"mic_type"` | ||||||
|  | 			Latitude  float64 `json:"latitude"` | ||||||
|  | 			Longitude float64 `json:"longitude"` | ||||||
|  | 		} `json:"get_sysinfo"` | ||||||
|  | 	} `json:"system"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Tplink Device host identification | ||||||
|  | type Tplink struct { | ||||||
|  | 	Host     string | ||||||
|  | 	SwitchID int | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type getSysInfo struct { | ||||||
|  | 	System struct { | ||||||
|  | 		SysInfo struct { | ||||||
|  | 		} `json:"get_sysinfo"` | ||||||
|  | 	} `json:"system"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type changeState struct { | ||||||
|  | 	System struct { | ||||||
|  | 		SetRelayState struct { | ||||||
|  | 			State int `json:"state"` | ||||||
|  | 		} `json:"set_relay_state"` | ||||||
|  | 	} `json:"system"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type changeStateMultiSwitch struct { | ||||||
|  | 	Context struct { | ||||||
|  | 		ChildIds []string `json:"child_ids"` | ||||||
|  | 	} `json:"context"` | ||||||
|  | 	System struct { | ||||||
|  | 		SetRelayState struct { | ||||||
|  | 			State int `json:"state"` | ||||||
|  | 		} `json:"set_relay_state"` | ||||||
|  | 	} `json:"system"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func encrypt(plaintext string) []byte { | ||||||
|  | 	n := len(plaintext) | ||||||
|  | 	buf := new(bytes.Buffer) | ||||||
|  | 	if err := binary.Write(buf, binary.BigEndian, uint32(n)); err != nil { | ||||||
|  | 		log.Printf("[WARNING] tplink.go: Unable to write to buffer %v\n", err) | ||||||
|  | 	} | ||||||
|  | 	ciphertext := buf.Bytes() | ||||||
|  |  | ||||||
|  | 	key := hashKey | ||||||
|  | 	payload := make([]byte, n) | ||||||
|  | 	for i := 0; i < n; i++ { | ||||||
|  | 		payload[i] = plaintext[i] ^ key | ||||||
|  | 		key = payload[i] | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for i := 0; i < len(payload); i++ { | ||||||
|  | 		ciphertext = append(ciphertext, payload[i]) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return ciphertext | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func decrypt(ciphertext []byte) string { | ||||||
|  | 	n := len(ciphertext) | ||||||
|  | 	key := hashKey | ||||||
|  | 	var nextKey byte | ||||||
|  | 	for i := 0; i < n; i++ { | ||||||
|  | 		nextKey = ciphertext[i] | ||||||
|  | 		ciphertext[i] ^= key | ||||||
|  | 		key = nextKey | ||||||
|  | 	} | ||||||
|  | 	return string(ciphertext) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func send(host string, dataSend []byte) ([]byte, error) { | ||||||
|  | 	var header = make([]byte, 4) | ||||||
|  |  | ||||||
|  | 	// establish connection (two second timeout) | ||||||
|  | 	conn, err := net.DialTimeout("tcp", host+":9999", time.Second*2) //nolint:gomnd | ||||||
|  | 	if err != nil { | ||||||
|  | 		return []byte{}, err | ||||||
|  | 	} | ||||||
|  | 	defer func() { | ||||||
|  | 		if err := conn.Close(); err != nil { //nolint:govet | ||||||
|  | 			log.Fatalf("[ERROR] tplink.go: Unable to close connection: %v\n", err) | ||||||
|  | 		} | ||||||
|  | 	}() | ||||||
|  |  | ||||||
|  | 	// submit data to device | ||||||
|  | 	writer := bufio.NewWriter(conn) | ||||||
|  | 	_, err = writer.Write(dataSend) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return []byte{}, err | ||||||
|  | 	} | ||||||
|  | 	if err := writer.Flush(); err != nil { //nolint:govet | ||||||
|  | 		return []byte{}, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// read response header to determine response size | ||||||
|  | 	headerReader := io.LimitReader(conn, int64(4)) //nolint:gomnd | ||||||
|  | 	_, err = headerReader.Read(header) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return []byte{}, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// read response | ||||||
|  | 	respSize := int64(binary.BigEndian.Uint32(header)) | ||||||
|  | 	respReader := io.LimitReader(conn, respSize) | ||||||
|  | 	var response = make([]byte, respSize) | ||||||
|  | 	_, err = respReader.Read(response) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return []byte{}, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return response, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func getDevID(s *Tplink) (string, error) { | ||||||
|  | 	info, err := s.SystemInfo() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 	return info.System.GetSysinfo.DeviceID, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SystemInfo Returns information from targeted device | ||||||
|  | func (s *Tplink) SystemInfo() (SysInfo, error) { | ||||||
|  | 	var ( | ||||||
|  | 		payload  getSysInfo | ||||||
|  | 		jsonResp SysInfo | ||||||
|  | 	) | ||||||
|  |  | ||||||
|  | 	j, _ := json.Marshal(payload) | ||||||
|  |  | ||||||
|  | 	data := encrypt(string(j)) | ||||||
|  | 	resp, err := send(s.Host, data) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return jsonResp, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err := json.Unmarshal([]byte(decrypt(resp)), &jsonResp); err != nil { | ||||||
|  | 		return jsonResp, err | ||||||
|  | 	} | ||||||
|  | 	return jsonResp, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ChangeState changes the power state of a single port device | ||||||
|  | // True = on | ||||||
|  | // False = off | ||||||
|  | func (s *Tplink) ChangeState(state bool) error { | ||||||
|  | 	var payload changeState | ||||||
|  |  | ||||||
|  | 	if state { | ||||||
|  | 		payload.System.SetRelayState.State = 1 | ||||||
|  | 	} else { | ||||||
|  | 		payload.System.SetRelayState.State = 0 | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	j, _ := json.Marshal(payload) | ||||||
|  | 	data := encrypt(string(j)) | ||||||
|  | 	if _, err := send(s.Host, data); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ChangeStateMultiSwitch changes the power state of a device on with multiple outlets/switches | ||||||
|  | // True = on | ||||||
|  | // False = off | ||||||
|  | func (s *Tplink) ChangeStateMultiSwitch(state bool) error { | ||||||
|  | 	var payload changeStateMultiSwitch | ||||||
|  |  | ||||||
|  | 	devID, err := getDevID(s) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	payload.Context.ChildIds = append(payload.Context.ChildIds, devID+fmt.Sprintf("%02d", s.SwitchID)) | ||||||
|  | 	if state { | ||||||
|  | 		payload.System.SetRelayState.State = 1 | ||||||
|  | 	} else { | ||||||
|  | 		payload.System.SetRelayState.State = 0 | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	j, _ := json.Marshal(payload) | ||||||
|  | 	data := encrypt(string(j)) | ||||||
|  | 	if _, err := send(s.Host, data); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user