Made the kasa integration more robust and added GetState function to get the current state
This commit is contained in:
parent
028ede0721
commit
2aff4d937a
|
@ -5,9 +5,13 @@ import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"net"
|
"net"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// This implementation is based on:
|
||||||
|
// https://www.softscheck.com/en/blog/tp-link-reverse-engineering/
|
||||||
|
|
||||||
func encrypt(data []byte) []byte {
|
func encrypt(data []byte) []byte {
|
||||||
var key byte = 171
|
var key byte = 171
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
|
@ -22,18 +26,23 @@ func encrypt(data []byte) []byte {
|
||||||
return buf.Bytes()
|
return buf.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
func decrypt(data []byte) string {
|
func decrypt(data []byte) ([]byte, error) {
|
||||||
var key byte = 171
|
var key byte = 171
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
binary.Write(buf, binary.BigEndian, uint32(len(data)))
|
|
||||||
|
|
||||||
for _, c := range data {
|
if len(data) < 4 {
|
||||||
a := key ^ c
|
return nil, fmt.Errorf("Array has a minumun size of 4")
|
||||||
key = c
|
|
||||||
buf.WriteByte(a)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return string(buf.Bytes())
|
size := binary.BigEndian.Uint32(data[0:4])
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
for i := 0; i < int(size); i++ {
|
||||||
|
a := key ^ data[i+4]
|
||||||
|
key = data[i+4]
|
||||||
|
buf[i] = a
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -45,44 +54,79 @@ func New(ip string) Kasa {
|
||||||
return Kasa{ip}
|
return Kasa{ip}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (kasa *Kasa) sendCmd(cmd cmd) {
|
func (kasa *Kasa) sendCmd(cmd cmd) (reply, error) {
|
||||||
con, err := net.Dial("tcp", fmt.Sprintf("%s:9999", kasa.ip))
|
con, err := net.Dial("tcp", fmt.Sprintf("%s:9999", kasa.ip))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return reply{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer con.Close()
|
defer con.Close()
|
||||||
|
|
||||||
b, err := json.Marshal(cmd)
|
b, err := json.Marshal(cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return reply{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = con.Write(encrypt(b))
|
_, err = con.Write(encrypt(b))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return reply{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := make([]byte, 2048)
|
resp := make([]byte, 2048)
|
||||||
_, err = con.Read(resp)
|
_, err = con.Read(resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return reply{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
d, err := decrypt(resp)
|
||||||
|
if err != nil {
|
||||||
|
return reply{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var reply reply
|
var reply reply
|
||||||
json.Unmarshal(resp, &reply)
|
err = json.Unmarshal(d, &reply)
|
||||||
|
if err != nil {
|
||||||
if reply.System.SetRelayState.ErrCode != 0 {
|
return reply, err
|
||||||
fmt.Println(reply)
|
|
||||||
fmt.Println(resp)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return reply, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (kasa *Kasa) SetState(on bool) {
|
func (kasa *Kasa) SetState(on bool) {
|
||||||
var cmd cmd
|
var cmd cmd
|
||||||
|
cmd.System.SetRelayState = &SetRelayState{State: 0}
|
||||||
if on {
|
if on {
|
||||||
cmd.System.SetRelayState.State = 1
|
cmd.System.SetRelayState.State = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
kasa.sendCmd(cmd)
|
reply, err := kasa.sendCmd(cmd)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if reply.System.SetRelayState.ErrCode != 0 {
|
||||||
|
log.Printf("Failed to set relay state, error: %d\n", reply.System.SetRelayState.ErrCode)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (kasa *Kasa) GetState() bool {
|
||||||
|
cmd := cmd{}
|
||||||
|
|
||||||
|
cmd.System.GetSysinfo = &GetSysinfo{}
|
||||||
|
|
||||||
|
reply, err := kasa.sendCmd(cmd)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if reply.System.GetSysinfo.ErrCode != 0 {
|
||||||
|
log.Printf("Failed to set relay state, error: %d\n", reply.System.GetSysinfo.ErrCode)
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return reply.System.GetSysinfo.RelayState == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
26
integration/kasa/message.go
Normal file
26
integration/kasa/message.go
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
package kasa
|
||||||
|
|
||||||
|
type reply struct {
|
||||||
|
System struct {
|
||||||
|
SetRelayState struct {
|
||||||
|
ErrCode int `json:"err_code"`
|
||||||
|
} `json:"set_relay_state"`
|
||||||
|
GetSysinfo struct {
|
||||||
|
RelayState int `json:"relay_state"`
|
||||||
|
ErrCode int `json:"err_code"`
|
||||||
|
} `json:"get_sysinfo"`
|
||||||
|
} `json:"system"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SetRelayState struct {
|
||||||
|
State int `json:"state"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetSysinfo struct{}
|
||||||
|
|
||||||
|
type cmd struct {
|
||||||
|
System struct {
|
||||||
|
SetRelayState *SetRelayState `json:"set_relay_state,omitempty"`
|
||||||
|
GetSysinfo *GetSysinfo `json:"get_sysinfo,omitempty"`
|
||||||
|
} `json:"system"`
|
||||||
|
}
|
|
@ -1,19 +0,0 @@
|
||||||
package kasa
|
|
||||||
|
|
||||||
type errCode struct {
|
|
||||||
ErrCode int
|
|
||||||
}
|
|
||||||
|
|
||||||
type reply struct {
|
|
||||||
System struct {
|
|
||||||
SetRelayState errCode `json:"set_relay_state"`
|
|
||||||
} `json:"system"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type cmd struct {
|
|
||||||
System struct {
|
|
||||||
SetRelayState struct {
|
|
||||||
State int `json:"state"`
|
|
||||||
} `json:"set_relay_state"`
|
|
||||||
} `json:"system"`
|
|
||||||
}
|
|
Reference in New Issue
Block a user