Three questions - Network, Parent/Child devices

Posted on
Wed Feb 13, 2019 3:06 pm
Ramias offline
Posts: 272
Joined: Nov 24, 2015

Three questions - Network, Parent/Child devices

I'm enhancing the current TP-Link smartplug plugin to support the 6 outlet smartstrip and the 2 outlet plug. I have it working and it's fantastic. but....

The 6 outlet strip returns a JSON payload that is too big for a single packet. The existing plugin code makes a TCP connection that closes too soon, resulting in incomplete JSON. I can overcome that and just strip it as a string and get the DeviceID I need (since that is at the top of the file) but getting status updates on all six. plugs requires working JSON.

I've overcome this with some UDP code (that I found on GitHub for another non-Indigo solution to this) that works 99.5% of the time; I have three physical devices representing 14 Indigo devices (2 strips and a dual outlet) and the other .5% it throws a timeout error. Code is below. Any suggestions on this?

2nd question:
Thinking of actually re-doing this to have a "parent" device and child devices for the power strips. Where the user would input and create the powerstrip itself as the parent, and the 6 child devices would be automatically created. And for status updates (outlet on/off and energy usage) there'd just be one query issued by the parent device and it would update the child devices within Indigo. (The JSON file for on/off status shows every device).

Any good examples on how to have a parent device create the child devices and update state on them? On/off would still be sent from the child devices.

And last question, this is a power strip, but in my case is mostly used for ceiling and counter lights. Is it possible to reclassify a device via a checkbox from a SimpleRelay to a LightSwitch? Is it as simple as setting supportsAllLightsOnOff to True? And perhaps changing the icon?

Here is the code for sending via TCP from the existing plugin; this works for all but getting info on the 6 outlet powerstripas:
Code: Select all
      if debug:
         print ("send cmd=%s" % (cmd, ))
      try:
         sock_tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
         sock_tcp.connect((self.ip, self.port))
         sock_tcp.send(encrypt(cmd))
         data = sock_tcp.recv(2048)
         sock_tcp.close()

         # don't know what the first 3 decrypted bytes are. skip them
         # Byte 4 is a '?' but for valid json replace it with '{'
         result = decrypt(data)
         return '{' + result[5:]
      except socket.error:
         quit("ERROR: Cound not connect to host " + self.ip + ":" + str(self.port))


And here is what I call for UDP. Timeout is set to 2.

Code: Select all
      if debug:
         print ("send cmd=%s" % (cmd, ))

      client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
      client_socket.settimeout(timeout)

      addr = (self.ip, self.port)

      client_socket.sendto(_encrypt_udp(cmd, prepend_length=False), addr)

      data, server = client_socket.recvfrom(1024)

      result = _decrypt_udp(data)
      return result

Posted on
Wed Feb 13, 2019 3:27 pm
jay (support) offline
Site Admin
User avatar
Posts: 18220
Joined: Mar 19, 2008
Location: Austin, Texas

Re: Three questions - Network, Parent/Child devices

Make your receive buffer bigger?

Code: Select all
data = sock_tcp.recv(4096)

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Posted on
Wed Feb 13, 2019 3:32 pm
FlyingDiver offline
User avatar
Posts: 7217
Joined: Jun 07, 2014
Location: Southwest Florida, USA

Re: Three questions - Network, Parent/Child devices

You might also need to increase the socket timeout:

Code: Select all
sock_tcp.setsockettimeout(5.0)


I don't think it makes sense to try to use UDP for something that you should be able to do using TCP.

For you device management, look at the Device Factory API and SDK example.

joe (aka FlyingDiver)
my plugins: http://forums.indigodomo.com/viewforum.php?f=177

Posted on
Thu Feb 14, 2019 7:48 pm
Ramias offline
Posts: 272
Joined: Nov 24, 2015

Re: Three questions - Network, Parent/Child devices

unfortunately neither of those two options (on their own, or together) work reliably.

The received data ends with "}}}". Could somebody please point me to some sample code that keeps the socket open until this string is returned?

Thanks

Posted on
Thu Feb 14, 2019 8:08 pm
FlyingDiver offline
User avatar
Posts: 7217
Joined: Jun 07, 2014
Location: Southwest Florida, USA

Re: Three questions - Network, Parent/Child devices

How much of the data are you consistently receiving? Are you filling up the buffer or timing out on the read?

joe (aka FlyingDiver)
my plugins: http://forums.indigodomo.com/viewforum.php?f=177

Posted on
Thu Feb 14, 2019 8:39 pm
Ramias offline
Posts: 272
Joined: Nov 24, 2015

Re: Three questions - Network, Parent/Child devices

Buffer is currently 2048 but was 4096 or even larger. Same issue.

String is JSON. Error is:

Code: Select all
ValueError: Unterminated string starting at: line 1 column 1018 (char 1017)


Total JSON string I think is 1221 or so but I think can vary based on values.

Basically it's not getting all of the data, so it's not passing valid JSON.

I'm currently testing this outside of Indigo and will then port the code in. I can press Run time after time; with the TCP code it fails at least half the time, then works. I guess sometimes that second packet arrives faster.

Posted on
Thu Feb 14, 2019 9:33 pm
FlyingDiver offline
User avatar
Posts: 7217
Joined: Jun 07, 2014
Location: Southwest Florida, USA

Re: Three questions - Network, Parent/Child devices

Try this:

Code: Select all
      if debug:
         print ("send cmd=%s" % (cmd, ))
      try:
         sock_tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
         sock_tcp.connect((self.ip, self.port))
         sock_tcp.setsockettimeout(0.5)
         
         sock_tcp.send(encrypt(cmd))
         
         data = ""
         while True:
             new_data = conn.recv(1024)
             if not new_data:
                 break
             else:
                 data = data + new_data
                 
         sock_tcp.close()

         # don't know what the first 3 decrypted bytes are. skip them
         # Byte 4 is a '?' but for valid json replace it with '{'
         result = decrypt(data)
         return '{' + result[4:]        # this now starts on the 5th byte, not the 6th
         
      except socket.error:
         quit("ERROR: Cound not connect to host " + self.ip + ":" + str(self.port))
         


I'm not sure what the "?" is, but could the first three bytes be the length of the incoming packet?

joe (aka FlyingDiver)
my plugins: http://forums.indigodomo.com/viewforum.php?f=177

Posted on
Thu Feb 14, 2019 10:19 pm
Ramias offline
Posts: 272
Joined: Nov 24, 2015

Re: Three questions - Network, Parent/Child devices

Thanks.

That code runs (had to change a few things --
sock_tcp.setsockettimeout had to be sock_tcp.settimeout

and new_data = conn.recv(1024) had to be
new_data = sock_tcp.recv(1024)

It runs but times out and never does the decrypt or socket close; goes straight to the exception.

Posted on
Fri Feb 15, 2019 7:09 am
FlyingDiver offline
User avatar
Posts: 7217
Joined: Jun 07, 2014
Location: Southwest Florida, USA

Re: Three questions - Network, Parent/Child devices

Code: Select all
    if debug:
        print ("send cmd=%s" % (cmd, ))
    try:
        sock_tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock_tcp.connect((self.ip, self.port))
        sock_tcp.settimeout(0.5)
    except socket.error:
        quit("ERROR: Cound not connect to host " + self.ip + ":" + str(self.port))
         
    sock_tcp.send(encrypt(cmd))
         
    data = ""
    while True:
        try:
            new_data = sock_tcp.recv(1024)
            print ("got %d bytes" % (len(new_data)))

        except socket.timeout:
            pass
        except socket.error:
            quit("ERROR: Socket error e: " + str(e))
       
        if not new_data:
            break
        else:
            data = data + new_data
             
     print ("Total data: %d bytes" % (len(data))
     sock_tcp.close()

     # don't know what the first 3 decrypted bytes are. skip them
     # Byte 4 is a '?' but for valid json replace it with '{'
     result = decrypt(data)
     return '{' + result[4:]        # this now starts on the 5th byte, not the 6th
         
Last edited by FlyingDiver on Fri Feb 15, 2019 9:01 am, edited 1 time in total.

joe (aka FlyingDiver)
my plugins: http://forums.indigodomo.com/viewforum.php?f=177

Posted on
Fri Feb 15, 2019 8:49 am
Ramias offline
Posts: 272
Joined: Nov 24, 2015

Re: Three questions - Network, Parent/Child devices

Thanks.

No matter what tweaks I make I keep getting:

line 118
Code: Select all
    except socket.timeout:
         ^

SyntaxError: invalid syntax

Posted on
Fri Feb 15, 2019 9:01 am
FlyingDiver offline
User avatar
Posts: 7217
Joined: Jun 07, 2014
Location: Southwest Florida, USA

Re: Three questions - Network, Parent/Child devices

Ramias wrote:
Thanks.

No matter what tweaks I make I keep getting:

line 118
Code: Select all
    except socket.timeout:
         ^

SyntaxError: invalid syntax


Missing ")" at end of print statement. Oops.

joe (aka FlyingDiver)
my plugins: http://forums.indigodomo.com/viewforum.php?f=177

Posted on
Fri Feb 15, 2019 9:19 am
Ramias offline
Posts: 272
Joined: Nov 24, 2015

Re: Three questions - Network, Parent/Child devices

It runs and I get this:

got 1024 bytes
got 214 bytes

(the other power strip gets 1024 and 226 bytes because it has longer labels for outlet names).

But it keeps running; it's like it is stuck in the first try loop and never times out or throws an error; until it totally times out and fails (never does the decrypt or return).

Thanks again for all the help on this.

Posted on
Fri Feb 15, 2019 9:40 am
FlyingDiver offline
User avatar
Posts: 7217
Joined: Jun 07, 2014
Location: Southwest Florida, USA

Re: Three questions - Network, Parent/Child devices

Code: Select all
    if debug:
        print ("send cmd=%s" % (cmd, ))
    try:
        sock_tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock_tcp.connect((self.ip, self.port))
        sock_tcp.settimeout(0.5)
    except socket.error:
        quit("ERROR: Cound not connect to host " + self.ip + ":" + str(self.port))
         
    sock_tcp.send(encrypt(cmd))
         
    data = ""
    while True:
        try:
            new_data = sock_tcp.recv(1024)
            print ("got %d bytes" % (len(new_data)))
            data = data + new_data

        except socket.timeout:
            break
        except socket.error:
            quit("ERROR: Socket error e: " + str(e))
             
     print ("Total data: %d bytes" % (len(data))
     sock_tcp.close()

     # don't know what the first 3 decrypted bytes are. skip them
     # Byte 4 is a '?' but for valid json replace it with '{'
     result = decrypt(data)
     return '{' + result[4:]        # this now starts on the 5th byte, not the 6th
         

joe (aka FlyingDiver)
my plugins: http://forums.indigodomo.com/viewforum.php?f=177

Posted on
Fri Feb 15, 2019 9:50 am
Ramias offline
Posts: 272
Joined: Nov 24, 2015

Re: Three questions - Network, Parent/Child devices

You sir, are a gentlemen and a scholar!

I think that does it. I had to change this

return '{' + result[4:]

to a 5 but it looks like it's working consistently.

Thanks for the help with this.

Page 1 of 1

Who is online

Users browsing this forum: No registered users and 10 guests