Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for v3.2 protocol #234

Closed
Cedopolice opened this issue Jul 28, 2019 · 65 comments · Fixed by #606
Closed

Add support for v3.2 protocol #234

Cedopolice opened this issue Jul 28, 2019 · 65 comments · Fixed by #606

Comments

@Cedopolice
Copy link

Cedopolice commented Jul 28, 2019

Bonjour,

J'obtiens cette erreur au demarrage de gladys:

TypeError: Cannot read property '1' of undefined 0|gladys | at _send.then.data (/home/pi/gladys/api/hooks/tuya/node_modules/tuyapi/index.js:123:41) 0|gladys | at <anonymous> 0|gladys | at process._tickDomainCallback (internal/process/next_tick.js:228:7)

et des que j'actionne le device:

0|gladys | (node:18902) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 3): TypeError: Cannot read property '1' of undefined 0|gladys | Tuya - Error, undefined device! 0|gladys | Sending 500 ("Server Error") response: 0|gladys | Error: find() timed out. Is the device powered on and the ID or IP correct? 0|gladys | at pTimeout (/home/pi/gladys/api/hooks/tuya/node_modules/tuyapi/index.js:593:19) 0|gladys | at Timeout.setTimeout [as _onTimeout] (/home/pi/gladys/api/hooks/tuya/node_modules/p-timeout/index.js:20:13) 0|gladys | at ontimeout (timers.js:475:11) 0|gladys | at tryOnTimeout (timers.js:310:5) 0|gladys | at Timer.listOnTimeout (timers.js:270:5) 0|gladys | Server Error: 0|gladys |

ligne 123 de tuyapi/index.js:
resolve(data.dps['1']);

J'utilise un smart switch:
SCW NF101

avec wireshark, je recupere:
ip":"192.168.1.xx","gwId":"bfx9268f215ecx78cdkpiv","active":2,"ablilty":0,"encrypt":true,"productKey":"zWxOK04ovOjw0Pxw","version":"3.2"}.B0

Mes identifinats sont de cette forme:
[ { id: 'bfx9268f215ecx78cdkpiv', key: 'b6a93451d8f07ga2' } ]

des idees?

@Apollon77
Copy link
Collaborator

Please show your code. Mostly this error comes from your code because you do not check errors.

@kueblc
Copy link
Collaborator

kueblc commented Jul 28, 2019

What I find interesting is "version":"3.2"

I haven't seen this protocol version, would you be able to capture the traffic when controlling the device locally with the app?

@Cedopolice
Copy link
Author

Cedopolice commented Jul 28, 2019

What I find interesting is "version":"3.2"

I haven't seen this protocol version, would you be able to capture the traffic when controlling the device locally with the app?

i can see this after mac filter:

mac_filter

// Import packages
const dgram = require('dgram');
const net = require('net');
const { EventEmitter } = require('events');
const pTimeout = require('p-timeout');
const pRetry = require('p-retry');
const debug = require('debug')('TuyAPI');

// Helpers
const { isValidString } = require('./lib/utils');
const { MessageParser, CommandType } = require('./lib/message-parser');

/**

  • Represents a Tuya device.
  • You must pass either an IP or an ID. If
  • you're experiencing problems when only passing
  • one, try passing both if possible.
  • @Class
  • @param {Object} options
  • @param {String} [options.ip] IP of device
  • @param {Number} [options.port=6668] port of device
  • @param {String} [options.id] ID of device (also called devId)
  • @param {String} [options.gwID=''] gateway ID (not needed for most devices),
  • if omitted assumed to be the same as options.id
  • @param {String} options.key encryption key of device (also called localKey)
  • @param {String} [options.productKey] product key of device (currently unused)
  • @param {Number} [options.version=3.3] protocol version
  • @example
  • const tuya = new TuyaDevice({id: 'xxxxxxxxxxxxxxxxxxxx',
  •                          key: 'xxxxxxxxxxxxxxxx'})
    

*/
class TuyaDevice extends EventEmitter {
constructor({ ip, port = 6668, id, gwID, key, productKey, version = 3.1 } = {}) {
super();
// Set device to user-passed options
this.device = { ip, port, id, gwID, key, productKey, version };

    // Check arguments
    if (!(isValidString(id) ||
            isValidString(ip))) {
        throw new TypeError('ID and IP are missing from device.');
    }

    // Check key
    if (!isValidString(this.device.key) || this.device.key.length !== 16) {
        throw new TypeError('Key is missing or incorrect.');
    }

    // Handles encoding/decoding, encrypting/decrypting messages
    this.device.parser = new MessageParser({
        key: this.device.key,
        version: this.device.version
    });

    // Contains array of found devices when calling .find()
    this.foundDevices = [];

    // Private instance variables

    // Socket connected state
    this._connected = false;

    this._responseTimeout = 5; // Seconds
    this._connectTimeout = 5; // Seconds
    this._pingPongPeriod = 10; // Seconds

    this._currentSequenceN = 0;
    this._resolvers = {};

    this._waitingForSetToResolve = false;
}

/**
 * Gets a device's current status.
 * Defaults to returning only the value of the first DPS index.
 * @param {Object} [options]
 * @param {Boolean} [options.schema]
 * true to return entire list of properties from device
 * @param {Number} [options.dps=1]
 * DPS index to return
 * @example
 * // get first, default property from device
 * tuya.get().then(status => console.log(status))
 * @example
 * // get second property from device
 * tuya.get({dps: 2}).then(status => console.log(status))
 * @example
 * // get all available data from device
 * tuya.get({schema: true}).then(data => console.log(data))
 * @returns {Promise<Boolean|Object>}
 * returns boolean if single property is requested, otherwise returns object of results
 */
get(options = {}) {
    const payload = {
        gwId: this.device.gwID,
        devId: this.device.id

    };

    debug('GET Payload:');
    debug(payload);

    // Create byte buffer
    const buffer = this.device.parser.encode({
        data: payload,
        commandByte: CommandType.DP_QUERY,
        sequenceN: ++this._currentSequenceN
    });

    // Send request and parse response
    return new Promise((resolve, reject) => {
        try {
            // Send request
            this._send(buffer).then(data => {
                if (options.schema === true) {
                    // Return whole response
                    resolve(data);
                } else if (options.dps) {
                    // Return specific property
                    resolve(data.dps[options.dps]);
                } else {
                    // Return first property by default
                    resolve(data.dps['1']);
                }
            });
        } catch (error) {
            reject(error);
        }
    });
}

@kueblc
Copy link
Collaborator

kueblc commented Jul 28, 2019

This doesn't tell me anything, I would need the activity while the device is being controlled by the app, not by tuyapi. Should be TCP over port 6668. In order to get this you'd need to capture traffic at the AP. You can set your computer up as the AP with hostapd or using a convenience script like create_ap. Then connect both the phone and device to this network and record with tcpdump or Wireshark while controlling the device in offline mode.

@Cedopolice
Copy link
Author

And exec.js

const TuyaDevice = require('tuyapi');
const Promise = require('bluebird');
const find = require('lodash.find');
const config = require('../config');

module.exports = function exec(params) {
const arrayIdentifier = params.deviceType.identifier.split('_');
const id = arrayIdentifier[0];
const dps = arrayIdentifier[1];
const type = params.deviceType.type;
const action = params.deviceType.deviceTypeIdentifier;
const value = params.state.value;
const deviceConfig = find(config.devices, ['id', id]);
// sails.log.debug(DEBUG - Device config: ${JSON.stringify(deviceConfig)});

if (!deviceConfig) {
    sails.log.error(`Tuya - Error, device unknown in config file!`);
    return Promise.reject();
}

try {
    const tuyaDevice = new TuyaDevice({ ip: '192.168.1.xx', id: deviceConfig.id, key: deviceConfig.key });
    // sails.log.debug(`DEBUG - Tuya device: ${JSON.stringify(tuyaDevice)}`);

    return tuyaDevice.find()
        .then(() => tuyaDevice.connect())
        .then(() => {
            // sails.log.debug(`DEBUG - Tuya device connected: ${tuyaDevice.isConnected()}`);
            switch (type) {
                case 'binary':
                    switch (action) {
                        case 'power':
                            sails.log.info(`Tuya - Power device: ${value}`);
                            const state = value === 1 ? 'true' : 'false';
                            return setState(tuyaDevice, dps, state)
                                .then(() => Promise.resolve(value));

                        case 'open':
                            if (value === 1) {
                                sails.log.info(`Tuya - Open device`);
                                return setState(tuyaDevice, dps, '1')
                                    .then(() => Promise.resolve(value));
                            } else {
                                sails.log.info(`Tuya - Stop device`);
                                return setState(tuyaDevice, dps, '3')
                                    .then(() => Promise.resolve(value));
                            }

                        case 'close':
                            if (value === 1) {
                                sails.log.info(`Tuya - Close device`);
                                return setState(tuyaDevice, dps, '2')
                                    .then(() => Promise.resolve(value));
                            } else {
                                sails.log.info(`Tuya - Stop device`);
                                return setState(tuyaDevice, dps, '3')
                                    .then(() => Promise.resolve(value));
                            }

                        default:
                            return Promise.reject();
                    }

                    // NOT YET USED BELOW
                case 'brightness': // (0-255)
                case 'hue': // (0-360)
                case 'saturation': // (0-255)
                default:
                    sails.log.error(`Tuya - Error, action not allowed!`);
                    return Promise.reject();
            }
        })
        .then(() => tuyaDevice.disconnect())
        .catch((err) => {
            sails.log.error(`Tuya - Error, undefined device!`);
            tuyaDevice.disconnect();
            return Promise.reject(err);
        });
} catch (err) {
    sails.log.error(`Tuya - Unknown error!`);
    return Promise.reject(err);
}

};

function setState(device, dps, state) {
return device.set({ dps, set: state })
.then((result) => {
// sails.log.debug(DEBUG - Change state result: ${result}!);
return Promise.resolve();
})
.catch((err) => Promise.reject(err));
}

@kueblc
Copy link
Collaborator

kueblc commented Jul 28, 2019

I see one of your problems here, you are calling .then() multiple times on the same Promise.

What you have waits for find() and then executes the rest of the code immediately, without waiting in between. So you are trying to command the device and disconnect before it has completed connecting.

You need to have them called sequentially, as in, ... find().then( () => tuyaDevice.connect().then( ...

@kueblc
Copy link
Collaborator

kueblc commented Jul 28, 2019

Have you had any success controlling this device with the example script in the README?

@Cedopolice
Copy link
Author

with the script in the readme:

2|test | Connected to device! 2|test | Data from device: json obj data unvalid 2|test | TypeError: Cannot read property '1' of undefined 2|test | at TuyaDevice.device.on.data (/home/pi/gladys/api/hooks/tuya/lib/test.js:32:64) 2|test | at emitThree (events.js:136:13) 2|test | at TuyaDevice.emit (events.js:217:7) 2|test | at TuyaDevice._packetHandler (/home/pi/gladys/api/hooks/tuya/node_modules/tuyapi/index.js:416:14) 2|test | at packets.forEach.packet (/home/pi/gladys/api/hooks/tuya/node_modules/tuyapi/index.js:317:55) 2|test | at Array.forEach (<anonymous>) 2|test | at Socket.client.on.data (/home/pi/gladys/api/hooks/tuya/node_modules/tuyapi/index.js:313:29) 2|test | at emitOne (events.js:116:13) 2|test | at Socket.emit (events.js:211:7) 2|test | at addChunk (_stream_readable.js:263:12)
And i will try to capture the traffic,

thanks

@Cedopolice
Copy link
Author

I can capture the traffic with wireshark, what do you need?

@kueblc
Copy link
Collaborator

kueblc commented Jul 28, 2019

Control the device with your phone app while it is in local/offline mode, you should see activity over TCP port 6668.

@Cedopolice
Copy link
Author

i can see activity but not on port 6668

@Cedopolice
Copy link
Author

mac_filter

@kueblc
Copy link
Collaborator

kueblc commented Jul 28, 2019

It's connecting to the cloud, you need to do this offline

@Cedopolice
Copy link
Author

Cedopolice commented Jul 29, 2019

I will try offline with wire shark.

With adb:
07-29 14:38:24.888 32185 32520 I ReactNativeJS: Running application "TYRCTApp" with appParams: {"initialProps":{"devInfo":{"isShare":false,"activeTime":1564403895,"uiId":"00000002ic","dps":{"1":false,"101":"0","102":0},"roomId":0,"isVDevice":false,"isAdmin":true,"verSw":"1.0.2","gwId":"bfb9268f215ecc78cdkpiv","isLocalOnline":true,"isOnline":true,"capability":1,"nodeId":"","uiConfig":{},"networkType":"WIFI","uuid":"0c54c85f17da0184","name":"Wi-Fi Smart Switch","icon":"https://images.tuyaeu.com/smart/icon/15335325596i4s1vqxrzo_0.jpg","devId":"bfb9268f215ecc78cdkpiv","attribute":3672960,"isUniversalPanel":false,"ability":0,"homeId":5212741,"appKey":"ekmnwp9f5pnh3trdtpgy","timezoneId":"Europe/Paris","parentId":null,"panelConfig":{"bic":"[{\"code\":\"timer\",\"selected\":true},{\"code\":\"jump_url\",\"selected\":false}]"},"appId":19,"pcc":"","ui":"00000002ic_0.0.8","bv":"11.02","t":1564403903,"uiPhase":"release","schema":{"1":{"type":"obj","name":"开 关","mode":"rw","code":"switch_1","id":"1","schemaType":"bool","iconname":"icon-power","property":"{\"type\":\"bool\"}"},"101":{"type":"obj","name":"继电器上电状态","mode":"rw","code":"relay_status","id":"101","schemaType":"enum","iconname":null,"property":"{\"range\":[\"0\",\"1\",\"2\"],\"type\":\"enum\"}"},"102":{"type":"obj","name":"倒计时","mode":"rw","code":"countdown","id":"102","schemaType":"value","iconname":null,"property":"{\"max\":86400,\"min\":0,\"scale\":0,\"step\":1,\"type\":\"value\",\"unit\":\"s\"}"}},"productId":"zWqOK04ovOjw0Pcw"}},"rootTag":1}. __DEV__ === false, development-level warning are OFF, performance optimizations are ON 07-29 14:38:24.906 32185 32520 I ReactNativeJS: navigator-layout Store Dispatch......

07-29 14:38:24.888 32185 32520 I ReactNativeJS: Running application "TYRCTApp" with appParams: {"initialProps":{"devInfo":{"isShare":false,"activeTime":1564403895,"uiId":"00000002ic","dps":{"1":false,"101":"0","102":0},"roomId":0,"isVDevice":false,"isAdmin":true,"verSw":"1.0.2","gwId":"bfb9268f215ecc78cdkpiv","isLocalOnline":true,"isOnline":true,"capability":1,"nodeId":"","uiConfig":{},"networkType":"WIFI","uuid":"0c54c85f17da0184","name":"Wi-Fi Smart Switch","icon":"https://images.tuyaeu.com/smart/icon/15335325596i4s1vqxrzo_0.jpg","devId":"bfb9268f215ecc78cdkpiv","attribute":3672960,"isUniversalPanel":false,"ability":0,"homeId":5212741,"appKey":"ekmnwp9f5pnh3trdtpgy","timezoneId":"Europe/Paris","parentId":null,"panelConfig":{"bic":"[{\"code\":\"timer\",\"selected\":true},{\"code\":\"jump_url\",\"selected\":false}]"},"appId":19,"pcc":"","ui":"00000002ic_0.0.8","bv":"11.02","t":1564403903,"uiPhase":"release","schema":{"1":{"type":"obj","name":"开 关","mode":"rw","code":"switch_1","id":"1","schemaType":"bool","iconname":"icon-power","property":"{\"type\":\"bool\"}"},"101":{"type":"obj","name":"继电器上电状态","mode":"rw","code":"relay_status","id":"101","schemaType":"enum","iconname":null,"property":"{\"range\":[\"0\",\"1\",\"2\"],\"type\":\"enum\"}"},"102":{"type":"obj","name":"倒计时","mode":"rw","code":"countdown","id":"102","schemaType":"value","iconname":null,"property":"{\"max\":86400,\"min\":0,\"scale\":0,\"step\":1,\"type\":\"value\",\"unit\":\"s\"}"}},"productId":"zWqOK04ovOjw0Pcw"}},"rootTag":1}. __DEV__ === false, development-level warning are OFF, performance optimizations are ON 07-29 14:38:24.906 32185 32520 I ReactNativeJS: navigator-layout Store Dispatch.....

07-29 14:38:37.679 32185 32572 I ReactNativeJS: '-----数据下发', { switch_1: true }, { '1': true } 07-29 14:38:37.732 32185 32572 I ReactNativeJS: '-----数据上报', { switch_1: true }, { '1': true }

I had tried:

`const TuyAPI = require('tuyapi');

const device = new TuyAPI({

id: 'bfb9268xxxxxxxxxxxxxx',
key: 'b6a95xxxxxxxxxxx'

});

let stateHasChanged = false;

// Find device on network
device.find().then(() => {
// Connect to device
device.connect();
});

// Add event listeners
device.on('connected', () => {
console.log('Connected to device!');
});

device.on('disconnected', () => {
console.log('Disconnected from device.');
});

device.on('error', error => {
console.log('Error!', error);
});

device.on('data', data => {
console.log('Data from device:', data);

console.log(`Boolean status of default property: ${data.dps['1']}.`);

// Set default property to opposite
if (!stateHasChanged) {
    device.set({ set: !(data.dps['1']) });

    // Otherwise we'll be stuck in an endless
    // loop of toggling the state.
    stateHasChanged = true;
}

});

// Disconnect after 10 seconds
setTimeout(() => { device.disconnect(); }, 10000);`

and error:

3|test | Connected to device! 3|test | Data from device: json obj data unvalid 3|test | TypeError: Cannot read property '1' of undefined 3|test | at TuyaDevice.device.on.data (/home/pi/gladys/api/hooks/tuya/node_modules/tuyapi/test.js:33:64) 3|test | at emitThree (events.js:136:13) 3|test | at TuyaDevice.emit (events.js:217:7) 3|test | at TuyaDevice._packetHandler (/home/pi/gladys/api/hooks/tuya/node_modules/tuyapi/index.js:415:14) 3|test | at packets.forEach.packet (/home/pi/gladys/api/hooks/tuya/node_modules/tuyapi/index.js:316:55) 3|test | at Array.forEach (<anonymous>) 3|test | at Socket.client.on.data (/home/pi/gladys/api/hooks/tuya/node_modules/tuyapi/index.js:312:29) 3|test | at emitOne (events.js:116:13) 3|test | at Socket.emit (events.js:211:7) 3|test | at addChunk (_stream_readable.js:263:12)

With anyproy:
[ { id: 'bfb9268xxxxxxxxxxxxxx', key: 'b6a95xxxxxxxxxxx' } ]

@Cedopolice
Copy link
Author

Cedopolice commented Jul 29, 2019

And with device and app connected on computer hotspot, with wireshark, offline:

@kueblc
Copy link
Collaborator

kueblc commented Jul 29, 2019

Unfortunately, I do not see any activity between your phone and the device in this capture. All I see here are repeated failed attempts to connect to the cloud. Did you successfully control the device with the app while you were recording?

@Cedopolice
Copy link
Author

Cedopolice commented Jul 29, 2019

yes, i had successfully control the device, is it possible?
i had used tcp filter only.
I will try with tcpdump and hostapd.

@ls819011
Copy link

Colin, I also can't read properties and control recently purchased Tuya switch and outlet using TuyAPI 5.1.1. I see that TuyAPI sends commands with non-encrypted payload and receives a responce with zero length payload.
2019-07-29 21_55_29-20190729-01-tuya pcapng

@kueblc
Copy link
Collaborator

kueblc commented Jul 29, 2019

@ls819011 If you can control the device locally with the app, not with tuyapi, please try capturing the traffic so we can better see what's going on. Have you used tuyapi successfully with other devices? Also worth posting your code so we can rule out use case mistakes.

@ls819011
Copy link

@kueblc Both my Android phone and Tuya switch are in the same wi-fi network but trying to control the switch using Android Smart Life app I see that phone and switch are communicating through the cloud (see "capture-Bridge0-Jul 29 22-27-49.pcapng" in attached archive).

No, I didn't use tuyapi earlier. I just purchased my 1st switch and couple of outlets which are also Tuya, at least they all are controlled by Tuya Smart Life app. I'm just in the begining of building my smart home.
Please also find code which I used trying to communicate to Tuya switch and packet capture I got with this code in the same archive.
Couple of characters in both id and key are swapped.
captures.zip

@kueblc
Copy link
Collaborator

kueblc commented Jul 29, 2019

You may be able to force the app to control the device locally if there is no internet connection from the router to the outside world.

Code looks OK, though I would advise against using a CPU blocking sleep function like that, instead use a JavaScript style timeout. One thing I did notice from the capture is that the device is likely using protocol 3.3, so you might want to try specifying version: 3.3 in the TuyAPI constructor, or remove ip as this will force find() to determine the version.

@Cedopolice
Copy link
Author

Cedopolice commented Jul 29, 2019

With hostapd and tcpdump, ofline (what option syntax do you need for the command tcpdump?)

is it good?

@kueblc
Copy link
Collaborator

kueblc commented Jul 29, 2019

Yes @Cedopolice you've got it right, now. It does look like the communications are encrypted so I'll need your localKey in order to decrypt and parse the format. This is not sensitive or personally identifiable information but you are welcome to email it to me rather than post it publicly. You can write to me at my gmail address of the same name.

@Cedopolice
Copy link
Author

Cedopolice commented Jul 29, 2019

Ok, but, how can i find the localkey? is it the key obtained with anyproxy?

@kueblc
Copy link
Collaborator

kueblc commented Jul 29, 2019

Yes it is the same key you need to use to be able to control the device, you should already have used it with your exec.js

@ls819011
Copy link

@kueblc Thanks for your help. I can control switch after adding version to constructor parameters.
Excluding IP-address from constructor parameters causes the folowing error even if version is specified:

Error! Error: Error from socket
    at Socket.client.on.err (/home/sergey/.node-red/node_modules/tuyapi/index.js:323:30)
    at Socket.emit (events.js:198:13)
    at emitErrorNT (internal/streams/destroy.js:91:8)
    at emitErrorAndCloseNT (internal/streams/destroy.js:59:3)
    at process._tickCallback (internal/process/next_tick.js:63:19)

but that's does not matter for me.
Delay has been added to code to understand which packet contains 'get' command. It will be removed in future.
One more time, thanks a lot for your help and recommendations.

@Cedopolice
Copy link
Author

I sent the key yesterday.
Sorry, my english is bad, but i'm curious about the way you decrypt the data. I would like to know to do it myself. Is it AES ?
If it's possible, can you explain rapidly how can i learn to do it, with some advice and i will search myself.

Thanks

@kueblc
Copy link
Collaborator

kueblc commented Jul 30, 2019

@ls819011 Very happy to hear your issue has been resolved.

@Cedopolice I did receive your key, thank you. I ran out of time yesterday but I will try to take a look today. Yes it is AES-ECB-128, which you could learn yourself by looking through the code. I do not have time to explain it, but at a high level I will be taking a look to see if and how the format differs from protocol 3.1 and 3.3 so we can implement 3.2.

@kueblc
Copy link
Collaborator

kueblc commented Jul 30, 2019

@Cedopolice could you please double check the key that you sent me? I can't seem to get a valid decrypt. If you'd like to try for yourself, this is the data I extracted from your capture:

23124d21aee7140a5847acb726eb4b2090d27d2595a41136cd1da3fe6831b20e867fac2dcecaa4665962ad6c0a5b542ef947ed047a846735625c7caec02ab023db1ad0d810717b8298f3c8021786ef044722dc2c3f169d62d841fc01d563fcd2d4716be72556a2365c7fa4414039efba

If you want to test it out in NodeJS, I use this little snippet for testing:

decrypt = (msg, key) => {
	var d = crypto.createDecipheriv('aes-128-ecb', key, '')
	return d.update(msg,'hex','utf8') + d.final('utf8')
}

See if you can find the key that produces plaintext from this data. It should be a JSON string.

@kueblc
Copy link
Collaborator

kueblc commented Jul 30, 2019

Another note, if you use the app to register the device to a new network, it will be issued a new localKey. I believe what you sent me was the old key.

@cscoppafox
Copy link

TinyTuya recently got 3.2 working, this is what their output looks like. Perhaps their recent changes can be studied:

3 Way Smart Dimmer Switch by Martin Jerry:

{
            "ip": "192.168.10.55",
            "gwId": "eb26f9da7bd1477823xxxx",
            "active": 2,
            "ablilty": 0,
            "encrypt": true,
            "productKey": "pswjx0wszilxxxxxx",
            "version": "3.2",
            "dps": {
                "dps": {
                    "1": false,
                    "2": 140
                },
                "t": 1662140729
            },
            "name": "Stair Lights",
            "key": "ffca23cbbdxxxxxx",
            "mac": "68:57:2d:xx:xx:xx",
            "id": "eb26f9da7bd14778xxxxxx",
            "ver": "3.2"
        }

@Apollon77
Copy link
Collaborator

do ypu have a link? main topic is that I did not saw that many 3.2 versios in the wild or users having issues :-)

@Scope666
Copy link

Scope666 commented Sep 2, 2022

do you have a link? main topic is that I did not saw that many 3.2 versios in the wild or users having issues :-)

I would start here: jasonacox/tinytuya#168

@Apollon77
Copy link
Collaborator

Apollon77 commented Nov 4, 2022

I could also take a shot here. If someone is willing to sende me such a device please contact me at iobroker@fischer-ka.de. That could simplify things a lot

@Scope666
Copy link

Scope666 commented Nov 4, 2022

I can't send it, but I'm willing to gather whatever info you need from it ... make requests, etc.

I have this device, which can't be flashed, and is permanently on 3.2. I pasted some output from it above.

https://www.amazon.com/gp/product/B07N6CJW42/

@Apollon77
Copy link
Collaborator

@Scope666 @cscoppafox nice ... you both have the same devcie ... but no longer possible to order :-(

@Scope666
Copy link

Scope666 commented Nov 4, 2022

Both are the same person, I mistakenly posted from my work account. I deleted and reposted under the correct one.

Like I said, I can grab all the info you need from it. Use APIs against it, etc.

Apollon77 added a commit to Apollon77/tuyapi that referenced this issue Nov 8, 2022
@Apollon77
Copy link
Collaborator

Hm ... ok I checked a bit some other libraries and also added theoretical 3.2 support to my 3.4 branch.

What I found is the following: 3.2 is like 3.3 when it comes to encrypted and such, BUT will most likely return "json unvalid" on "get()", so need to use the "refresh" method with the relevant datapoints set to null and such ... this should be handled already by the library already automatically, so "should be all fine" :-)

So if someone wants to try -> #606

@Scope666
Copy link

Scope666 commented Nov 8, 2022

Hi @Apollon77 ... I tried running the 1st listed example code with my device's ID and Key and got this:

root@500b5bac85ba:/home/root/test# node test
(node:5029) UnhandledPromiseRejectionWarning: Error: find() timed out. Is the device powered on and the ID or IP correct?
    at /home/root/test/node_modules/tuyapi/index.js:985:13
    at Timeout._onTimeout (/home/root/test/node_modules/p-timeout/index.js:25:13)
    at listOnTimeout (internal/timers.js:554:17)
    at processTimers (internal/timers.js:497:7)
(node:5029) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:5029) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

@Apollon77
Copy link
Collaborator

To make sure: The device is turned on and in the same network where also UDP packages are routed?

Plrase try to start again with DEBUG=TuyAPI* node test and post the output (because this could be really nothing about 3.2 but general)

@Scope666
Copy link

Scope666 commented Nov 8, 2022

This is from the 3rd listed example that lets you specify an IP:

root@500b5bac85ba:/home/root/test# node test2
Connected to device!
DATA from device:  data format error
Disconnected from device.

@Scope666
Copy link

Scope666 commented Nov 8, 2022

With debug on:

root@500b5bac85ba:/home/root/test# DEBUG=TuyAPI* node test2
  TuyAPI IP and ID are already both resolved. +0ms
  TuyAPI Connecting to 192.168.10.55... +1ms
  TuyAPI Socket connected. +1s
Connected to device!
  TuyAPI GET Payload: +0ms
  TuyAPI {
  TuyAPI   gwId: 'eb26f9da7bd1477823lcx6',
  TuyAPI   devId: 'eb26f9da7bd1477823lcx6',
  TuyAPI   t: '1667922785',
  TuyAPI   dpId: [ 4, 5, 6, 18, 19, 20 ],
  TuyAPI   uid: 'eb26f9da7bd1477823lcx6'
  TuyAPI } +0ms
  TuyAPI GET Payload: +3ms
  TuyAPI {
  TuyAPI   gwId: 'eb26f9da7bd1477823lcx6',
  TuyAPI   devId: 'eb26f9da7bd1477823lcx6',
  TuyAPI   t: '1667922785',
  TuyAPI   dps: {},
  TuyAPI   uid: 'eb26f9da7bd1477823lcx6'
  TuyAPI } +0ms
  TuyAPI Received data: 000055aa000000020000000a0000001d000000016461746120666f726d6174206572726f72351e44bc0000aa55 +214ms
  TuyAPI Parsed: +1ms
  TuyAPI {
  TuyAPI   payload: 'data format error',
  TuyAPI   leftover: false,
  TuyAPI   commandByte: 10,
  TuyAPI   sequenceN: 2
  TuyAPI } +0ms
  TuyAPI Received DATA packet +0ms
  TuyAPI data: 10 : data format error +0ms
DATA from device:  data format error
  TuyAPI Pinging 192.168.10.55 +10s
  TuyAPI Received data: 000055aa00000000000000090000000c00000000b051ab030000aa55 +47ms
  TuyAPI Parsed: +0ms
  TuyAPI { payload: false, leftover: false, commandByte: 9, sequenceN: 0 } +1ms
  TuyAPI Pong from 192.168.10.55 +0ms

@Apollon77
Copy link
Collaborator

Ok, this is what I mentioned but seems "just adifferent error message" then "json obj data unvalid" ... try again github (or try this change locally a0e2048). How it looks then?

@Scope666
Copy link

Scope666 commented Nov 8, 2022

Ok, here's the output after manually editing index.js with your change above:

root@500b5bac85ba:/home/root/test# DEBUG=TuyAPI* node test2
  TuyAPI IP and ID are already both resolved. +0ms
  TuyAPI Connecting to 192.168.10.55... +1ms
  TuyAPI Socket connected. +101ms
Connected to device!
  TuyAPI GET Payload: +0ms
  TuyAPI {
  TuyAPI   gwId: 'eb26f9da7bd1477823lcx6',
  TuyAPI   devId: 'eb26f9da7bd1477823lcx6',
  TuyAPI   t: '1667923330',
  TuyAPI   dpId: [ 4, 5, 6, 18, 19, 20 ],
  TuyAPI   uid: 'eb26f9da7bd1477823lcx6'
  TuyAPI } +1ms
  TuyAPI GET Payload: +2ms
  TuyAPI {
  TuyAPI   gwId: 'eb26f9da7bd1477823lcx6',
  TuyAPI   devId: 'eb26f9da7bd1477823lcx6',
  TuyAPI   t: '1667923330',
  TuyAPI   dps: {},
  TuyAPI   uid: 'eb26f9da7bd1477823lcx6'
  TuyAPI } +0ms
  TuyAPI Received data: 000055aa000000020000000a0000001d000000016461746120666f726d6174206572726f72351e44bc0000aa55 +67ms
  TuyAPI Parsed: +1ms
  TuyAPI {
  TuyAPI   payload: 'data format error',
  TuyAPI   leftover: false,
  TuyAPI   commandByte: 10,
  TuyAPI   sequenceN: 2
  TuyAPI } +0ms
  TuyAPI Received DATA packet +0ms
  TuyAPI data: 10 : data format error +0ms
DATA from device:  data format error
  TuyAPI SET Payload: +0ms
  TuyAPI {
  TuyAPI   devId: 'eb26f9da7bd1477823lcx6',
  TuyAPI   gwId: 'eb26f9da7bd1477823lcx6',
  TuyAPI   uid: '',
  TuyAPI   t: 1667923330,
  TuyAPI   dps: { '1': null }
  TuyAPI } +0ms
  TuyAPI Received data: 000055aa00000000000000080000004b00000000332e320000000000000c83000000012c32857af63535eedf8f947927439030cebd54290edaa108008ec1253eb14635ea0686fb16045bba446cbeaae81562993346ed610000aa55 +7ms
  TuyAPI Parsed: +0ms
  TuyAPI {
  TuyAPI   payload: { dps: { '1': true }, t: 1667923329 },
  TuyAPI   leftover: false,
  TuyAPI   commandByte: 8,
  TuyAPI   sequenceN: 0
  TuyAPI } +1ms
  TuyAPI Received DATA packet +0ms
  TuyAPI data: 8 : [object Object] +0ms
DATA from device:  { dps: { '1': true }, t: 1667923329 }
  TuyAPI Received data: 000055aa00000003000000070000000c00000000c5591c5f0000aa55 +107ms
  TuyAPI Parsed: +0ms
  TuyAPI { payload: false, leftover: false, commandByte: 7, sequenceN: 3 } +1ms
  TuyAPI Got SET ack. +0ms
  TuyAPI Disconnect +712ms
Disconnected from device.
  TuyAPI Socket closed: 192.168.10.55 +0ms

@Scope666
Copy link

Scope666 commented Nov 8, 2022

What I'm running:

root@500b5bac85ba:/home/root/test# cat test2
const TuyAPI = require('tuyapi');

const device = new TuyAPI({
    id: 'eb26f9da7bd1477823lcx6',
    key: 'ffca23cbbdxxxxxx',
    ip: '192.168.10.55',
    version: '3.2',
    issueRefreshOnConnect: true});

// Find device on network
device.find().then(() => {
    // Connect to device
    device.connect();
});

// Add event listeners
device.on('connected', () => {
    console.log('Connected to device!');
});

device.on('disconnected', () => {
    console.log('Disconnected from device.');
});

device.on('error', error => {
    console.log('Error!', error);
});

device.on('dp-refresh', data => {
    console.log('DP_REFRESH data from device: ', data);
});

device.on('data', data => {
    console.log('DATA from device: ', data);

});

// Disconnect after 10 seconds
setTimeout(() => { device.disconnect(); }, 1000);

@Apollon77
Copy link
Collaborator

In factthat looks good, or? He was able to query data? Can be optimized because in fact protocol 3.2 should ever return scuh an error so I can remove the first request ...

So have fun playing areound more if you like. Try to control something or such

@Scope666
Copy link

Scope666 commented Nov 8, 2022

So it does look like it's connecting and receiving data from the device right?

@Apollon77
Copy link
Collaborator

Yes exactly. I updated GitHub again to noteven try the "normal get way" when protocol is 3.2 and directly use the alternative way. So if you like play around more with this version ... :-)

In fact according to other sources there are other devices that report 3.1 or 3.3 but have a "22 char long deviceid" which should be the same but I'm not 100% sure on that, so we have two calls for these devices for now still.

@Scope666
Copy link

Scope666 commented Nov 8, 2022

Ok, here's the output after your latest changes:

root@500b5bac85ba:/home/root/test# DEBUG=TuyAPI* node test2
  TuyAPI IP and ID are already both resolved. +0ms
  TuyAPI Connecting to 192.168.10.55... +1ms
  TuyAPI Socket connected. +28ms
Connected to device!
  TuyAPI GET Payload: +0ms
  TuyAPI {
  TuyAPI   gwId: 'eb26f9da7bd1477823lcx6',
  TuyAPI   devId: 'eb26f9da7bd1477823lcx6',
  TuyAPI   t: '1667924717',
  TuyAPI   dpId: [ 4, 5, 6, 18, 19, 20 ],
  TuyAPI   uid: 'eb26f9da7bd1477823lcx6'
  TuyAPI } +0ms
  TuyAPI GET Payload: +3ms
  TuyAPI {
  TuyAPI   gwId: 'eb26f9da7bd1477823lcx6',
  TuyAPI   devId: 'eb26f9da7bd1477823lcx6',
  TuyAPI   t: '1667924717',
  TuyAPI   dps: {},
  TuyAPI   uid: 'eb26f9da7bd1477823lcx6'
  TuyAPI } +0ms
  TuyAPI SET Payload: +0ms
  TuyAPI {
  TuyAPI   devId: 'eb26f9da7bd1477823lcx6',
  TuyAPI   gwId: 'eb26f9da7bd1477823lcx6',
  TuyAPI   uid: '',
  TuyAPI   t: 1667924716,
  TuyAPI   dps: { '1': null }
  TuyAPI } +0ms
  TuyAPI Received data: 000055aa00000000000000080000004b00000000332e320000000000000c85000000018ae67919d3236db8a45d1d7739d04f55d5b578be8fde7798cb56404afa61227cec5278683eeb40339f8284e450d120ae33b2b7b90000aa55 +135ms
  TuyAPI Parsed: +0ms
  TuyAPI {
  TuyAPI   payload: { dps: { '1': false }, t: 1667924715 },
  TuyAPI   leftover: false,
  TuyAPI   commandByte: 8,
  TuyAPI   sequenceN: 0
  TuyAPI } +0ms
  TuyAPI Received DATA packet +1ms
  TuyAPI data: 8 : [object Object] +0ms
DATA from device:  { dps: { '1': false }, t: 1667924715 }
  TuyAPI Received data: 000055aa00000003000000070000000c00000000c5591c5f0000aa55 +3ms
  TuyAPI Parsed: +0ms
  TuyAPI { payload: false, leftover: false, commandByte: 7, sequenceN: 3 } +0ms
  TuyAPI Got SET ack. +0ms
  TuyAPI Disconnect +830ms
Disconnected from device.
  TuyAPI Socket closed: 192.168.10.55 +1ms

@Apollon77
Copy link
Collaborator

Yes, as expected, thank you. Sofor me this would be "kind of fixed" :-)

I will bring this together with the protocol 3.4 changes as a PR

@Scope666
Copy link

Scope666 commented Nov 8, 2022

Excellent! If you ever need me to test anything else with the device, please let me know. Ultimate goal is to be able to control the device with LocalTuya in Home Assistant, this is a great first step.

@Apollon77
Copy link
Collaborator

Apollon77 commented Nov 8, 2022

But is Homeassistant not Python? in any case tinytuya (python lib) already have 3.2 support ... (it ws kind of my reference)

@Scope666
Copy link

Scope666 commented Nov 8, 2022

It is, but LocalTuya mentions tuyapi quite a bit, I figured it might be using parts from it:

https://github.com/rospogrigio/localtuya

@Apollon77
Copy link
Collaborator

Hm ... ok, but what I saw is that they just had kind of a clone of tinytuya lib in their files ... and yes without 3.2 and 3.4 support ... but tuyapi is not supported ... So in fact they need to update the python stuff

codetheweb pushed a commit that referenced this issue Nov 13, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.