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

Disconnected event not emitted & no reconnect #275

Closed
neojski opened this issue Dec 15, 2019 · 7 comments
Closed

Disconnected event not emitted & no reconnect #275

neojski opened this issue Dec 15, 2019 · 7 comments

Comments

@neojski
Copy link
Contributor

neojski commented Dec 15, 2019

Describe the bug
Disconnected event is not emitted when I remove power from the device. It's only emitted once the device is plugged back in and connects to the network. What's more, the device never recovers.

I think there used to be something called persistent connection with previous versions of TuyAPI. I've recently updated the package and it's not there anymore. So maybe I'm just not using it correctly now?

To Reproduce

  1. connect to smart plug
  2. unplug it from mains

Expected behavior

  1. disconnected event should be emitted but isn't.
  2. once plugged back in further "data" events should be generated but aren't

Debug Output

  TuyAPI Finding missing IP undefined or ID 1274756684f3ebb89107 +0ms
  TuyAPI Received UDP message. +669ms
  TuyAPI UDP data: +4ms
  TuyAPI {
  TuyAPI   payload: {
  TuyAPI     ip: '192.168.0.40',
  TuyAPI     gwId: '1274756684f3ebb89107',
  TuyAPI     active: 2,
  TuyAPI     ability: 0,
  TuyAPI     mode: 0,
  TuyAPI     encrypt: true,
  TuyAPI     productKey: 'UhLkQlpfvO0fdrf3',
  TuyAPI     version: '3.1'
  TuyAPI   },
  TuyAPI   leftover: false,
  TuyAPI   commandByte: 0,
  TuyAPI   sequenceN: 0
  TuyAPI } +0ms
  TuyAPI Connecting to 192.168.0.40... +8ms
  TuyAPI Socket connected. +10Post 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):ms
  TuyAPI GET Payload: +1ms
  TuyAPI { gwId: '1274756684f3ebb89107', devId: '1274756684f3ebb89107' } +0ms
  TuyAPI Received data: 000055aa000000010000000a00000059000000007b226465764964223a223132373437353636383466336562623839313037222c22647073223a7b2231223a66616c73652c2232223a302c2234223a302c2235223a302c2236223a323331337d7d3f781abf0000aa55 +14ms
  TuyAPI Parsed: +1ms
  TuyAPI {
  TuyAPI   payload: {
  TuyAPI     devId: '1274756684f3ebb89107',
  TuyAPI     dps: { '1': false, '2': 0, '4': 0, '5': 0, '6': 2313 }
  TuyAPI   },
  TuyAPI   leftover: false,
  TuyAPI   commandByte: 10,
  TuyAPI   sequenceN: 1
  TuyAPI } +0ms
  TuyAPI Received data: 000055aa00000000000000080000008b00000000332e3131613133336239653337306636373266487848714c6f394d5a6c5a796a6f5757514267683733384c6863456b356a526833644b4b4651695050315a785868795756546f69794a432b2f397931766a30776438723256706752687279414d6c7653454b69654a4d76434a312f5570305766556d434c39707a6870316f3d1cfb1ee50000aa55 +6s
  TuyAPI Parsed: +2ms
  TuyAPI {
  TuyAPI   payload: {
  TuyAPI     devId: '1274756684f3ebb89107',
  TuyAPI     dps: { '6': 2306 },
  TuyAPI     t: 1576409759,
  TuyAPI     s: 18
  TuyAPI   },
  TuyAPI   leftover: false,
  TuyAPI   commandByte: 8,
  TuyAPI   sequenceN: 0
  TuyAPI } +0ms
  TuyAPI Pinging 192.168.0.40 +4s
  TuyAPI Received data: 000055aa00000000000000090000000c00000000b051ab030000aa55 +212ms
  TuyAPI Parsed: +0ms
  TuyAPI { payload: false, leftover: false, commandByte: 9, sequenceN: 0 } +0ms
  TuyAPI Pong from 192.168.0.40 +1ms
  TuyAPI Received data: 000055aa00000000000000080000008b00000000332e3136323263386432613938303165333365487848714c6f394d5a6c5a796a6f5757514267683733384c6863456b356a526833644b4b46516950503161766834643875672b666837466c344a56665a78354f43484845506365664d43386f5462464d48326b44526a30334c662f5a466b674d6475656d474a59316e4d6f3d6702b7b70000aa55 +8s
  TuyAPI Parsed: +2ms
  TuyAPI {
  TuyAPI   payload: {
  TuyAPI     devId: '1274756684f3ebb89107',
  TuyAPI     dps: { '6': 2296 },
  TuyAPI     t: 1576409771,
  TuyAPI     s: 19
  TuyAPI   },
  TuyAPI   leftover: false,
  TuyAPI   commandByte: 8,
  TuyAPI   sequenceN: 0
  TuyAPI } +0ms
  TuyAPI Pinging 192.168.0.40 +2s
  TuyAPI Received data: 000055aa00000000000000090000000c00000000b051ab030000aa55 +53ms
  TuyAPI Parsed: +0ms
  TuyAPI { payload: false, leftover: false, commandByte: 9, sequenceN: 0 } +0ms
  TuyAPI Pong from 192.168.0.40 +0ms
*** this is when I disconnected the plug
  TuyAPI Pinging 192.168.0.40 +10s
  TuyAPI Pinging 192.168.0.40 +10s
  TuyAPI Pinging 192.168.0.40 +10s
*** this is when I connected the plug back. I expected the disconnect to happen when the plug was actually disconnected and recover here
  TuyAPI Error event from socket. 192.168.0.40 Error: read ECONNRESET
    at TCP.onStreamRead (internal/stream_base_commons.js:201:27) {
  errno: 'ECONNRESET',
  code: 'ECONNRESET',
  syscall: 'read'
} +5s
  TuyAPI Socket closed: 192.168.0.40 +1ms
*** no more output ever

Screenshots
None

Desktop (please complete the following information):

  • OS: archlinux
  • OS Version: N/A
  • Node Version: v12.13.1

Additional context
None

@neojski
Copy link
Contributor Author

neojski commented Dec 15, 2019

FWIW, adding a loop:

    setInterval(function () {
      device.connect();
    }, 1000);

that was previously there and was removed in e193cae#diff-168726dbe96b3ce427e7fedce31bb0bcL640 makes the plug recover after disconnection. Given that the commit says "remove persistent option" this is a feature and not a bug? (though it seems like a pretty weird decision to me).

@neojski
Copy link
Contributor Author

neojski commented Dec 15, 2019

I've found some dead come, namely

tuyapi/index.js

Line 390 in c3425f9

clearTimeout(this._sendTimeout);
. I think I want to make this code alive again and on kill the socket when _sendTimeout triggers.

It also looks to me that device.connect is leaking resources because it only has two states: connected and disconnected. It creates a new socket if the status is disconnected but it takes some time to connect. So if you call device.connect a couple of times in a short period it'll leak resources.

@codetheweb
Copy link
Owner

In earlier versions, if the persistent option was set to false TuyAPI would reconnect to the device on every function call (get() and set()). If it was set to true, TuyAPI would keep a socket open and send heartbeat packets. With the event-based code, reopening a socket on every function call just stopped making sense.

You're correct that the line you linked to is dead. Also, I'm not sure why

tuyapi/index.js

Line 290 in c3425f9

// this.emit('error', new Error('connection timed out'));
is commented out.

To be honest, it's been a while since I've touched this code but I believe I removed the reconnection code on purpose. It seemed like people were already handling it themselves. I should have documented doing so, at the very least. The new @tuyapi/driver package has a better defined scope, so hopefully that will clear things up a little.

Finally, I'm not an expert on Node.js memory management but I think it's fine if this.client = new net.Socket(); is ran multiple times as it's assigning the socket reference to what is in effect a global variable. If this.client is overwritten, the old socket can be garbage collected.

@neojski
Copy link
Contributor Author

neojski commented Dec 16, 2019

I would be happy to switch to the typescript @tuyapi/driver but it doesn't seem to provide the service discovery that the old API provides: it requires ip.
Also, it has a some other issues I've noticed so far:

  1. it doesn't emit disconnected so there's no easy way to figure out when to reconnect
  2. after adding code to emit reconnect it never actually recovers. I don't know why that is yet

If I were to write a fix would you prefer me to fix this library or the typescript driver?

@neojski
Copy link
Contributor Author

neojski commented Dec 16, 2019

Fine, I actually figured it out in @tuyapi/driver: _handleSocketConnect calls _recursiveHeartbeat which checks for old heartbeats (this is good and an improvement over how it works here) but the last heartbeat is actually old because it wasn't reset after reconnect. I'll fix that and send a pull request.

@codetheweb
Copy link
Owner

Cool, thanks. (Any fixes baring major bugs should go to the new package).

If you look at the development branch of that repo there's a Find class. Not sure yet if I'm going to integrate that with the Device class or leave that up to the consumer, as the driver package is supposed to be lower-level and leave much of the implementation up to the user.

You may also want to follow this issue: #169.

@neojski
Copy link
Contributor Author

neojski commented Dec 16, 2019

TuyaAPI/driver#29

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants