diff options
Diffstat (limited to 'hs100.go')
-rw-r--r-- | hs100.go | 118 |
1 files changed, 118 insertions, 0 deletions
diff --git a/hs100.go b/hs100.go new file mode 100644 index 0000000..c972f8a --- /dev/null +++ b/hs100.go @@ -0,0 +1,118 @@ +// vim: shiftwidth=4 tabstop=4 noexpandtab + +package main + +import ( + "context" + "encoding/binary" + "fmt" + "net" + "time" +) + +const ( + MAX_PAYLOAD = 4294967295 // TP-Link WiFi plug protocol: max. 2^32-1 bytes +) + +// Hs100 bundles every data associated with one TP-Link HS100 smart plug. +type Hs100 struct { + Config Hs100Conf +} + +// Hs100Conf is the configuration of one TP-Link HS100 smart plug. +type Hs100Conf struct { + Ip net.IP + Name string +} + +// encrypt() encrypts data for a TP-Link WiFi plug. +func encrypt(data []byte) ([]byte, error) { + + // assert maximum payload size to cast data length safely + if len(data) > MAX_PAYLOAD { + return []byte{}, fmt.Errorf("Too many bytes to encrypt (%d > %d)!\n", + len(data), MAX_PAYLOAD) + } + length := uint32(len(data)) + + // encode payload length as header + out := make([]byte, 4) // header buffer + binary.BigEndian.PutUint32(out, length) + + // encryption algorithm + key := byte(171) + for _, value := range data { + key = key ^ value + out = append(out, byte(key)) + } + + return out, nil +} + +// decrypt() decrypts data coming from a TP-Link WiFi plug. +func decrypt(data []byte) []byte { + + // TODO check if length given in header is correct + + // cut-off header + data = data[4:] + + // decryption algorithm + key := byte(171) + for index, value := range data { + data[index] = key ^ value + key = value + } + + return data +} + +// send() sends data via TCP to an address (like "192.168.1.42:9999"). +func send(address string, data []byte) error { + + // create a Dialer with context + var d net.Dialer + ctx, cancel := context.WithTimeout(context.Background(), 3 * time.Second) + defer cancel() + + // establish connection + conn, err := d.DialContext(ctx, "tcp", address) + if err != nil { + return fmt.Errorf("Failed to dial: %v", err) + } + defer conn.Close() + + // writing data + _, err = conn.Write(data) + if err != nil { + return fmt.Errorf("Could not write data: %v", err) + } + + return nil + +} + +// set() sets the relay state of a TP-Link WiFi plug. +func set(host string, state string) error { + + cmd := "" + + // modify command according to state + if state == "on" { + cmd = `{"system":{"set_relay_state":{"state":1}}}` + } else if state == "off" { + cmd = `{"system":{"set_relay_state":{"state":0}}}` + } else { + return fmt.Errorf("set() just accepts values 'on' and 'off'!") + } + + // format address, encrypt data and send it + address := fmt.Sprintf("%s:9999", host) + data, err := encrypt([]byte(cmd)) + if err != nil { + return err + } + err = send(address, data) + + return err +} |