tuya

package module
v0.0.0-...-966ee74 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 15, 2020 License: Apache-2.0 Imports: 15 Imported by: 0

README

tuya

Golang API for tuya devices (or the like)

Experimental code subject to major changes

Limits

Support limited to protocol version 3.1 and 3.3

Tested only with Neo power plugs

Standalone application

Primary as an example and for my one use I have released a small web application that uses this API.

The application can be installed on a Raspberry PI (for example).

You will find it there : https://github.com/py60800/tinytuya

Acknowledgements

@codetheweb for reverse engineering the protocol

Prerequisites

Collect the keys and the id of tuya devices according to @Codetheweb method

Reliability

The Neo devices I use (Tuya clone) behave as expected most of the time but I have experienced some random crash during development.

Usage

Get the API go get "github.com/py60800/tuya"

Import 'github.com/py60800/tuya`

Create json configuration, thanks to keys and id collected previously (Use backquotes for multiline conf data or get it from a file)

conf := `[{"gwId":"1582850884f3eb30128e", "key":"XXXXXXX", "type":"Switch", "name":"s1"}]`
//multiline data
conf := `[
 {"gwId":"1582850884f3eb30128e", 
  "key":"XXXXXXXXXXX",
  "type":"Switch",
  "name":"sw1" },
 {"gwId":"86273325cc50e3c8fe2d",
  "key":"XXXXXXXXXXX",
  "type":"Switch",
  "name":"sw2" }
  ]`

Create a device manager: dm := tuya.NewDeviceManager(conf)

Get configured devices by their name b1,ok := dm.GetDevice("sw1")

Check type and cast to get active interface sw1 := b1.(tuya.Switch)

Play with the device :

sw1.Set(true) // doesn't wait for the result of the command

sw1.SetW(5*time.Second) // ensure the command is properly done

st,_ := sw1.Status()

fmt.Println("sw1 status:", st)

Design considerations

IP addresses are collected automatically from UDP messages broadcast on port 6666

API is supposed to be thread safe (I hope). A device can be used by concurrent go coroutines however communication with each device are serialized (no more than one TCP connection)

Communication with Tuya device is asynchronous. This means that tuya device can notify a change if someone plays with the hardware switch. Naive implement may encounters issues while a expecting one request for each response.

Extension

Looking at switch.go source code, it should be easy to create new devices using the same protocol.

Just define appropriate interface, code what is specific in a dedicated file and update the factory to make it usable.

Notes

I have found many oddities in tuya protocol:

  • Actual 64 bits encryption instead of 128 bits encryption (incorrect string to byte conversion)

  • ECB encryption is weak

  • Half of MD5 signing is used

  • Useless prefixes and suffixes

  • Worthless base64 encoding

  • Protocol not properly layered (command outside payload)

By many aspects, it seems that the protocol was designed for serial line communication.

Need help ?

Open an issue!

Documentation

Overview

To be updated if new logical devices are created

Index

Constants

View Source
const (
	CodeMsgSet        = 7
	CodeMsgStatus     = 10
	CodeMsgPing       = 9
	CodeMsgAutoStatus = 8
)

Code for tuya messages

View Source
const (
	SwitchOff          = 0
	SwitchOn           = 1
	SwitchUndetermined = 2
)

Variables

This section is empty.

Functions

This section is empty.

Types

type Appliance

type Appliance struct {
	Version string
	// contains filtered or unexported fields
}

the appliance proxies the hardware device

func (*Appliance) GetDevice

func (d *Appliance) GetDevice() Device

func (*Appliance) SendCommand

func (d *Appliance) SendCommand(cmd int, jdata interface{}) error

Send message unencrypted

func (*Appliance) SendEncryptedCommand

func (d *Appliance) SendEncryptedCommand(cmd int, jdata interface{}) error

-------------------------------

func (*Appliance) String

func (d *Appliance) String() string

type Device

type Device interface {
	Type() string
	Name() string
	Subscribe(SyncChannel) int64
	Unsubscribe(int64)
	// contains filtered or unexported methods
}

type DeviceManager

type DeviceManager struct {
	sync.Mutex
	// contains filtered or unexported fields
}

func NewDeviceManager

func NewDeviceManager(jdata string) *DeviceManager

func (*DeviceManager) ApplianceCount

func (dm *DeviceManager) ApplianceCount() int

-------------------------------------------

func (*DeviceManager) ApplianceKeys

func (dm *DeviceManager) ApplianceKeys() []string

-------------------------------------------

func (*DeviceManager) DeviceKeys

func (dm *DeviceManager) DeviceKeys() []string

-------------------------------------------

func (*DeviceManager) GetAppliance

func (dm *DeviceManager) GetAppliance(key string) (*Appliance, bool)

-------------------------------------------

func (*DeviceManager) GetDevice

func (dm *DeviceManager) GetDevice(key string) (Device, bool)

-------------------------------------------

type ISwitch

type ISwitch struct {
	// contains filtered or unexported fields
}

func (*ISwitch) Name

func (b *ISwitch) Name() string

func (*ISwitch) Notify

func (b *ISwitch) Notify(code int, d Device)

func (*ISwitch) Set

func (s *ISwitch) Set(on bool) error

func (*ISwitch) SetN

func (s *ISwitch) SetN(on bool, dps int) error

func (*ISwitch) SetNW

func (s *ISwitch) SetNW(on bool, dps int, delay time.Duration) (bool, error)

func (*ISwitch) SetW

func (s *ISwitch) SetW(on bool, delay time.Duration) (bool, error)

func (*ISwitch) Status

func (s *ISwitch) Status() (bool, error)

func (*ISwitch) StatusW

func (s *ISwitch) StatusW(delay time.Duration) (bool, error)

func (*ISwitch) Subscribe

func (b *ISwitch) Subscribe(c SyncChannel) int64

func (*ISwitch) Type

func (b *ISwitch) Type() string

Implementation of Device interface provided by baseDevice

func (*ISwitch) Unsubscribe

func (b *ISwitch) Unsubscribe(key int64)

type Switch

type Switch interface {
	Set(bool) error
	SetN(bool, int) error
	SetW(bool, time.Duration) (bool, error)
	SetNW(bool, int, time.Duration) (bool, error)
	Status() (bool, error)
	StatusW(time.Duration) (bool, error)
}

type SyncChannel

type SyncChannel chan SyncMsg

func MakeSyncChannel

func MakeSyncChannel() SyncChannel

type SyncMsg

type SyncMsg struct {
	Code int
	Dev  Device
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL