tuya

package
v0.0.0-...-1476cbe Latest Latest
Warning

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

Go to latest
Published: Jan 9, 2022 License: Apache-2.0, 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

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) MakeBaseMsg

func (d *Appliance) MakeBaseMsg() map[string]interface{}

create base messages

func (*Appliance) ProcessResponse

func (d *Appliance) ProcessResponse(code int, b []byte)

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) StatusMsg

func (d *Appliance) StatusMsg() []byte

Status message should be encrypted by the version

func (*Appliance) String

func (d *Appliance) String() string

type BaseDevice

type BaseDevice struct {
	sync.Mutex

	App *Appliance
	// contains filtered or unexported fields
}

to be embedded in Device

func (*BaseDevice) Init

func (b *BaseDevice) Init(typ string, a *Appliance, c *ConfigurationData)

BaseDevice initialization to be invoked during configation

func (*BaseDevice) Name

func (b *BaseDevice) Name() string

func (*BaseDevice) Notify

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

func (*BaseDevice) Subscribe

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

func (*BaseDevice) Type

func (b *BaseDevice) Type() string

Implementation of Device interface provided by BaseDevice

func (*BaseDevice) Unsubscribe

func (b *BaseDevice) Unsubscribe(key int64)

type ConfigurationData

type ConfigurationData struct {
	Name    string
	GwId    string
	Type    string
	Key     string
	Ip      string //optional
	Version string //optional, default to 3.1, may be updated by UDP listener
}

configuration data

type Device

type Device interface {
	Type() string
	Name() string
	Subscribe(SyncChannel) int64
	Unsubscribe(int64)
	Configure(*Appliance, *ConfigurationData)
	ProcessResponse(int, []byte)
	Init(string, *Appliance, *ConfigurationData)
}

type DeviceManager

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

func NewDeviceManager

func NewDeviceManager(jdata string) *DeviceManager

func NewDeviceManagerRaw

func NewDeviceManagerRaw() *DeviceManager

func (*DeviceManager) ApplianceCount

func (dm *DeviceManager) ApplianceCount() int

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

func (*DeviceManager) ApplianceKeys

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

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

func (*DeviceManager) DefineDevice

func (dm *DeviceManager) DefineDevice(Name, GwId, Key, Ip, Version string, device Device)

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

func (*DeviceManager) DeviceKeys

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

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

func (*DeviceManager) DisableLogging

func (dm *DeviceManager) DisableLogging()

func (*DeviceManager) GetAppliance

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

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

func (*DeviceManager) GetDevice

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

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

type SyncChannel

type SyncChannel chan SyncMsg

func MakeSyncChannel

func MakeSyncChannel() SyncChannel

type SyncMsg

type SyncMsg struct {
	Code int
	Dev  Device
}

Jump to

Keyboard shortcuts

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