Page 1 of 3

Comparing Device Objects [Solved]

PostPosted: Sat Mar 17, 2018 7:39 pm
by DaveL17
I think I might be code-blind. I'm working with something based on subscribeToChanges() and trying to isolate on what's actually changed between origDev and newDev. In starting to work on a way to compare the two objects, I came across a result that I find perplexing.

Code: Select all
dev1 = indigo.devices[1149686816]
dev2 = indigo.devices[1149686816]
indigo.server.log(str(dev1 == dev2))
Log:
Code: Select all
Mar 17, 2018, 8:31:34 PM
   Script                          False

I would think they would be equal......but how to tell the difference between the two?

I started down a path of converting the device instances to Unicode and then parsing the string, but is there an easier way? What I want is only the changes:
Code: Select all
# prop or state: (old, new)
brightness: (42, 100)
onState: (False, True)

Re: Comparing Device Objects

PostPosted: Sat Mar 17, 2018 7:50 pm
by Colorado4Wheeler
Code: Select all
if old.onState == new.onState:
   ...


How I check for changes is iterate through all the attributes and states. So for states:
Code: Select all
for key, val in old.states.iteritems():
  if key in new.states and new.states[key] != old.states[key]:
   ....


Edit: I forgot to tack on .iteritems() to that iteration.

Re: Comparing Device Objects

PostPosted: Sat Mar 17, 2018 8:31 pm
by FlyingDiver
DaveL17 wrote:
I think I might be code-blind. I'm working with something based on subscribeToChanges() and trying to isolate on what's actually changed between origDev and newDev. In starting to work on a way to compare the two objects, I came across a result that I find perplexing.

Code: Select all
dev1 = indigo.devices[1149686816]
dev2 = indigo.devices[1149686816]
indigo.server.log(str(dev1 == dev2))
Log:
Code: Select all
Mar 17, 2018, 8:31:34 PM
   Script                          False

I would think they would be equal......but how to tell the difference between the two?


They're not equal because your Python code is getting a copy of the actual device info on the server. Not a pointer to it. So each copy is distinct. And you're comparing the device object pointers, not the contents.

Re: Comparing Device Objects

PostPosted: Sun Mar 18, 2018 5:41 am
by DaveL17
Colorado4Wheeler wrote:
Code: Select all
if old.onState == new.onState:
   ...


How I check for changes is iterate through all the attributes and states. So for states:
Code: Select all
for key, val in old.states.iteritems():
  if key in new.states and new.states[key] != old.states[key]:
   ....


Edit: I forgot to tack on .iteritems() to that iteration.

Thanks. I should've been more clear (told you I was code blind)...

I realize I can compare them element by element, I was hoping there was a more straightforward way of doing the comparison. I don't know, like with a dictionary comprehension or something:
Code: Select all
dev1 = {'k1': 'v1', 'k2': 'v2'}
dev2 = {'k1': 'v1', 'k2': 'v3'}

d = {k: (dev1[k], dev2[k]) for k in dev1.keys() if dev1[k] != dev2[k]}

indigo.server.log(str(d))

Result:
Code: Select all
{'k2': ('v2', 'v3')}

But that won't work for device objects. How about indigo.listOfChanges(origDev, newDev)? :)

FlyingDiver wrote:
And you're comparing the device object pointers, not the contents.

That's the bit I find perplexing. Why isn't it comparing the contents? If I compare two dictionaries with the same contents, they will come back as equal. In one case it's comparing the pointers and in the other case it's comparing the values.

Re: Comparing Device Objects

PostPosted: Sun Mar 18, 2018 6:54 am
by FlyingDiver
DaveL17 wrote:
FlyingDiver wrote:
And you're comparing the device object pointers, not the contents.

That's the bit I find perplexing. Why isn't it comparing the contents? If I compare two dictionaries with the same contents, they will come back as equal. In one case it's comparing the pointers and in the other case it's comparing the values.


I believe that's because the dictionary data type implements the __cmp__ function which is designed to do a deep compare. The Indigo device data type does not.

Re: Comparing Device Objects

PostPosted: Sun Mar 18, 2018 7:16 am
by DaveL17
FlyingDiver wrote:
I believe that's because the dictionary data type implements the __cmp__ function which is designed to do a deep compare. The Indigo device data type does not.

I see I need to do a bit more studying. :)

Re: Comparing Device Objects

PostPosted: Sun Mar 18, 2018 7:23 am
by FlyingDiver
https://docs.python.org/2/reference/dat ... ct.__cmp__

object.__cmp__(self, other)
Called by comparison operations if rich comparison (see above) is not defined. Should return a negative integer if self < other, zero if self == other, a positive integer if self > other. If no __cmp__(), __eq__() or __ne__() operation is defined, class instances are compared by object identity (“address”)

Re: Comparing Device Objects

PostPosted: Sun Mar 18, 2018 7:29 am
by DaveL17
Thanks!

Re: Comparing Device Objects

PostPosted: Sun Mar 18, 2018 1:51 pm
by matt (support)
We probably should implement (internally) the __cmp__ method for Indigo objects. But in the mean time I think what you want to do is this:

indigo.server.log(str(dev1.id == dev2.id))

As internally Indigo forces all objects to have a unique ID which is not mutable (and doesn't change as python instances of the objects are created).

Re: Comparing Device Objects

PostPosted: Sun Mar 18, 2018 2:10 pm
by DaveL17
Thanks Matt -- I'm looking to compare the 'origDev' and 'newDev' objects that are sent to def deviceUpdated(self, origDev, newDev) for changes. I'm looking to identify what about the device has changed without iterating through all the props and states. Maybe that's the only way to do it...

Re: Comparing Device Objects

PostPosted: Sun Mar 18, 2018 4:56 pm
by matt (support)
Ah yes, my suggestion won't help with that at all. I think even if we implemented the __cmp__ method that wouldn't help with what you are after though, since you want a list of exactly what has changed. Here is roughly how the SQL Logger plugin handles it:

changeDict = { key:val for key, val in newDev.states.iteritems() if key not in origDev.states or val != origDev.states[key] }

Although I don't believe that handles the case of a state being deleted (it does handle them being added). That is pretty much an edge case though, as in general plugins should never delete a state since it will potentially break Triggers, Conditionals, etc., that reference it.

Re: Comparing Device Objects

PostPosted: Mon Mar 19, 2018 6:03 am
by DaveL17
Thanks Matt. This works great for device states, but I was also looking for a way to detect changes to device props. But after a few tests, it doesn't look like deviceUpdated() is called when a device's props change. Does that sound right?

Re: Comparing Device Objects

PostPosted: Mon Mar 19, 2018 6:25 am
by matt (support)
deviceUpdated() is called when the properties (or anything else: name, states, etc.) change. I'm not sure why it isn't in your case/example, but definitely is routinely (we have plugins that rely on it).

Re: Comparing Device Objects

PostPosted: Mon Mar 19, 2018 7:17 am
by DaveL17
All I can come up with is this error:

Code: Select all
   Script Error                    embedded script: developer is too stupid to live
   Script Error                    Exception Traceback (most recent call shown last):

     embedded script, line 123, at top level
PEBKACError: developer is too stupid to live

It is, of course, called. So I guess my question is this: Is there an iterator I can call to grab all the device's props for inspection? I haven't been able to land on one that seems to work. May also be a result of the above error.

If I need to call the props from an internal list, I can do that, too.

Re: Comparing Device Objects

PostPosted: Mon Mar 19, 2018 8:59 am
by Colorado4Wheeler
You missed a critical 'Except' in that trap....

Code: Select all
Except:
  "He writes good plugins that a lot of people use and that makes up for anything he thinks are dumb mistakes"


:lol: