Comparing Device Objects [Solved]

Posted on
Mon Mar 19, 2018 9:10 am
jay (support) offline
Site Admin
User avatar
Posts: 18220
Joined: Mar 19, 2008
Location: Austin, Texas

Re: Comparing Device Objects

DaveL17 wrote:
So I guess my question is this: Is there an iterator I can call to grab all the device's props for inspection?


I think a slight change to Matt's example above should do it:

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


Untested but I think it should work.

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Posted on
Mon Mar 19, 2018 9:36 am
DaveL17 offline
User avatar
Posts: 6753
Joined: Aug 20, 2013
Location: Chicago, IL, USA

Re: Comparing Device Objects

Thanks Jay, but that won't work for me. Perhaps a bit more information on what's taken me down this rabbit hole. With my plugin, I want to capture all changes--props and states--for devices which are not owned by the plugin. For example, when there is a call for heat from a thermostat device, I want to capture all the changes to that device.

I'm developing against an Indigo dimmer device, and your revision gives me (appropriately):

Code: Select all
TypeError: 'DimmerDevice' object is not iterable


This is the crux of my question. Is there an iterator I can call to get all the dimmer's props, or do I need to write a list and iterate against that?

Code: Select all
for prop in ['batteryLevel', 'blueLevel', 'brightness', ...]:
    indigo.server.log(unicode(getattr(origDev, prop)))

I came here to drink milk and kick ass....and I've just finished my milk.

[My Plugins] - [My Forums]

Posted on
Mon Mar 19, 2018 9:50 am
DaveL17 offline
User avatar
Posts: 6753
Joined: Aug 20, 2013
Location: Chicago, IL, USA

Re: Comparing Device Objects

Colorado4Wheeler wrote:
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:

'Good' may be too kind. :)

I came here to drink milk and kick ass....and I've just finished my milk.

[My Plugins] - [My Forums]

Posted on
Mon Mar 19, 2018 11:15 am
jay (support) offline
Site Admin
User avatar
Posts: 18220
Joined: Mar 19, 2008
Location: Austin, Texas

Re: Comparing Device Objects

DaveL17 wrote:
Thanks Jay, but that won't work for me. Perhaps a bit more information on what's taken me down this rabbit hole. With my plugin, I want to capture all changes--props and states--for devices which are not owned by the plugin. For example, when there is a call for heat from a thermostat device, I want to capture all the changes to that device.

I'm developing against an Indigo dimmer device, and your revision gives me (appropriately):

Code: Select all
TypeError: 'DimmerDevice' object is not iterable


This is the crux of my question. Is there an iterator I can call to get all the dimmer's props, or do I need to write a list and iterate against that?

Code: Select all
for prop in ['batteryLevel', 'blueLevel', 'brightness', ...]:
    indigo.server.log(unicode(getattr(origDev, prop)))


Sorry, should be ownerProps, not pluginProps:

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


Though I will say that I wouldn't have expected you to get the error you did... Hmm

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Posted on
Mon Mar 19, 2018 11:28 am
DaveL17 offline
User avatar
Posts: 6753
Joined: Aug 20, 2013
Location: Chicago, IL, USA

Re: Comparing Device Objects

Thanks Jay. Maybe attributes is the better word to use. I was hoping for an iterator to include changes to:

origDev.address,
origDev.batteryLevel,
origDev.configured,
...
etc.

I don't think there's an iterator to inspect those (at least there's not one I can put my hands on.) I can do it with my list approach, it just requires knowing the device's attributes ahead of time.

I came here to drink milk and kick ass....and I've just finished my milk.

[My Plugins] - [My Forums]

Posted on
Mon Mar 19, 2018 1:27 pm
jay (support) offline
Site Admin
User avatar
Posts: 18220
Joined: Mar 19, 2008
Location: Austin, Texas

Re: Comparing Device Objects

AAHHH! Nope, nothing to iterate over those. Many of them are just pointers back to the appropriate state value, but not all.

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Posted on
Mon Mar 19, 2018 3:53 pm
DaveL17 offline
User avatar
Posts: 6753
Joined: Aug 20, 2013
Location: Chicago, IL, USA

Re: Comparing Device Objects

Cool. Thanks Jay. I think I may have an idea for a workaround.

Sorry for the wild goose chase!

I came here to drink milk and kick ass....and I've just finished my milk.

[My Plugins] - [My Forums]

Posted on
Tue Mar 20, 2018 6:18 am
DaveL17 offline
User avatar
Posts: 6753
Joined: Aug 20, 2013
Location: Chicago, IL, USA

Re: Comparing Device Objects

So I thought I might turn the device dict into a string and parse out all the attributes, but that proved to be a bit too much work to be worthwhile. Instead, I decided to go with a list of desired attributes. I'm not sure that doing the attributes with a dictionary comprehension is the most efficient way, but it works and seems fast.

Code: Select all
    def deviceUpdated(self, origDev, newDev):

        indigo.PluginBase.deviceUpdated(self, origDev, newDev)

        if origDev.id in [697415992,]:

            # Attribute changes
            exclude_list = ['globalProps', 'lastChanged', 'lastSuccessfulComm', 'ownerProps', 'states']
            attrib_list = [attr for attr in dir(origDev) if not callable(getattr(origDev, attr)) and '__' not in attr and attr not in exclude_list]
            attrib_dict = {attrib: (getattr(origDev, attrib), getattr(newDev, attrib)) for attrib in attrib_list if getattr(origDev, attrib) != getattr(newDev, attrib)}

            # Property changes
            orig_props = dict(origDev.ownerProps)
            new_props = dict(newDev.ownerProps)
            props_dict = {key: (orig_props[key], new_props[key]) for key in orig_props if orig_props[key] != new_props[key]}

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

            if len(attrib_dict) > 0 or len(state_dict) > 0 or len(props_dict) > 0:
                indigo.server.log(u"\nDevice Changes: [{0}]\n{1:<8}{2}\n{3:<8}{4}\n{5:<8}{6}".format(newDev.name, 'Attr:', attrib_dict, 'Props', props_dict, 'States', state_dict))


This is the output when the device 'dave' is turned on (note that onState isn't the best example because the attribute and the state are redundant):
Code: Select all
dave: Attribs: {'onState': (False, True)}  States: {u'brightnessLevel': (0, 60), u'onOffState': (False, True)}

This is the output when the device 'dave' is renamed to 'Dave':
Code: Select all
Dave: Attribs: {'name': (u'dave', u'Dave')}  States: {}

This is the output when the device 'Dave' is disabled:
Code: Select all
Dave: Attribs: {'enabled': (True, False)}  States: {}


EDIT: updates to pull attributes from dir(origdey).
Edit 2: Updates to parse changes to device props.
Last edited by DaveL17 on Tue Mar 20, 2018 8:01 pm, edited 2 times in total.

I came here to drink milk and kick ass....and I've just finished my milk.

[My Plugins] - [My Forums]

Posted on
Tue Mar 20, 2018 10:18 am
jay (support) offline
Site Admin
User avatar
Posts: 18220
Joined: Mar 19, 2008
Location: Austin, Texas

Re: Comparing Device Objects [Solved]

You can use the dir method to get a list of object properties:

Code: Select all
property_list = dir(origDev)


That includes methods as well, so you'll need to filter out those. The callable method is useful for that:

Code: Select all
>>> callable(getattr(origDev, "brightness"))
False
>>> callable(getattr(origDev, "refreshFromServer"))
True
>>>


Just another option.

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Posted on
Tue Mar 20, 2018 2:18 pm
DaveL17 offline
User avatar
Posts: 6753
Joined: Aug 20, 2013
Location: Chicago, IL, USA

Re: Comparing Device Objects [Solved]

jay (support) wrote:
You can use the dir method to get a list of object properties:

Code: Select all
property_list = dir(origDev)


That includes methods as well, so you'll need to filter out those. The callable method is useful for that:

Code: Select all
>>> callable(getattr(origDev, "brightness"))
False
>>> callable(getattr(origDev, "refreshFromServer"))
True
>>>


Just another option.

Thanks Jay - this is great. I considered and dismissed dir() because of the reason that callable() is meant to address (didn't know about callable so thanks for that, too). It wasn't perfect, but a slight change to exclude attributes that contain '__' gets there. This tested well. I'll modify the above example later tonight to use this instead.

Code: Select all
[attr for attr in dir(dev) if not callable(getattr(dev, attr)) and not '__' in attr]

Code: Select all
['address', 'batteryLevel', 'blueLevel', 'brightness', 'buttonGroupCount', 'configured', 'defaultBrightness', 'description', 'deviceTypeId', 'displayStateId', 'displayStateImageSel', 'displayStateValRaw', 'displayStateValUi', 'enabled', 'energyAccumBaseTime', 'energyAccumTimeDelta', 'energyAccumTotal', 'energyCurLevel', 'errorState', 'folderId', 'globalProps', 'greenLevel', 'id', 'lastChanged', 'lastSuccessfulComm', 'ledStates', 'model', 'name', 'onBrightensToDefaultToggle', 'onBrightensToLast', 'onState', 'ownerProps', 'pluginId', 'pluginProps', 'protocol', 'redLevel', 'remoteDisplay', 'states', 'subModel', 'supportsAllLightsOnOff', 'supportsAllOff', 'supportsColor', 'supportsRGB', 'supportsRGBandWhiteSimultaneously', 'supportsStatusRequest', 'supportsTwoWhiteLevels', 'supportsTwoWhiteLevelsSimultaneously', 'supportsWhite', 'supportsWhiteTemperature', 'version', 'whiteLevel', 'whiteLevel2', 'whiteTemperature']

I came here to drink milk and kick ass....and I've just finished my milk.

[My Plugins] - [My Forums]

Posted on
Tue Mar 20, 2018 3:55 pm
DaveL17 offline
User avatar
Posts: 6753
Joined: Aug 20, 2013
Location: Chicago, IL, USA

Re: Comparing Device Objects [Solved]

I've updated the example above to include Jay's suggestion to use dir(dev). There's still a little bit of work to go because changes to ownerProps returns an object rather than the detailed change.

Example

I came here to drink milk and kick ass....and I've just finished my milk.

[My Plugins] - [My Forums]

Posted on
Tue Mar 20, 2018 8:08 pm
DaveL17 offline
User avatar
Posts: 6753
Joined: Aug 20, 2013
Location: Chicago, IL, USA

Re: Comparing Device Objects [Solved]

I'm not entirely unhappy with this. It now handles changes to a device's properties (via a config dialog and whatnot). I've revised the example above to reflect the latest.

Example

Change device name:
Code: Select all
Device Changes: [dave 1 device 1x]
Attr:   {'name': (u'dave 1 device 1', u'dave 1 device 1x')}
Props   {}
States  {}

Change device props:
Code: Select all
Device Changes: [dave 1 device 1]
Attr:   {}
Props   {u'setting': (u'foo', u'foobar')}
States  {}

Turn device on:
Code: Select all
Device Changes: [dave 1 device 1]
Attr:   {'onState': (False, True), 'displayStateImageSel': (indigo.kStateImageSel.DimmerOff, indigo.kStateImageSel.DimmerOn), 'displayStateValUi': (u'0', u'60'), 'displayStateValRaw': (0, 60), 'brightness': (0, 60)}
Props   {}
States  {u'brightnessLevel': (0, 60), u'onOffState': (False, True)}

I came here to drink milk and kick ass....and I've just finished my milk.

[My Plugins] - [My Forums]

Posted on
Wed Mar 21, 2018 9:00 am
jay (support) offline
Site Admin
User avatar
Posts: 18220
Joined: Mar 19, 2008
Location: Austin, Texas

Re: Comparing Device Objects [Solved]

Nice!

I love Python.

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Posted on
Wed Mar 21, 2018 3:28 pm
DaveL17 offline
User avatar
Posts: 6753
Joined: Aug 20, 2013
Location: Chicago, IL, USA

Re: Comparing Device Objects [Solved]

Thanks Jay.

I came here to drink milk and kick ass....and I've just finished my milk.

[My Plugins] - [My Forums]

Posted on
Thu Mar 22, 2018 8:25 pm
DaveL17 offline
User avatar
Posts: 6753
Joined: Aug 20, 2013
Location: Chicago, IL, USA

Re: Comparing Device Objects [Solved]

I marked this as solved and then keep posting to it. There should probably be a rule about that. :wink:

the docs wrote:
[ subscribeToChanges() ] Use this method sparingly as it causes a significant amount of traffic between IndigoServer and your plugin.

Is there a way to dynamically enable/disable the changes traffic between the server and the plugin? I'm thinking that with a checkbox I could enable/disable the communication so that the traffic would only be there when it was needed.

Or is there way to effectively unsubscribeToChanges()?

I came here to drink milk and kick ass....and I've just finished my milk.

[My Plugins] - [My Forums]

Who is online

Users browsing this forum: No registered users and 2 guests