Made the kasa integration more robust and added GetState function to get the current state

This commit is contained in:
Dreaded_X 2022-11-18 04:05:13 +01:00
parent 028ede0721
commit 2aff4d937a
Signed by: Dreaded_X
GPG Key ID: 76BDEC4E165D8AD9
3 changed files with 89 additions and 38 deletions

View File

@ -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
}

View 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"`
}

View File

@ -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"`
}