tuyapi: Payload returns nulls

Describe the bug Null payload informations returned

To Reproduce

  1. Run async example node asyn.js (https://github.com/codetheweb/tuyapi)
const TuyAPI = require('tuyapi');

const device = new TuyAPI({
  id: 'xxxxxxxxxxxxxxxxxxxx',
  key: 'xxxxxxxxxxxxxxxx'});

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);
  1. Check data from device

Expected behavior Not null values returned

Debug Output Post the output of your program/app/script when run with the DEBUG environment variable set to *. Example: DEBUG=* node test.js. Copy the output and paste it below (in between the code fences):

node async.js
Connected to device!
Error! json obj data unvalid
Data from device: { dps:
   { '1': null,
     '2': null,
     '3': null,
     '101': null,
     '102': null,
     '103': null } }
Boolean status of default property: null.
Data from device: { dps: { '1': true }, t: 1593283963 }
Boolean status of default property: true.
Disconnected from device.

Desktop (please complete the following information):

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 30 (22 by maintainers)

Most upvoted comments

Thanks @codetheweb. Thinking about using SET instead of GET, I wonder if a better option would be to simply implement a fallback to using CONTROL_NEW instead. That seems to be the method that works for these devices. The CONTROL_NEW command seems to have a slightly different schema, basically no gwId: property, otherwise the same and change the command code. That seems to be what other implementations are doing such as:

https://redmine.telecom-bretagne.eu/projects/xaal/repository/entry/code/Python/branches/0.7/devices/protocols/Tuya/xaal/tuya/tuyaclient.py

Unless I’m reading that completely wrong (I’m quite worse at Python vs Javascript, which is perhaps a sad statement) it appears they just try DP_QUERY, if they detect the “json obj data unvalid” message they call fix_buggy_dp_query(), which uses CONTROL_NEW instead.

That’s basically the same as I’m doing in my code, but I’m falling back to a SET with null, since TuyAPI abstacts the commands. I’ll try to hack my local TuyAPI to use the CONTROL_NEW fallback and see if it works.

This behavior is now opt-in in v6.0.0.

OK, I think I’ve worked out how to deal with this for my code at least. After reading the mile long #246 thread, it seems that some devices don’t return an invalid response and, after that, the proper data, however, some devices appear to never return valid data to a DP_QUERY. I see some libraries using a CONTROL_NEW command as a fallback, although I’m still not sure I fully understand it, but I couldn’t work out any way to do that in TuyAPI without changing the code (I see no way to control the command type for get().

However, I did find that if I detect a null response to a get() that it’s still possible get the device to send an update by using the send() command. It seems that if you issue a “send()” with a null payload to the DPS key you want to get(), you can cajole the device to at least send a data update with the value. So far this seems to work for me and the devices I have access to with this behavior. I just trap the invalid response, log it as invalid, issue a send() and wait for the data update. I have no idea if this works for all devices, but as I noted above, it seems OK for the ones I have.

If I manage to get more time perhaps I’ll try to do some packet traces and then decode them and see if I can get more clarity on how the Tuya app itself deals with this, but I don’t know if that time will come soon.

I’m struggling with this as well. Newer devices seem to use a new command type (DP_QUERY_NEW perhaps?) and so I can’t seem to get on demand DPS updates from these devices using get(). I can set(), and I can subscribe to data updates and get data if changes are made to the device, but get() always returns invalid data which means I can’t get initial device state. I’m trying to research this in more detail in some other projects that implement tuya protocol which don’t seem to have this problem.