Differences between indigo.Dict and python dict

Posted on
Thu Jun 21, 2012 1:30 pm
bcall offline
Posts: 59
Joined: May 17, 2012

Differences between indigo.Dict and python dict

I defined a dictionary variable in my plugin init method using myDict = indigo.Dict(). I subsequently tried to assign an entry to the dictionary using myDict['key'] = value but threw an error. If I change the definition of the variable in my init method to myDict = {} there is no error. But, it raises a question about my understanding (or lack thereof) regarding indigo.Dict().

I thought indigo.Dict() did the same thing as the python dict() and returned a dictionary object initialized to whatever is in the parens. After reviewing the forum, I've seen some posts indicating that it's similar, but not exactly the same.

Is there somewhere I can find a description of indigo.Dict(), how it differs from dict() and when I should use one vs the other in Indigo?

Posted on
Thu Jun 21, 2012 1:35 pm
matt (support) offline
Site Admin
User avatar
Posts: 21411
Joined: Jan 27, 2003
Location: Texas

Re: unexpected behavior from indigo.Dict()

There are some differences, which I'll outline in a minute.

But what was the error, and what was the instance type of your value?

Image

Posted on
Thu Jun 21, 2012 2:01 pm
bcall offline
Posts: 59
Joined: May 17, 2012

Re: unexpected behavior from indigo.Dict()

Here's a brief outline:

Code: Select all
def __init__:
    self.myDevices=indigo.Dict()

deviceStartComm:
    deviceDict = dict([(device.address, device) for device in indigo.devices
           if device.pluginId==self.pluginId
           and device.deviceTypeId=='aType'])
    self.myDevices['aType'] = deviceDict


I'm creating a lookup table for certain of my plugin's devices per a suggestion Jay made in one of the forum posts.

I get an "unexpected XML" error
I'm not at my computer right now, so I can't get the exact text of the error. If you need that, please let me know and I'll add that information when I get back to the screen (I'm sending this from my phone).

When I change the original declaration to self.myDevices={} or self.myDevices=dict(), I no longer get the error.

Posted on
Thu Jun 21, 2012 2:10 pm
matt (support) offline
Site Admin
User avatar
Posts: 21411
Joined: Jan 27, 2003
Location: Texas

Re: unexpected behavior from indigo.Dict()

Got it. The problem is the value you are assigning to the indigo.Dict is a python dict (that part is okay) which contains a list of tuples that has a device instance (not okay). indigo.Dicts can hold most atomic types (bool, ints, floats, strings) and list/dict containers, but only if those list/dict containers also only contain compatible types. In this case, the device instance is likely what is causing the exception.

I'll post some additional differences in a bit.

Image

Posted on
Thu Jun 21, 2012 2:27 pm
bcall offline
Posts: 59
Joined: May 17, 2012

Re: unexpected behavior from indigo.Dict()

Could you also post info about the benefits of an indigo.Dict vs a python dict, and when I should use one vs the other. I'd like to be able to take advantage of any added functionality.

Thanks!

Btw- it was Jay's suggestion (in his post) to put the actual dev in the lookup dictionary rather than just the dev's id (which wouldn't have caused the error). It's a great suggestion on Jay's part, but it did add about a half an hour to my daily aggro. :)

Posted on
Thu Jun 21, 2012 2:45 pm
jay (support) offline
Site Admin
User avatar
Posts: 18199
Joined: Mar 19, 2008
Location: Austin, Texas

Re: unexpected behavior from indigo.Dict()

You really only need to use Indigo.dicts when you want the indigo server to store something for you permanently (in a props dict for instance). If the objects are just for use during runtime and don't need to be sent to the server then I just use normal python dicts.

Storing instances vs just the id is a decision best made by examining what you want to do with the value. If you're just going to pass the ID to other methods then just store that. If, however, you need to inspect some other aspects of the device instance then storing the instance is better than getting it from the server every time. You have to decide what's best for your situation.

I'm not sure which post you're talking about, but if your device has states the need updating then you must call the updateStateOnServer method on the device instance. And since most devices have states that need updating it's more efficient to store the instance rather than the ID which requires that you get the device from the server each time you need it.

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Posted on
Thu Jun 21, 2012 2:51 pm
matt (support) offline
Site Admin
User avatar
Posts: 21411
Joined: Jan 27, 2003
Location: Texas

Differences between indigo.Dict and python dict

Differences between indigo.Dict and python dict (and indigo.List and python list)

The indigo.Dict and indigo.List classes are wrappers around internal dict and list objects used by Indigo server and clients. They provide python access to native Indigo instances directly, and allow an easy mechanism for plugins and scripts to access and store data in Indigo, such as preferences, device properties, etc.

The behavior of indigo.Dict and indigo.List is similar but not identical to the native python containers. Specifically, the Indigo containers:
  • must contain values that are of types: bool, float, int, string, list or dict. Any list/dict containers must recursively contain only compatible values.
  • do not support value access via slicing (we might add this eventually).
  • always retrieve values (ex: myprops["key"] or mylist[2]) as a copy and not reference of the object.
  • keys can only contain letters, numbers, and other ASCII characters.
  • keys cannot contain spaces.
  • keys cannot start with a number or punctuation character.
  • keys cannot start with the letters XML, xml, or Xml.

The third point above can lead to some unexpected behaviors when compared to python dicts and lists, especially when multiple levels of container nesting is being used. What is means is that you can set items and append to items, but the items returned from the get / iterators are always new copies of the values and not references. For example, consider:

Code: Select all
c = indigo.List()
c.append(4)
c.append(5)
c.append(True)
c.append(False)

a = indigo.Dict()
a['a'] = "the letter a"
a['b'] = False
a['c'] = c            # a COPY of list C above is inserted (not a reference)
print(str(a))

# Because a['c'] has a COPY of list C this will NOT append to instance a:
c.append(6)
print(str(a))         # no change from previous output!

# And because a['c'] returns a COPY of its value this will also NOT append to instance a:
a['c'].append(6)
print(str(a))         # no change from previous output!

But all is not lost. You can just re-assign the nested object to the root container:

Code: Select all
# Instead you must re-assign a copy to the container. Since we already
# appended c with 6 above, we just need to reassign it now:
a['c'] = c
print(str(a))         # now has a COPY of the updated object, c

If you just need to override an existing value in a container (and not append to it), then a more efficient solution is to use our helper method setitem_in_item. It avoids creating temporary copies of values.

Code: Select all
a.setitem_in_item('c', 4, "the number six")
print(str(a))

Likewise, it is most efficient to use getitem_in_item when accessing nested items since it avoids temporary copies of containers. For example, this creates a temporary copy of the c object before accessing index 4:

Code: Select all
a['c'][4]               # works, but is slow because a temporary copy of a['c'] is created

Where this returns the same result but is more efficient:

Code: Select all
a.getitem_in_item('c', 4)   # same result, but fast

Image

Posted on
Thu Jun 21, 2012 3:03 pm
bcall offline
Posts: 59
Joined: May 17, 2012

Re: unexpected behavior from indigo.Dict()

Jay,

I think you may have misinterpreted my last post. I really do think placing the actual instance of the dev in the dict (and using a lookup dict in the first place) are great suggestions. It's really simplified some of my code and minimized the calls to the database. I was just poking a little fun because of the unexpected (unexpected to me, that is) incompatibility with indigoDict. :)

Posted on
Mon Apr 21, 2014 4:21 pm
freshwuzhere offline
Posts: 105
Joined: Apr 05, 2012

Re: Differences between indigo.Dict and python dict

Trying to delete some properties before updating with new properties. I don't want old redundant values hanging around so thought it best to get rid of them.

Can't seem to make that work.
Last edited by freshwuzhere on Tue Apr 22, 2014 12:22 am, edited 5 times in total.

Posted on
Mon Apr 21, 2014 5:22 pm
matt (support) offline
Site Admin
User avatar
Posts: 21411
Joined: Jan 27, 2003
Location: Texas

Re: Differences between indigo.Dict and python dict

Do you really want them deleted totally, or do you just want to set them to None (or if they are a string you could set them to an empty string "")?

Normally during polling device states are updated, but not values in the pluginProps dict. States are more difficult to delete, although it is possible (it isn't something you would want to do every time you poll a device though).

If you really want to delete the key/value pair out of the pluginProps dictionary this should work:

Code: Select all
dev = indigo.devices[123456]
newProps = dev.pluginProps
del newProps["someKey"]
dev.replacePluginPropsOnServer(newProps)

Image

Posted on
Tue Apr 22, 2014 12:20 am
freshwuzhere offline
Posts: 105
Joined: Apr 05, 2012

Re: Differences between indigo.Dict and python dict

Thanks for your response - I also made del work as you demonstrated - BUT the problem is with properties.update (or my code :-)

Trying to add a property :-

Code: Select all
 
      localProps = dev.pluginProps
      if len(tokenList) == 8 :
         localProps.update({(name+"ID"):tokenList[0],"checkForUpdates":True})
         localProps.update({(name+"ON_OFF"):tokenList[1],"checkForUpdates":True})
      dev.replacePluginPropsOnServer(localProps)
 


Works fine if (name+"ID") exists - crashes the device and restarts the device comms if (name + "ID") doesn't exist. Documentation says properties.update will add a property if it doesn't exist. It seems to add it then break the device.

Posted on
Tue Apr 22, 2014 8:04 am
matt (support) offline
Site Admin
User avatar
Posts: 21411
Joined: Jan 27, 2003
Location: Texas

Re: Differences between indigo.Dict and python dict

I just tried this in a plugin:

Code: Select all
localProps = dev.pluginProps
localProps.update({("footest1"):True,"footest2":"asdf"})
dev.replacePluginPropsOnServer(localProps)

indigo.server.log(u"footest1 %s" % (str(dev.pluginProps["footest1"])))
indigo.server.log(u"footest2 %s" % (str(dev.pluginProps["footest2"])))


where neither footest1 or footest2 initially existed. It appears to have added the properties to the device and no errors or exceptions occurred. Perhaps the problem is occurring further down your code path?

Image

Posted on
Tue Apr 22, 2014 7:16 pm
freshwuzhere offline
Posts: 105
Joined: Apr 05, 2012

Re: [ANSWERED] Differences between indigo.Dict and python di

OK now wasted another 8+ hours on this......

When ANY property is updated - deviceStopComm is called. If no properties are updated - it runs fine. If you Update Properties with the same values - no problem.

I tried with your code - it behaved the same - when any Property is changed - it stops the Comms.

I'm sure this is something wrong with something I have done but I have placed the localProps.update call in many places and the result is the same - it crashes the device.

It kind of looks like if you update the properties (where the serial device name is stored) it re-starts the Serial Comms to make sure it has the correct serial port name?

Code: Select all
            self.plugin.debugLog("###### ____ ###### \n ###    before update =  " + str(dev) + "\n######______#######")

            localProps = dev.pluginProps
            localProps.update({("footest1"):True,"footest2":"asdf"})
            dev.replacePluginPropsOnServer(localProps)

            indigo.server.log(u"footest1 %s" % (str(dev.pluginProps["footest1"])))
            indigo.server.log(u"footest2 %s" % (str(dev.pluginProps["footest2"])))

            self.plugin.debugLog("###### ____ ###### \n ###    after update =  " + str(dev) + "\n######______#######")



and the output looks like this

CoolMaster2 Debug ###### ____ ######
### before update = address :
com.XXX.plugin : (dict)
devicePort_serialConnType : local (string)
devicePort_serialPortLocal : /dev/cu.usbserial-A601PGPZ (string)
devicePort_serialPortNetRfc2217 : rfc2217:// (string)
devicePort_serialPortNetSocket : socket:// (string)
devicePort_uiAddress : (string)
######______#######

CoolMaster2 footest1 True
CoolMaster2 footest2 asdf
CoolMaster2 Debug ###### ____ ######

### after update = address :
pluginProps : com.XXX.plugin : (dict)
devicePort_serialConnType : local (string)
devicePort_serialPortLocal : /dev/cu.usbserial-A601PGPZ (string)
devicePort_serialPortNetRfc2217 : rfc2217:// (string)
devicePort_serialPortNetSocket : socket:// (string)
devicePort_uiAddress : (string)
footest1 : true (bool)
footest2 : asdf (string)
######______#######
CoolMaster2 Debug SUCCESS in read serial =
CoolMaster2 Debug <<-- entering deviceStopComm, Device: Test_Serial; ID=25594833, Type=CoolMaster_Controller
CoolMaster2 Debug Initiating stop of concurrent thread.

Posted on
Tue Apr 22, 2014 7:32 pm
matt (support) offline
Site Admin
User avatar
Posts: 21411
Joined: Jan 27, 2003
Location: Texas

Re: [ANSWERED] Differences between indigo.Dict and python di

Correct, when a device property changes Indigo calls deviceStopComm on the previous (before changes) device then it calls deviceStartComm on the new device instance. This is so the plugin is alerted to changes to the device, such as serial port, IP addresses, etc, regardless of how they occur (device config UI dialog validated and saved, or a runtime change via dev.replacePluginPropsOnserver()).

The good news is your plugin has control over which properties should trigger the stop/start callback sequences. For example, if you only want stop/start called for mySerialPort property then define:

Code: Select all
   def didDeviceCommPropertyChange(self, origDev, newDev):
      if origDev.pluginProps['mySerialPort'] != newDev.pluginProps['mySerialPort']:
         return True
      return False

Image

Posted on
Tue Apr 22, 2014 7:43 pm
freshwuzhere offline
Posts: 105
Joined: Apr 05, 2012

Re: [ANSWERED] Differences between indigo.Dict and python di

THANKS!

Can't believe I missed this in the documentation! 8) - Sorry it's been a LONG day......

Who is online

Users browsing this forum: No registered users and 9 guests