This commit is contained in:
Josh Deprez 2023-01-06 16:29:05 +11:00
parent 6b1a0cdf93
commit 6eda162a70
No known key found for this signature in database
3 changed files with 133 additions and 0 deletions

3
go.mod Normal file
View File

@ -0,0 +1,3 @@
module gitea.drjosh.dev/josh/plugctl
go 1.19

76
plug.go Normal file
View File

@ -0,0 +1,76 @@
// Package plugctl provides an API for Kasa smart home switches.
package plugctl
import (
"io"
"net"
)
// Plug provides an API for a single Kasa smart home switch.
type Plug struct {
Addr net.TCPAddr
}
type msg struct {
System *systemMsg `json:"system,omitempty"`
EMeter *eMeterMsg `json:"emeter,omitempty"`
}
type eMeterMsg struct {
GetRealtime *getRealtimeMsg `json:"get_realtime"`
}
type getRealtimeMsg struct{}
type systemMsg struct {
GetSysInfo *getSysInfoMsg `json:"get_sysinfo"`
SetRelayState *setRelayStateMsg `json:"set_relay_state,omitempty"`
}
type getSysInfoMsg struct{}
type setRelayStateMsg struct {
State int `json:"state"`
}
type decryptReader struct {
r io.Reader
c byte
}
func newDecrypter(r io.Reader) *decryptReader {
return &decryptReader{
r: r,
c: 0xab,
}
}
func (d *decryptReader) Read(b []byte) (int, error) {
n, err := d.r.Read(b)
for i, x := range b {
b[i] = d.c ^ x
d.c = x
}
return n, err
}
type encryptWriter struct {
w io.Writer
c byte
}
func newEncrypter(w io.Writer) *encryptWriter {
return &encryptWriter{
w: w,
c: 0xab,
}
}
func (e *encryptWriter) Write(b []byte) (int, error) {
eb := make([]byte, len(b))
for i, x := range b {
e.c ^= x
eb[i] = e.c
}
return e.w.Write(eb)
}

54
plug_test.go Normal file
View File

@ -0,0 +1,54 @@
package plugctl
import (
"bytes"
"encoding/base64"
"io"
"testing"
)
func TestDecrypt(t *testing.T) {
// on message from hs100.sh
in64 := "AAAAKtDygfiL/5r31e+UtsWg1Iv5nPCR6LfEsNGlwOLYo4HyhueT9tTu36Lfog=="
in, err := base64.StdEncoding.DecodeString(in64)
if err != nil {
t.Fatalf("base64.DecodeString(%q) error: %v", in64, err)
}
in = in[4:]
want := []byte(`{"system":{"set_relay_state":{"state":1}}}`)
dec := newDecrypter(bytes.NewReader(in))
got, err := io.ReadAll(dec)
if err != nil {
t.Fatalf("io.ReadAll(decryptReader) error: %v", err)
}
if !bytes.Equal(got, want) {
t.Errorf("decrypt(%02x) = %q, want %q", in, got, want)
}
}
func TestEncrypt(t *testing.T) {
// on message from hs100.sh
want64 := "AAAAKtDygfiL/5r31e+UtsWg1Iv5nPCR6LfEsNGlwOLYo4HyhueT9tTu36Lfog=="
want, err := base64.StdEncoding.DecodeString(want64)
if err != nil {
t.Fatalf("base64.DecodeString(%q) error: %v", want64, err)
}
want = want[4:]
in := []byte(`{"system":{"set_relay_state":{"state":1}}}`)
var buf bytes.Buffer
newEncrypter(&buf).Write(in)
if got := buf.Bytes(); !bytes.Equal(got, want) {
t.Errorf("encrypt(%q) = %02x, want %02x", in, got, want)
}
}
// func TestEncryptDecrypt(t *testing.T) {
// want := make([]byte, 64)
// if _, err := rand.Read(want); err != nil {
// t.Fatalf("rand.Read(want) error: %v", err)
// }
// if got := decrypt(encrypt(want)); !bytes.Equal(got, want) {
// t.Errorf("decrypt(encrypt(%02x)) = %02x, want %02x", want, got, want)
// }
// }