[ANSWERED] Add Custom Device State Dynamically

Posted on
Tue Feb 09, 2021 5:38 am
autolog offline
Posts: 3988
Joined: Sep 10, 2013
Location: West Sussex, UK [GMT aka UTC]

Re: Add Custom Device State Dynamically

matt (support) wrote:
... What you want to do is something like this:
Code: Select all
   def getDeviceStateList(self, dev):
      stateList = indigo.PluginBase.getDeviceStateList(self, dev)
      if stateList is not None:
         # Add any dynamic states onto the device based on the node's characteristics.
         if dev-supports-something:
            someNumState = self.getDeviceStateDictForNumberType(u"someNumState", u"Some Level Label", u"Some Level Label")
            someStringState = self.getDeviceStateDictForStringType(u"someStringState", u"Some Level Label", u"Some Level Label")
            someOnOffBoolState = self.getDeviceStateDictForBoolOnOffType(u"someOnOffBoolState", u"Some Level Label", u"Some Level Label")
            someYesNoBoolState = self.getDeviceStateDictForBoolYesNoType(u"someYesNoBoolState", u"Some Level Label", u"Some Level Label")
            someOneZeroBoolState = self.getDeviceStateDictForBoolOneZeroType(u"someOneZeroBoolState", u"Some Level Label", u"Some Level Label")
            someTrueFalseBoolState = self.getDeviceStateDictForBoolTrueFalseType(u"someTrueFalseBoolState", u"Some Level Label", u"Some Level Label")
            stateList.append(someNumState)
            stateList.append(someStringState)
            stateList.append(someOnOffBoolState)
            stateList.append(someYesNoBoolState)
            stateList.append(someOneZeroBoolState)
            stateList.append(someTrueFalseBoolState)
      return stateList



With Indigo 7.5, I am looking fora self.getDeviceStateDictForFloatType but trying that doesn't seem to work?

Can it be added or is there another way to force this? :)

Posted on
Tue Feb 09, 2021 3:54 pm
matt (support) offline
Site Admin
User avatar
Posts: 21411
Joined: Jan 27, 2003
Location: Texas

Re: [ANSWERED] Add Custom Device State Dynamically

Try getDeviceStateDictForRealType(). :twisted:

Image

Posted on
Tue Dec 28, 2021 6:06 pm
LSpad offline
Posts: 30
Joined: Oct 01, 2018

Re: [ANSWERED] Add Custom Device State Dynamically

Hi there,

Apologies for revisiting an old question. My use case is actually a fair bit simpler, as I’m not looking for different state lists by device, just a single list of states for all devices defined on startup.

Is it possible to define a dynamic list of states in the devices.xml? I’ve tried setting it out using the same construction as in the events.xml but I keep throwing an error that lists in devices.xml must have at least one state (even if I pre populate the dynamic list construction method with a tuple for unknown state). In my devices.xml I’m currently doing something like:
Code: Select all
<State id="mode">
        <ValueType>
                <List class="self" filter="" method="myModeListGenerator"/>
        </ValueType>
        <TriggerLabel>Operation Mode Changed</TriggerLabel>
        <TriggerLabelPrefix>Mode Changed to</TriggerLabelPrefix>
        <ControlPageLabel>Current Mode</ControlPageLabel>
        <ControlPageLabelPrefix>Mode is</ControlPageLabelPrefix>
</State>

Posted on
Wed Dec 29, 2021 6:51 pm
matt (support) offline
Site Admin
User avatar
Posts: 21411
Joined: Jan 27, 2003
Location: Texas

Re: [ANSWERED] Add Custom Device State Dynamically

Unfortunately, no. For the XML based <State> it is 100% static (no callbacks), so the only way to dynamically alter the list is to override the getDeviceStateList() method as described above.

Image

Posted on
Mon Jan 03, 2022 3:45 pm
LSpad offline
Posts: 30
Joined: Oct 01, 2018

Re: [ANSWERED] Add Custom Device State Dynamically

Many thanks for getting back Jay,

I'm struggling a little to crack this one! I have a plugin I'm developing which creates a bunch of AV renderers representing audio or visual products on a network. Each renderer has a unique set of sources that it can play, combining local sources that are unique to each renderer with shared distributed sources.

In my devices.xml I have created a state called source which is a string list:
Code: Select all
<States>
         <State  id="source">
            <ValueType>
               <List>
                  <Option value="Unknown">Unknown</Option>
                  <Option value="Standby">Standby</Option>
               </List>
            </ValueType>
            <TriggerLabel>Source Changed</TriggerLabel>
            <TriggerLabelPrefix>Source is</TriggerLabelPrefix>
            <ControlPageLabel>Current Source</ControlPageLabel>
            <ControlPageLabelPrefix>Source is</ControlPageLabelPrefix>
</State>


When my plugin starts up it interrogates the central gateway for the network which provides an .xml config file containing details of all network nodes, including their available sources. I'm storing that data for each device and I'd like to be able to dynamically alter the state list for each device so that the state options only include the available sources to the device. I've overwritten

Code: Select all
def getDeviceStateList(self, dev):
        stateList = indigo.PluginBase.getDeviceStateList(self, dev)

        if stateList is not None:
            if dev.deviceTypeId in self.devicesTypeDict and dev.deviceTypeId == u"AVrenderer":
                # Add dynamic states onto statelist for devices of type AV renderer
                sources = dev.ownerProps['sources']
                for sourceName in sources:

                    src = (u"source", sources[sourceName]['source'], sourceName)
                    stateList.append(src)

        return stateList


so I'm trying to append something like the following to that state list:
(u'source', u'A.TAPE/A.MEM', u'A.TAPE')
(u'source', u'CD', u'CD')
(u'source', u'PHONO/N.RADIO', u'PHONO')
(u'source', u'RADIO', u'RADIO')
(u'source', u'A.TAPE2/N.MUSIC', u'iTUNES')

This doesn't work and I can see I'm mangling the stateliest - how do I add the elements to the list correctly?

With best wishes
Luke

Posted on
Mon Jan 03, 2022 7:17 pm
FlyingDiver offline
User avatar
Posts: 7189
Joined: Jun 07, 2014
Location: Southwest Florida, USA

Re: [ANSWERED] Add Custom Device State Dynamically

Looks like you're adding a tuple to the state list, not a dict.

Here's the code from the Ecobee plugin:

Code: Select all
   def getDeviceStateList(self, dev):
       
        stateList = indigo.PluginBase.getDeviceStateList(self, dev)
        device_type = dev.pluginProps.get("device_type", None)
                               
        if device_type in ['athenaSmart', 'corSmart', 'apolloSmart', 'vulcanSmart']:

            stateList.append({  "Disabled"     : False,
                                "Key"          : "hvacMode",
                                "StateLabel"   : "HVAC Mode",   
                                "TriggerLabel" : "HVAC Mode",   
                                "Type"         : 150 })
            stateList.append({  "Disabled"     : False,
                                "Key"          : "latestEventType",
                                "StateLabel"   : "Last Event",   
                                "TriggerLabel" : "Last Event",   
                                "Type"         : 150 })
            stateList.append({  "Disabled"     : False,
                                "Key"          : "device_type",
                                "StateLabel"   : "Model",   
                                "TriggerLabel" : "Model",   
                                "Type"         : 150 })
            stateList.append({  "Disabled"     : False,
                                "Key"          : "climate",     
                                "StateLabel"   : "Climate",
                                "TriggerLabel" : "Climate",
                                "Type"         : 150 })
            stateList.append({  "Disabled"     : False,
                                "Key"          : "equipmentStatus",     
                                "StateLabel"   : "Status",
                                "TriggerLabel" : "Status",
                                "Type"         : 150 })
            stateList.append({  "Disabled"     : False,
                                "Key"          : "occupied",
                                "StateLabel"   : "Occupied (yes or no)",   
                                "TriggerLabel" : "Occupied",   
                                "Type"         : 52 })
            stateList.append({  "Disabled"     : False,
                                "Key"          : "autoAway",
                                "StateLabel"   : "Auto-Away (yes or no)",   
                                "TriggerLabel" : "Auto-Away",   
                                "Type"         : 52 })
            stateList.append({  "Disabled"     : False,
                                "Key"          : "autoHome",
                                "StateLabel"   : "Auto-Home (yes or no)",   
                                "TriggerLabel" : "Auto-Home",   
                                "Type"         : 52 })
            stateList.append({  "Disabled"     : False,
                                "Key"          : "fanMinOnTime",
                                "StateLabel"   : "Minimum fan time",   
                                "TriggerLabel" : "Minimum fan time",   
                                "Type"         : 100 })
       
        elif device_type in ['nikeSmart']:

            stateList.append({  "Disabled"     : False,
                                "Key"          : "hvacMode",
                                "StateLabel"   : "HVAC Mode",   
                                "TriggerLabel" : "HVAC Mode",   
                                "Type"         : 150 })
            stateList.append({  "Disabled"     : False,
                                "Key"          : "latestEventType",
                                "StateLabel"   : "Last Event",   
                                "TriggerLabel" : "Last Event",   
                                "Type"         : 150 })
            stateList.append({  "Disabled"     : False,
                                "Key"          : "device_type",
                                "StateLabel"   : "Model",   
                                "TriggerLabel" : "Model",   
                                "Type"         : 150 })
            stateList.append({  "Disabled"     : False,
                                "Key"          : "climate",     
                                "StateLabel"   : "Climate",
                                "TriggerLabel" : "Climate",
                                "Type"         : 150 })
            stateList.append({  "Disabled"     : False,
                                "Key"          : "equipmentStatus",     
                                "StateLabel"   : "Status",
                                "TriggerLabel" : "Status",
                                "Type"         : 150 })
            stateList.append({  "Disabled"     : False,
                                "Key"          : "autoAway",
                                "StateLabel"   : "Auto-Away (yes or no)",   
                                "TriggerLabel" : "Auto-Away",   
                                "Type"         : 52 })
            stateList.append({  "Disabled"     : False,
                                "Key"          : "autoHome",
                                "StateLabel"   : "Auto-Home (yes or no)",   
                                "TriggerLabel" : "Auto-Home",   
                                "Type"         : 52 })
            stateList.append({  "Disabled"     : False,
                                "Key"          : "fanMinOnTime",
                                "StateLabel"   : "Minimum fan time",   
                                "TriggerLabel" : "Minimum fan time",   
                                "Type"         : 100 })
       

        elif device_type in ['idtSmart', 'siSmart']:

            stateList.append({  "Disabled"     : False,
                                "Key"          : "hvacMode",
                                "StateLabel"   : "HVAC Mode",   
                                "TriggerLabel" : "HVAC Mode",   
                                "Type"         : 150 })
            stateList.append({  "Disabled"     : False,
                                "Key"          : "latestEventType",
                                "StateLabel"   : "Last Event",   
                                "TriggerLabel" : "Last Event",   
                                "Type"         : 150 })
            stateList.append({  "Disabled"     : False,
                                "Key"          : "device_type",
                                "StateLabel"   : "Model",   
                                "TriggerLabel" : "Model",   
                                "Type"         : 150 })
            stateList.append({  "Disabled"     : False,
                                "Key"          : "climate",     
                                "StateLabel"   : "Climate",
                                "TriggerLabel" : "Climate",
                                "Type"         : 150 })
            stateList.append({  "Disabled"     : False,
                                "Key"          : "equipmentStatus",     
                                "StateLabel"   : "Status",
                                "TriggerLabel" : "Status",
                                "Type"         : 150 })
            stateList.append({  "Disabled"     : False,
                                "Key"          : "fanMinOnTime",
                                "StateLabel"   : "Minimum fan time",   
                                "TriggerLabel" : "Minimum fan time",   
                                "Type"         : 100 })
       
        return stateList
           

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

Posted on
Tue Jan 04, 2022 8:16 am
LSpad offline
Posts: 30
Joined: Oct 01, 2018

Re: [ANSWERED] Add Custom Device State Dynamically

Many thanks for this Joe,

That's really helpful - however, I'm not sure I understand correctly. What I'm trying to do is add a tuple to the existing list of possible states for 'source'. As far as I can tell, essentially those options are represented as statename.option in the stateList so If I try the following:

Code: Select all
    def getDeviceStateList(self, dev):
        stateList = indigo.PluginBase.getDeviceStateList(self, dev)

        if stateList is not None:
            if dev.deviceTypeId in self.devicesTypeDict and dev.deviceTypeId == u"AVrenderer":
                # Add dynamic states onto stateList for devices of type AV renderer
                sources = dev.ownerProps['sources']
                for source_name in sources:
                    # src = {u"source", sources[source_name]['source'], source_name}
                    stateList.append({"Disabled": False,
                                      "Key": "source." + source_name,
                                      "StateLabel": "Source is " + source_name,
                                      "TriggerLabel": "Source is " + source_name,
                                      "Type": 50})
        return stateList


It essentially produces the options I want in the triggers list, but I raise the error
Code: Select all
Error (client)                  device "BeoMaster 7000" state key source.A.TAPE not defined


Perhaps I'm taking the wrong approach editing the stateList - I'm not trying to add a new state, but rather add a tuple to an existing the options for an existing state? Apologies for lots of silly questions but there is little documentation on this as far as I can see?

Posted on
Tue Jan 04, 2022 9:18 am
FlyingDiver offline
User avatar
Posts: 7189
Joined: Jun 07, 2014
Location: Southwest Florida, USA

Re: [ANSWERED] Add Custom Device State Dynamically

When do you get that error? Do you have debug logging that shows what method you're in?

Are you calling
Code: Select all
device.stateListOrDisplayStateIdChanged()
at an appropriate time? Such as in deviceStartComm()?

I've never tried modifying the state value list on the fly, so I'm not sure this actually works for your use case.

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

Posted on
Tue Jan 04, 2022 2:57 pm
LSpad offline
Posts: 30
Joined: Oct 01, 2018

Re: [ANSWERED] Add Custom Device State Dynamically

Okay - cracked it!

The missing element was setting the StateKey (which I guessed by blind luck from the error message)

Code: Select all
    def getDeviceStateList(self, dev):
        stateList = indigo.PluginBase.getDeviceStateList(self, dev)

        if stateList is not None:
            if dev.deviceTypeId in self.devicesTypeDict and dev.deviceTypeId == u"AVrenderer":
                # Add dynamic states onto stateList for devices of type AV renderer
                try:
                    sources = dev.ownerProps['sources']
                    for source_name in sources:
                        stateList.append(
                            {
                                "Disabled": False,
                                "Key": "source." + source_name,
                                "StateKey": sources[source_name]['source'],
                                "StateLabel": "Source is " + source_name,
                                "TriggerLabel": "Source is " + source_name,
                                "Type": 50
                            }
                        )
                except KeyError:
                    indigo.server.log("Device " + dev.name + " does not have state key 'sources'\n")
        return stateList

    def deviceStartComm(self, dev):
        dev.stateListOrDisplayStateIdChanged()

Who is online

Users browsing this forum: No registered users and 1 guest