Page 1 of 2

API Addition: dev.stateListOrDisplayStateIdChanged() method

PostPosted: Sat Nov 12, 2011 5:48 pm
by matt (support)
The release version of v5.0.0 has a new device instance method: stateListOrDisplayStateIdChanged().

Background: Plugins can subclass the method getDeviceStateList() to provide dynamic state list definition information. The default implementation provides a static solution by retrieving the device state list definition from the Devices.xml file. Likewise, the method getDeviceDisplayStateId() can be used to dynamically determine which device state should be displayed in the State column of the main device table UI.

Problem: The Indigo Server only calls getDeviceStateList() and getDeviceDisplayStateId() at very specific times, like when a plugin device dialog is dismissed. Plugins that need to refresh a device state list definition or display state ID at other times couldn't.

Solution: Call stateListOrDisplayStateIdChanged() on the device instance you need refreshed. Indigo will then automatically call your plugin's getDeviceStateList() and getDeviceDisplayStateId() methods (or use the base implementation of looking up the list from Devices.xml) and update the Indigo Server (and all clients).

This is particularly useful for plugin updates that need to add new device states to existing device instances created by older versions. In this case, the plugin will need to update the device instances by calling stateListOrDisplayStateIdChanged(). A likely place to do this type of instance level upgrading is inside your plugin's deviceStartComm() method.

Example usage:

Code: Select all
dev = indigo.devices["den fixture"]
dev.stateListOrDisplayStateIdChanged()      # will call getDeviceStateList() and getDeviceDisplayStateId()

Re: API Addition: dev.stateListOrDisplayStateIdChanged() met

PostPosted: Sun Nov 18, 2012 2:10 pm
by bschollnick2
matt (support) wrote:
The release version of v5.0.0 has a new device instance method: stateListOrDisplayStateIdChanged().
Example usage:

Code: Select all
dev = indigo.devices["den fixture"]
dev.stateListOrDisplayStateIdChanged()      # will call getDeviceStateList() and getDeviceDisplayStateId()


Matt,

I have run into a situation with Switchboard, where stateListOrDisplayStateIdChanged () doesn't seem to be updating some devices?

Is there some situation or condition where this might happen? For example, the age of a device?

def deviceStartComm (self, dev):
self.debugLog ("Adding %s to Switchboard Devices List" % dev.name)
dev.stateListOrDisplayStateIdChanged()
....
....

I have not been able to detect any cause or reason for this... But It does seem to be happening....

- Ben

Re: API Addition: dev.stateListOrDisplayStateIdChanged() met

PostPosted: Sun Nov 18, 2012 2:47 pm
by matt (support)
It only works if the plugin that makes the stateListOrDisplayStateIdChanged() call is the same plugin that owns the device (that is, the pluginIds have to match). For devices that match though it should then trigger calls to the plugin's getDeviceStateList() and getDeviceDisplayStateId() methods. Add some debug logging to the start of those -- does it not get called? Any errors in the Event Log? Also check the console for errors. Sometimes they get logged there if they cannot get pushed to the server.

Re: API Addition: dev.stateListOrDisplayStateIdChanged() met

PostPosted: Sun Nov 18, 2012 4:51 pm
by bschollnick2
matt (support) wrote:
It only works if the plugin that makes the stateListOrDisplayStateIdChanged() call is the same plugin that owns the device (that is, the pluginIds have to match). For devices that match though it should then trigger calls to the plugin's getDeviceStateList() and getDeviceDisplayStateId() methods. Add some debug logging to the start of those -- does it not get called? Any errors in the Event Log? Also check the console for errors. Sometimes they get logged there if they cannot get pushed to the server.


The plugin_id for those devices hasn't change to my knowledge. It's the Switchboard plugin after all....

And I just checked, those devices are indeed being added via deviceStartComm.... And no additional errors in the console.log....
Where would I verify that the plugin_id hasn't changed for the devices? And is there anyway to normalize the plugin_id, assuming that is the cause of the issue?

I went through the 6 X10 devices that I have in my production switchboard, and as far as I can tell the plugin_id is identical.... And only one of them is updated.... And that was manually updated, when I went into the device, and saved it...

So, how can we debug this further? I have verified that the proper devices.xml file is in the plugin....

The odd issue is that the field is showing up in the Device Configuration page, but the variable & value is not seen in the device's record....

- Ben

Re: API Addition: dev.stateListOrDisplayStateIdChanged() met

PostPosted: Sun Nov 18, 2012 7:47 pm
by matt (support)
Are you trying to update the pluginProps on the device or the states definition on the device? And to double-check, you did add some debug logging to getDeviceStateList() and getDeviceDisplayStateId() to see if they were being called and they are not?

Re: API Addition: dev.stateListOrDisplayStateIdChanged() met

PostPosted: Mon Nov 19, 2012 4:29 am
by bschollnick2
matt (support) wrote:
And to double-check, you did add some debug logging to getDeviceStateList() and getDeviceDisplayStateId() to see if they were being called and they are not?


Maybe I misunderstanding something here.... I don't have getDeviceStateList or getDeviceDisplayStateId in use in Switchboard for creating custom states... My understanding is that if your not using custom states-----------

Whoa...

Let's reverse here, and make sure my assumptions are correct.... Because, I just thought about this.

1) This dev.stateListOrDisplayStateIdChanged() definitely updates the state information on the device, does it update anything else?
a) If it only updates state information, is there an equivalent for the field information on the device?

I know I started using it, to solve a state issue, but I've been assuming it causes a full refresh of the device....

To answer your other question, I added a field record to the device.... After adding getDeviceStateList and getDeviceDisplayStateId, I seriously thought about the function as something other than a refresh mechanism....

So is there someway to refresh the device's fields without having a user go through manually edit each device, and then save it?

- Ben

Re: API Addition: dev.stateListOrDisplayStateIdChanged() met

PostPosted: Mon Nov 19, 2012 10:59 am
by jay (support)
getDeviceStateList() is called when we build the list of available states for a device. The default implementation just returns what's in the Devices.xml. You can of course override the method if you like.

That method is generally only called when the device is first created. You can call the dev.stateListOrDisplayStateIdChanged() method to cause the state list to be rebuilt - so if you add or remove a state from the Devices.xml file then calling that method will force Indigo to rebuild the list for the device.

Re: API Addition: dev.stateListOrDisplayStateIdChanged() met

PostPosted: Mon Nov 19, 2012 11:28 am
by matt (support)
bschollnick2 wrote:
a) If it only updates state information, is there an equivalent for the field information on the device?

No, but that is because your plugin can update the pluginProps at anytime it wants (on plugin start, when deviceStartComm() is called, etc.) The state definition list (not state values themselves) are different and cannot be updated by the plugin via a push. Instead the client/server pulls the state information from the plugin when it is needed. Therefore, we had to add the stateListOrDisplayStateIdChanged() method to let the server/client know that the state definition has changed and needs to be pulled from the plugin again.

bschollnick2 wrote:
So is there someway to refresh the device's fields without having a user go through manually edit each device, and then save it?

I would do it at plugin startup time or deviceStartComm. Or often times the best approach is to access values via get() instead of the bracket syntax since you can specify a default. So instead of:

Code: Select all
foo = dev.pluginProps["someNewKeyThatMayNotExist"]

which will throw an error if the key doesn't exist, use:

Code: Select all
foo = dev.pluginProps.get("someNewKeyThatMayNotExist", 0 or some other default value)

Re: API Addition: dev.stateListOrDisplayStateIdChanged() met

PostPosted: Mon Nov 19, 2012 11:33 am
by jay (support)
Sorry, I totally wasn't making the fields=properties leap with you...

Re: API Addition: dev.stateListOrDisplayStateIdChanged() met

PostPosted: Mon Nov 19, 2012 11:44 am
by bschollnick2
jay (support) wrote:
Sorry, I totally wasn't making the fields=properties leap with you...


Jay,

I think we're slightly misunderstanding each other here....?

I added this to the devices.xml.... And restarted the plugin...

<Field type="checkbox" id="IgnoreHeartbeats">
<Label>Turn Off Heartbeat Warnings</Label>
<Description></Description>
</Field>

This field, shows up in the devices configuration screen... But when I look at the device in python:

>>> print indigo.devices[1646055197]
address : 105
buttonGroupCount : 0
description :
deviceTypeId : X10Device
enabled : True
errorState :
folderId : 833197598
globalProps : MetaProps : (dict)
com.schollnick.indigoplugin.switchboard : (dict)
DeviceCode : 2 (string)
HouseCode : J (string)
IgnoreDevice : false (bool)
IgnoreOrphan : false (bool)
OverrideHeartbeatTimeOut : true (bool)
X10HeartBeatTimeOut : 200 (string)
X10Security : 105 (string)
address : 105 (string)
simpleSeparator1 : (string)
simpleSeparator2 : (string)
simpleSeparator3 : (string)
id : 1646055197
lastChanged : 2000-01-01 00:00:00
model : X10 Monitored Device
name : test X10
pluginId : com.schollnick.indigoplugin.switchboard
pluginProps : emptyDict : (dict)
protocol : Plugin
remoteDisplay : True
states : States : (dict)
Display_onState : false (bool)
Last_Triggered : (string)
Last_Updated : (string)
Last_X10Command : (string)
Last_X10HeartBeat : (string)
onState : false (bool)
supportsAllLightsOnOff : False
supportsAllOff : False
supportsStatusRequest : False
version : 0

Note the lack of IgnoreHeartsbeats.....?

Now a newly created device, will have that field in GlobalProps.... And if I have the users, enter the Device Record, edit it, Save it, and Close the device record the field will be there as well.

(Same device after reserving)

>>> >>> print indigo.devices[1646055197]
address : 105
buttonGroupCount : 0
description :
deviceTypeId : X10Device
enabled : True
errorState :
folderId : 833197598
globalProps : MetaProps : (dict)
com.schollnick.indigoplugin.switchboard : (dict)
DeviceCode : 2 (string)
HouseCode : J (string)
IgnoreDevice : false (bool)
IgnoreHeartbeats : false (bool)
IgnoreOrphan : false (bool)
OverrideHeartbeatTimeOut : true (bool)
X10HeartBeatTimeOut : 200 (string)
X10Security : 105 (string)
address : 105 (string)
simpleSeparator1 : (string)
simpleSeparator2 : (string)
simpleSeparator3 : (string)
id : 1646055197
lastChanged : 2000-01-01 00:00:00
model : X10 Monitored Device
name : test X10
pluginId : com.schollnick.indigoplugin.switchboard
pluginProps : emptyDict : (dict)
protocol : Plugin
remoteDisplay : True
states : States : (dict)
Display_onState : false (bool)
Last_Triggered : (string)
Last_Updated : (string)
Last_X10Command : (string)
Last_X10HeartBeat : (string)
onState : false (bool)
supportsAllLightsOnOff : False
supportsAllOff : False
supportsStatusRequest : False
version : 0

I had made the assumption that dev.stateListOrDisplayStateIdChanged() would force a refresh on that as well....

But Switchboard can have 10-15 devices... And I can't rely on the users forcing a refresh of all those devices....
Is there *any* way that I can programatically do this? Or request some API manner to do this?

- Ben

Re: API Addition: dev.stateListOrDisplayStateIdChanged() met

PostPosted: Mon Nov 19, 2012 11:50 am
by jay (support)
Check Matt's post. You can add properties directly any time you want. Fields in the config ui are just properties.

Re: API Addition: dev.stateListOrDisplayStateIdChanged() met

PostPosted: Mon Nov 19, 2012 8:39 pm
by bschollnick2
jay (support) wrote:
Check Matt's post. You can add properties directly any time you want. Fields in the config ui are just properties.


Okay, just to clarify, this is in the GlobalProps category, since these are custom devices.

I see the example on that Matt gives, but those changes aren't propagated to the server, and replacePluginPropsOnServer is throwing an error....

Switchboard IgnoreHeartbeats
Switchboard Error exception in deviceStartComm(Rec Room - Backyard Door): Python argument types in
Dict.__setitem__(Dict, type, bool)

Setting the property to True / False / 0 / 1 doesn't seem to work....
Code: Select all
   def   verify_device_properties ( self, dev, propertyname, boolean = False) :
      if indigo.devices[dev].globalProps[plugin_id].has_key (propertyname):
         return
      else:
         #indigo.devices[dev].globalProps[plugin_id][propertyname] = None
         indigo.server.log ("%s" % propertyname)
         newProps = dev.globalProps[plugin_id]
         if boolean:
            newProps[property] = True
         else:
            newProps[property] = ""
         
         dev.replacePluginPropsOnServer(newProps)

      if dev.deviceTypeId == "X10Device":
         self.verify_device_properties (dev, "IgnoreDevice", boolean = True)
         self.verify_device_properties (dev, "IgnoreOrphan", boolean = True)
         self.verify_device_properties (dev, "IgnoreHeartbeats", boolean = True)
         self.verify_device_properties (dev, "OverrideHeartbeatTimeout", boolean = True)
         self.verify_device_properties (dev, "X10HeartBeatTimeOut")
         # self.X10List [ <X10 Sensor ID> ] = X10 Monitored Device ID
         #
         #   Get Security ID from the device
         #
         X10Security = str(indigo.devices[dev].globalProps[plugin_id]["X10Security"]).upper()



Should work, but as I mentioned, the boolean code doesn't seem to be working quite right...

Using get in this case, doesn't solve the problem. It doesn't write the field to the globalProps, it masks the problem, due to the default value... Since the value disappears once the server or plugin is reset...

- Benjamin

Re: API Addition: dev.stateListOrDisplayStateIdChanged() met

PostPosted: Mon Nov 19, 2012 8:48 pm
by jay (support)
pluginProps are where properties defined in the config ui show up. globalProps are for adding arbitrary properties to devices not owned by your plugin.

Re: API Addition: dev.stateListOrDisplayStateIdChanged() met

PostPosted: Mon Nov 19, 2012 8:50 pm
by matt (support)
pluginProps are both where you should put your properties and where the ConfigUI properties are automatically inserted.

globalProps is the global dictionary of all pluginProps. It should not be modified. Plugins should modify their pluginProps instead via replacePluginPropsOnServer(). See this explanation for the details including examples of using replacePluginPropsOnServer().

Re: API Addition: dev.stateListOrDisplayStateIdChanged() met

PostPosted: Mon Nov 19, 2012 8:53 pm
by matt (support)
And if replacePluginPropsOnServer() is throwing an error, then what is the error (the above has an exception log, but it looks incomplete)? And is the exact code above what causes the error?