Closed Nurffe closed 4 years ago
It was decided here (see linked issue thread) that for some applications returning a null payload for that specific error is acceptable. The behavior of your device looks similar to what's described in that thread.
I have a device that returns these null values (an LSC ceiling light), but with the code like above I never get the second 'Data from device' line with actual correct information, I also cannot control the device. Is there any way to debug this? The tuya application can control the device fine.
I printed the device after connection and got this.
TuyaDevice {
_events: [Object: null prototype] {
connected: [Function],
disconnected: [Function],
error: [Function],
data: [AsyncFunction]
},
_eventsCount: 4,
_maxListeners: undefined,
device: {
ip: 'SNIP',
port: 6668,
id: 'SNIP',
gwID: 'SNIP',
key: 'SNIP',
productKey: 'SNIP',
version: '3.3',
parser: MessageParser {
version: '3.3',
cipher: [TuyaCipher],
key: 'SNIP'
}
},
foundDevices: [ { id: 'SNIP', ip: 'SNIP' } ],
_connected: false,
_responseTimeout: 5,
_connectTimeout: 5,
_pingPongPeriod: 10,
_currentSequenceN: 0,
_resolvers: {},
_waitingForSetToResolve: false,
client: Socket {
connecting: true,
_hadError: false,
_parent: null,
_host: null,
_readableState: ReadableState {
objectMode: false,
highWaterMark: 16384,
buffer: BufferList { head: null, tail: null, length: 0 },
length: 0,
pipes: null,
pipesCount: 0,
flowing: true,
ended: false,
endEmitted: false,
reading: false,
sync: true,
needReadable: false,
emittedReadable: false,
readableListening: false,
resumeScheduled: true,
emitClose: false,
autoDestroy: false,
destroyed: false,
defaultEncoding: 'utf8',
awaitDrainWriters: null,
multiAwaitDrain: false,
readingMore: false,
decoder: null,
encoding: null,
[Symbol(kPaused)]: false
},
readable: false,
_events: [Object: null prototype] {
end: [Function: onReadableStreamEnd],
timeout: [Function],
data: [Function],
error: [Function],
close: [Function],
connect: [AsyncFunction]
},
_eventsCount: 6,
_maxListeners: undefined,
_writableState: WritableState {
objectMode: false,
highWaterMark: 16384,
finalCalled: false,
needDrain: false,
ending: false,
ended: false,
finished: false,
destroyed: false,
decodeStrings: false,
defaultEncoding: 'utf8',
length: 0,
writing: false,
corked: 0,
sync: true,
bufferProcessing: false,
onwrite: [Function: bound onwrite],
writecb: null,
writelen: 0,
afterWriteTickInfo: null,
bufferedRequest: null,
lastBufferedRequest: null,
pendingcb: 0,
prefinished: false,
errorEmitted: false,
emitClose: false,
autoDestroy: false,
bufferedRequestCount: 0,
corkedRequestsFree: [Object]
},
writable: true,
allowHalfOpen: false,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: null,
_server: null,
timeout: 5000,
[Symbol(asyncId)]: 12,
[Symbol(kHandle)]: TCP {
reading: false,
onconnection: null,
[Symbol(owner)]: [Circular]
},
[Symbol(kSetNoDelay)]: false,
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: Timeout {
_idleTimeout: 5000,
_idlePrev: [TimersList],
_idleNext: [Timeout],
_idleStart: 4894,
_onTimeout: [Function: bound ],
_timerArgs: undefined,
_repeat: null,
_destroyed: false,
[Symbol(refed)]: false,
[Symbol(asyncId)]: 14,
[Symbol(triggerId)]: 0
},
[Symbol(kBuffer)]: null,
[Symbol(kBufferCb)]: null,
[Symbol(kBufferGen)]: null,
[Symbol(kCapture)]: false,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0
},
[Symbol(kCapture)]: false
}
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.
@beele to get additional debug output, run your script with the environment variable DEBUG set to (i.e. `DEBUG= node script.js`).
I have the same issue.My device is Zigbee bridge with sub switch. The debug log is below
TuyAPI Finding missing IP undefined or ID eb0f4c330d33b7f24bmugy +0ms TuyAPI Received UDP message. +551ms TuyAPI UDP data: +2ms TuyAPI { TuyAPI payload: { TuyAPI ip: '10.1.9.42', TuyAPI gwId: 'eb0f4c330d33b7f24bmugy', TuyAPI active: 2, TuyAPI ablilty: 0, TuyAPI encrypt: true, TuyAPI productKey: 'keyfa7hya4gfa7g9', TuyAPI version: '3.3' TuyAPI }, TuyAPI leftover: false, TuyAPI commandByte: 19, TuyAPI sequenceN: 0 TuyAPI } +0ms TuyAPI Connecting to 10.1.9.42... +3ms TuyAPI Socket connected. +7ms Connected to device! TuyAPI GET Payload: +4ms TuyAPI { TuyAPI gwId: 'eb0f4c330d33b7f24bmugy', TuyAPI devId: 'eb0f4c330d33b7f24bmugy', TuyAPI t: '1597896001', TuyAPI dps: {}, TuyAPI uid: 'eb0f4c330d33b7f24bmugy' TuyAPI } +0ms TuyAPI Received data: 000055aa000000010000000a0000002c0000000145b2ed8cecc9fb8e0dc3b1346ad0cc0200f8a330d1e17874c639501ff852b7e55262f6820000aa55 +14ms Error! json obj data unvalid TuyAPI Parsed: +2ms TuyAPI { TuyAPI payload: { TuyAPI dps: { TuyAPI '1': null, TuyAPI '2': null, TuyAPI '3': null, TuyAPI '101': null, TuyAPI '102': null, TuyAPI '103': null TuyAPI } TuyAPI }, TuyAPI leftover: false, TuyAPI commandByte: 10, TuyAPI sequenceN: 1 TuyAPI } +0ms Data from device: { dps: { '1': null, '2': null, '3': null, '101': null, '102': null, '103': null } } TuyAPI Disconnect +6s TuyAPI Socket closed: 10.1.9.42 +1ms
@tasict it looks like your code only attempts to get data once, what happens if you try a second time after the first fetch fails?
@codetheweb it looks the same.
TuyAPI Finding missing IP undefined or ID eb0f4c330d33b7f24bmugy +0ms TuyAPI Received UDP message. +683ms TuyAPI UDP data: +4ms TuyAPI { TuyAPI payload: { TuyAPI ip: '10.1.9.18', TuyAPI gwId: 'eb89a604dedb9d87e0rvig', TuyAPI active: 2, TuyAPI ablilty: 0, TuyAPI encrypt: true, TuyAPI productKey: 'nqvhejakb112obkn', TuyAPI version: '3.3' TuyAPI }, TuyAPI leftover: false, TuyAPI commandByte: 19, TuyAPI sequenceN: 0 TuyAPI } +1ms TuyAPI Received UDP message. +3s TuyAPI UDP data: +1ms TuyAPI { TuyAPI payload: { TuyAPI ip: '10.1.9.42', TuyAPI gwId: 'eb0f4c330d33b7f24bmugy', TuyAPI active: 2, TuyAPI ablilty: 0, TuyAPI encrypt: true, TuyAPI productKey: 'keyfa7hya4gfa7g9', TuyAPI version: '3.3' TuyAPI }, TuyAPI leftover: false, TuyAPI commandByte: 19, TuyAPI sequenceN: 0 TuyAPI } +0ms TuyAPI Connecting to 10.1.9.42... +3ms TuyAPI Socket connected. +12ms Connected to device! TuyAPI GET Payload: +2ms TuyAPI { TuyAPI gwId: 'eb0f4c330d33b7f24bmugy', TuyAPI devId: 'eb0f4c330d33b7f24bmugy', TuyAPI t: '1598254769', TuyAPI dps: {}, TuyAPI uid: 'eb0f4c330d33b7f24bmugy' TuyAPI } +1ms TuyAPI GET Payload: +4ms TuyAPI { TuyAPI gwId: 'eb0f4c330d33b7f24bmugy', TuyAPI devId: 'eb0f4c330d33b7f24bmugy', TuyAPI t: '1598254769', TuyAPI dps: {}, TuyAPI uid: 'eb0f4c330d33b7f24bmugy' TuyAPI } +1ms TuyAPI Received data: 000055aa000000010000000a0000002c0000000145b2ed8cecc9fb8e0dc3b1346ad0cc0200f8a330d1e17874c639501ff852b7e55262f6820000aa55 +9ms Error! json obj data unvalid TuyAPI Parsed: +1ms TuyAPI { TuyAPI payload: { TuyAPI dps: { TuyAPI '1': null, TuyAPI '2': null, TuyAPI '3': null, TuyAPI '101': null, TuyAPI '102': null, TuyAPI '103': null TuyAPI } TuyAPI }, TuyAPI leftover: false, TuyAPI commandByte: 10, TuyAPI sequenceN: 1 TuyAPI } +0ms Data from device: { dps: { '1': null, '2': null, '3': null, '101': null, '102': null, '103': null } } TuyAPI Received data: 000055aa000000020000000a0000002c0000000145b2ed8cecc9fb8e0dc3b1346ad0cc0200f8a330d1e17874c639501ff852b7e56da988170000aa55 +7ms Error! json obj data unvalid TuyAPI Parsed: +1ms TuyAPI { TuyAPI payload: { TuyAPI dps: { TuyAPI '1': null, TuyAPI '2': null, TuyAPI '3': null, TuyAPI '101': null, TuyAPI '102': null, TuyAPI '103': null TuyAPI } TuyAPI }, TuyAPI leftover: false, TuyAPI commandByte: 10, TuyAPI sequenceN: 2 TuyAPI } +0ms Data from device: { dps: { '1': null, '2': null, '3': null, '101': null, '102': null, '103': null } } TuyAPI Disconnect +6s TuyAPI Socket closed: 10.1.9.42 +1ms
It's probably an issue with TuyAPI then @tasict.
Could you play around with the payload it's sending on lines 94 - 99 of index.js
and see if removing parameters does anything? Try removing the dps
parameter to start with.
*only remove dps* TuyAPI IP and ID are already both resolved. +0ms TuyAPI Connecting to 10.1.9.42... +7ms TuyAPI Socket connected. +6ms Connected to device! TuyAPI GET Payload: +3ms TuyAPI { TuyAPI gwId: 'eb0f4c330d33b7f24bmugy', TuyAPI devId: 'eb0f4c330d33b7f24bmugy', TuyAPI t: '1598579310', TuyAPI uid: 'eb0f4c330d33b7f24bmugy' TuyAPI } +0ms TuyAPI GET Payload: +9ms TuyAPI { TuyAPI gwId: 'eb0f4c330d33b7f24bmugy', TuyAPI devId: 'eb0f4c330d33b7f24bmugy', TuyAPI t: '1598579310', TuyAPI uid: 'eb0f4c330d33b7f24bmugy' TuyAPI } +0ms TuyAPI Received data: 000055aa000000010000000a0000002c000000010c233b4c2abd2e1f0e3a93e58522ebef6fce531ff98e92602e53a886fad6ea5b9cab5f1b0000aa55 +24ms TuyAPI Parsed: +3ms TuyAPI { TuyAPI payload: 'parse data error', TuyAPI leftover: false, TuyAPI commandByte: 10, TuyAPI sequenceN: 1 TuyAPI } +1ms Data from device: parse data error parse data error TuyAPI Received data: 000055aa000000020000000a0000002c000000010c233b4c2abd2e1f0e3a93e58522ebef6fce531ff98e92602e53a886fad6ea5ba360218e0000aa55 +15ms TuyAPI Parsed: +1ms TuyAPI { TuyAPI payload: 'parse data error', TuyAPI leftover: false, TuyAPI commandByte: 10, TuyAPI sequenceN: 2 TuyAPI } +0ms Data from device: parse data error TuyAPI Disconnect +10s TuyAPI Socket closed: 10.1.9.42 +2ms Remove all TuyAPI IP and ID are already both resolved. +0ms TuyAPI Connecting to 10.1.9.42... +6ms TuyAPI Socket connected. +14ms Connected to device! TuyAPI GET Payload: +3ms TuyAPI {} +0ms TuyAPI GET Payload: +7ms TuyAPI {} +0ms TuyAPI Received data: 000055aa000000010000000a0000002c000000010c233b4c2abd2e1f0e3a93e58522ebef6fce531ff98e92602e53a886fad6ea5b9cab5f1b0000aa55 +20ms TuyAPI Parsed: +3ms TuyAPI { TuyAPI payload: 'parse data error', TuyAPI leftover: false, TuyAPI commandByte: 10, TuyAPI sequenceN: 1 TuyAPI } +1ms Data from device: parse data error parse data error TuyAPI Received data: 000055aa000000020000000a0000002c000000010c233b4c2abd2e1f0e3a93e58522ebef6fce531ff98e92602e53a886fad6ea5ba360218e0000aa55 +23ms TuyAPI Parsed: +1ms TuyAPI { TuyAPI payload: 'parse data error', TuyAPI leftover: false, TuyAPI commandByte: 10, TuyAPI sequenceN: 2 TuyAPI } +0ms
I found something interesting that the same code test with tuya wire zigbee hub and result is different.
/homebridge # DEBUG=* node gateway.js
TuyAPI IP and ID are already both resolved. +0ms
TuyAPI Connecting to 10.1.9.28... +6ms
TuyAPI Socket connected. +5ms
Connected to device!
TuyAPI GET Payload: +2ms
TuyAPI {
TuyAPI gwId: 'ebdd7925c7531bf4falakg',
TuyAPI devId: 'ebd2e8be35c5cc00d3nkp9',
TuyAPI t: '1599384049',
TuyAPI dps: {},
TuyAPI uid: 'ebd2e8be35c5cc00d3nkp9'
TuyAPI } +1ms
TuyAPI Received data: 000055aa000000010000000a0000001c00000001a4fd046f973e79562bee4dfbd672a9ca03a3f1f40000aa55 +15ms
TuyAPI Parsed: +2ms
TuyAPI {
TuyAPI payload: 'devid not found',
TuyAPI leftover: false,
TuyAPI commandByte: 10,
TuyAPI sequenceN: 1
TuyAPI } +1ms
Data from device: devid not found
TuyAPI Pinging 10.1.9.28 +10s
TuyAPI Received data: 000055aa00000000000000090000000c00000000b051ab030000aa55 +5ms
TuyAPI Parsed: +0ms
TuyAPI { payload: false, leftover: false, commandByte: 9, sequenceN: 0 } +1ms
TuyAPI Pong from 10.1.9.28 +0ms
TuyAPI Disconnect +5s
TuyAPI Socket closed: 10.1.9.28 +2ms
Disconnected from device.
I meet the same problem than Nurfle who originated this thread. But I don't understand your reply "It was decided here (see linked issue thread) that for some applications returning a null payload for that specific error is acceptable." Acceptable?
@kart-able I can see both sides of the argument. Maybe a flag to control this behavior would make more sense?
I had some hope in a solution because, even if it says json obj data unvalid
, the switch goes on. But it can't go off for some reason...
@codetheweb I am also facing the same "json obj data unvalid" issue. It occurs when i connect for the first time and for every GET request. For SET request i am not getting any error. I am getting similar outputs of @tasict .
Can we find a solution for this. I can provide any debug logs if you need.
Cheers 👍
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.
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.
Thanks for looking into it @tsightler.
Maybe we could issue a SET command with a null payload by default to get the current state (assuming that works on all devices)?
I have been wondering the same thing and have even considered doing that in my code, but I'm not 100% confident it works every time. I mean, it always appears to trigger at least a data update, but I don't always seem to get a response from the SET command itself. For my code this is OK because I trap all data updates to cache the DPS values, so the effective behavior is the same either way, but I need to dig a little more before I feel truly comfortable with this approach as the primary method.
OK, maybe this was just a bug in my code as I had a place where I was attempting to parse the result of the SET as if it was a GET. Once I fixed that I can't find any cases where a SET does not return a valid result, so indeed.
I wish we had a response that was more clear instead of the weird response with all the null values. It took me quite a while to realize that TuyAPI itself was generating this response and not the device. Couldn't this response be pretty much anything? Like some JSON with "error:" and the actual response from the device. Sending random data that looks like it might have been a valid response just seems like a strange way to handle this case.
I wish we had a response that was more clear instead of the weird response with all the null values. It took me quite a while to realize that TuyAPI itself was generating this response and not the device. Couldn't this response be pretty much anything? Like some JSON with "error:" and the actual response from the device. Sending random data that looks like it might have been a valid response just seems like a strange way to handle this case.
Yeah, I agree. I'm going to change this behavior so it's an opt-in flag rather than doing it by default.
This behavior is now opt-in in v6.0.0.
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:
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.
Falling back to *_NEW is something I intended to implement but never quite got around to. You're on the right track.
@tsightler that xaal project looks pretty cool, surprised I haven't heard about it.
I'll try to hack my local TuyAPI to use the CONTROL_NEW fallback and see if it works.
👍
My first attempt wasn't very successful. It seems that responses to CONTROL_NEW are received as command type STATUS, which appears to be the same as SEND function so I tried to implement this the same as SEND. Copied that function, modified it to send the CONTROL_NEW command in proper format, checked for a NULL response in the get() function and fall back to calling get_control_new(), and well, it seemed to work from a code perspective, but I never saw a STATUS from my device.
So I spent a bunch of time looking at this other implementations and trying to make sure that I was doing everything correctly. Couldn't see anything different, so decided to actually try some of those implementations on my device because of course I had only actually read the code, not actually tried any of them with this specific device that was the most problematic.
Well, guess what, those alternate implementations don't succeed on my device either. Tuyaface tries DP_QUERY, falls back to CONTROL_NEW, but still never gets a response device, so what that tells me is that CONTROL_NEW isn't supported on some devices either. I had seen notes in some implementations that CONTROL_NEW didn't always return the full data, but I hadn't seen any reports that there were cases where it didn't work at all. Oh well, back to the drawing board.
So now I'm starting to wonder if the fallback should indeed just be to SET null since, at least for the cases where a DPS key is specifically requested, this is easy to implement. For the "schema" case, it's a bigger issue and I'm not sure how that would work.
I guess maybe I need to do some packet captures from my app and decode them to see how the app is doing this, but there's some part of me that expects it might just cache the DPS values in the app and never needs to query the schema. Is there any already existing data on other ways to get the schema from newer Tuya devices?
I guess maybe I need to do some packet captures from my app and decode them to see how the app is doing this, but there's some part of me that expects it might just cache the DPS values in the app and never needs to query the schema.
Also possible it controls it through API calls to Tuya's cloud. The app seems to quite aggressively fall back on that if there's any type of problem communicating with a device locally.
Is there any already existing data on other ways to get the schema from newer Tuya devices?
Not that I'm aware of.
Also possible it controls it through API calls to Tuya's cloud. The app seems to quite aggressively fall back on that if there's any type of problem communicating with a device locally.
Indeed, but I have all my automation devices on a separate VLAN/Wifi network so I just put my phone on that and firewall it from the internet, which leaves the app no choice, it's local control or nothing. That being said, I haven't tried that with this specific device yet.
So, after hitting the wall with CONTROL_NEW for the time being, I decided the best short term option was to implement a fallback to SET for any case where GET returns null. This wouldn't help with the get schema case, but at least GET calls for specific DPS values would work in all cases. I wrote a quick check for the "json obj data unvalid" message and had it return "Device does not support schema command" for schema and otherwise try the SET null method. This worked great, fall back was transparent to the API consumer, and issuing a schema command returned a message that was at least useful to understand what was happening.
However, I did hit one pretty decent snag. The summary version is, if you issue a SET null to a DPS that does not exist on the device, the device does not send a reply at all, so the set call will just wait forever for a STATUS command that never comes. To me this seems like a bug in SET as it should almost certainly timeout I'd guess, after a couple of seconds at most. I see a _responseTimout parameter, but don't see it used anywhere. Was this just something that was never implemented?
OK, implemented a simple timeout on the send function, seems to work. I'll submit an PR and the powers that be can review.
One thing I don't understand, in the SET command there's a flag "_waitingForSetToResolve". This starts off false, but the first time you issue a SET it's set to true. Ok, makes sense, we've issued a SET statement and we need to wait for a STATUS response with the response data so this flag is indicating that any CONTROL commands should be processed as a response to the SET. I see the flag used in the packetHandler as part of the conditions for handling the resolve. However, once this flag is set to true, it's never set false again, so what's the point really? It feels like the check for _setResolver being a function is probably good enough. Is this just some artifact of the past or am I completely misinterpreting this code? I don't really see how though, basically, it's set to true for the device and never reset back to false at any point so my lean is to just remove it completely.
I see a _responseTimout parameter, but don't see it used anywhere. Was this just something that was never implemented?
According to git blame the logic for that was removed in https://github.com/codetheweb/tuyapi/pull/143; although I couldn't tell you why. I blame past me for not writing a more descriptive commit message. 😛 It does probably make sense to add it back, but that should be a separate PR. Should have looked at your PR first, sorry. Should be fine.
It feels like the check for _setResolver being a function is probably good enough. Is this just some artifact of the past or am I completely misinterpreting this code? I don't really see how though, basically, it's set to true for the device and never reset back to false at any point so my lean is to just remove it completely.
You're correct, this is no longer used. I think its purpose was to prop up the spaghetti code that was/is the synchronous + event based functions.
Thanks to some awesome work by @tsightler in #363, behavior for this should now be slightly improved.
As long as you specify the DPS index when calling get()
(omit it to default to 1
or get({dps: 5})
), null payloads should no longer be returned in v6.0.1. I'm tentatively closing this, if anyone continues to have issues please let me know and I'll reopen it.
Describe the bug Null payload informations returned
To Reproduce
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'])});
} });
// Disconnect after 10 seconds setTimeout(() => { device.disconnect(); }, 10000);
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.