Inserting static code snippets into python action scripts

Posted on
Thu Mar 28, 2019 8:41 am
russg offline
Posts: 46
Joined: Oct 02, 2014

Inserting static code snippets into python action scripts

Hi Folks,
Not sure this post is in the right location....

I have a large number of python action scripts. Each script has a static header (reading indigo variables into the script) and a static footer (writing indigo variables back out). Instead of duplicating these blocks multiple times, I'd like to save them to a file, and then import them into the python action scripts as needed. Questions:

1. How can I import these snippets inside a python script, and
2. what directory would the header/footer files have to reside in? (I'm assuming there is no repository inside the Indigo UI for that).

thanks in advance for any help!
-russg

Posted on
Thu Mar 28, 2019 9:23 am
jay (support) offline
Site Admin
User avatar
Posts: 18199
Joined: Mar 19, 2008
Location: Austin, Texas

Re: Inserting static code snippets into python action script

So, Python doesn't really do includes like other languages where the source is just copy/pasted before the script is executed or the file is compiled. Rather, Python allows you to import modules and/or the things defined inside the module. At import time, the module is "run" or "executed" and depending on how it's imported stuff inside the module becomes available to the importer (Here's a reasonable discussion of Python imports). This is significantly more flexible than just a copy/paste of code which is what you get with most languages.

For your specific requirement, there are several approaches. The one that most closely matches what you're trying to do is probably by making a shared module that looks something like this:

Code: Select all
try:
    import indigo
except ImportError:
    print "The indigo module can only be used by scripts started from within Indigo"
    raise ImportError

# Note that I'm getting the full variable object here rather than just the value (which I could also do)
# which makes a difference in how I use them in the importing script (see below)
some_var_1 = indigo.variables[12345] # get first var here
some_var_2 = indigo.variables[67890] # get second var here
some_var_3 = indigo.variables[98765] # get third var here
# Continue to get whatever vars you want


Store that in a file in the above linked location as, say, standard_vars.py. Then, in the script that needs them:

Code: Select all
from standard_vars import some_var_1, some_var_2, some_var_3
# Use them as you would normal Indigo variables
the_value_1 = some_var_1.value
indigo.variable.updateValue(some_var_1, value="New value")


So you use the imported Indigo variable objects in the script that imports them.

Ultimately, it really depends on what you want to accomplish: if it's just keeping the IDs of the variables in a single place, then this approach is OK (and will be better in Indigo 7.3 as I implied in reply to your other post). I sorta get the feeling that that's only part of the story though, so if you could describe the scenario more fully (or post the code as it stands now for a couple of the scripts) there may be an even better way to handle it. For instance, if you just want to start with a collection of values which you would use then update them all, you could use one handler to return a list/dict of values and another handler which would take a list/dict of values and do the update.

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Posted on
Thu Mar 28, 2019 10:16 am
russg offline
Posts: 46
Joined: Oct 02, 2014

Re: Inserting static code snippets into python action script

Hi Jay,
You hit it on the head at the end, when you said
"start with a collection of values which you would use then update them all, you could use one handler to return a list/dict of values and another handler which would take a list/dict of values and do the update"

Are there any good examples of how to do just that?

Thanks for your patience. You must feel like you're trying to explain linear algebra to a rabbit.
...russg

Posted on
Thu Mar 28, 2019 2:31 pm
jay (support) offline
Site Admin
User avatar
Posts: 18199
Joined: Mar 19, 2008
Location: Austin, Texas

Re: Inserting static code snippets into python action script

For instance, you could create 2 shared methods:

Code: Select all
try:
    import indigo
except ImportError:
    print "The indigo module can only be used by scripts started from within Indigo"
    raise ImportError

# Add as many vars to this map as you want to return, and create
# your own keys that you'll use in the scripts that import this
VAR_MAP = {
    "subject": 1234567890, # ID of the subject var
    "body": 9876543210, # ID of the body var (continue to add here)
}

def get_var_data(vars_to_get=None):
    return_dict = dict()
    unfound_dict = dict()
    if not vars_to_get:
        # If you don't pass in a list of keys, then just get
        # everything
        vars_to_get = VAR_MAP.keys()
    for key in vars_to_get:
        try:
            return_dict[key] = indigo.variables[VAR_MAP[key]].value
        except:
            # If we can't get the variable, make the value in
            # the return dict None. That way the script may
            # be able to continue doing stuff if it has enough
            # of the data and can do a reasonable default value
            # for the missing one.
            return_dict[key] = None
    # Ok, now we have the return_dict, we just return it
    return return_dict

def update_var_data(update_dict):
    for name, new_value in update_dict.iteritems():
        if name in VAR_MAP:
            indigo.variable.updateValue(VAR_MAP[name], value=str(new_value))


Then you can call those methods in your embedded scripts, or test them in a scripting shell:

Code: Select all
Python 2.7.10 (default, Oct  6 2017, 22:29:07)
[GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.31)]
Connected to Indigo Server v7.3.0, api v2.1 (localhost:1176)
>>> import standard_vars
>>> values = standard_vars.get_var_data()
>>> print(values)
{'body': u'Original body', 'subject': u'Original Subject'}
>>> values["subject"] = "Here's a new subject"
>>> values["body"] = "Here's a new value"
>>> print(values)
{'body': "Here's a new value", 'subject': "Here's a new subject"}
>>> standard_vars.update_var_data(values)
>>>
>>> vars_to_get = ["subject", "body", "something else"]
>>> values = standard_vars.get_var_data(vars_to_get)
>>> print(values)
{'body': u"Here's a new value", 'something else': None, 'subject': u"Here's a new subject"}
>>>


There are a variety of other possible solutions as well - this is just the first that came to mind.

Actually, you might want to just ask yourself if it would be even easier to implement each of your embedded scripts in a single shared module, then what you call from your embedded script would be something super simple like:

Code: Select all
my_shared_functions.send_standard_notification()


and all the work is done in the my_shared_functions.py file, completely encapsulated. Might speak more to your OOP design preferences... :lol:

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Posted on
Thu Mar 28, 2019 10:07 pm
russg offline
Posts: 46
Joined: Oct 02, 2014

Re: Inserting static code snippets into python action script

Hi Jay,
OK, that makes sense, I'll play with that for a bit. Thanks again for your patience, I have a long way to go learning Python!
-russg

Posted on
Fri Mar 29, 2019 10:34 pm
russg offline
Posts: 46
Joined: Oct 02, 2014

Re: Inserting static code snippets into python action script

Hey Jay,
This is working great - thank you! I'm running into one issue, though, and I'm hoping you can help:

My variable map has 10 entries. When I update all 10, everything is fine. But, when I skip updating a variable, it fails to update all of the variables after it. For example, if I name my shared file getIndigoVariables, inside it would be:

Code: Select all
VAR_MAP = {
   "var1": 1523218536, # ID of the subject var
   "var2": 1625813152,
   "var3": 672424110,
}


Then, inside my python script would be:

Code: Select all
  import getIndigoVariables
  values = getIndigoVariables.get_var_data()
  values["var1"] = "something new"
  values["var3"] = "something new"
  getIndigoVariables.update_var_data(values)


Is there a way to do this so that you can use get_var_data to load all of the variables, update only a few of them, and then use update_var_data to update only the variables you change? As is, the variable updates stop after var2 (which is not updated) and var3 does not get updated.

thanks,
-russg

Posted on
Mon Apr 01, 2019 5:11 pm
jay (support) offline
Site Admin
User avatar
Posts: 18199
Joined: Mar 19, 2008
Location: Austin, Texas

Re: Inserting static code snippets into python action script

Are you sure? I just did you test and it worked fine for me. The update method clearly cycles through everything in the dict passed into it and does the update. Are your IDs correct?

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Posted on
Mon Apr 01, 2019 10:24 pm
russg offline
Posts: 46
Joined: Oct 02, 2014

Re: Inserting static code snippets into python action script

Hey Jay,
Thanks for your patience on this. I have something weird going on. I just create a new test script, and it works just as you described. My old script, however, still acts weird when I am missing one variable in the dict. I'm going to have to spend some more time figuring out what I'm doing wrong.

So, now I am wondering why I couldn't just loop through and enumerate all of the variables for the definition of VAR_MAP? Then, when I call get_var_data, I have all of the Indigo variables available.. I could update the ones I need, and then update_var_data would update just the ones I changed. As is, I'm bound to need a new variable, and I would have to go back and update the python script and all of my Indigo scripts. And that's begging for a typo to come back and haunt me. Outside of memory is there any reason not to? Would updating all of the Indigo variables at once create contention of some some? I realize that a parallel process could update something between the read and write-back of my scripts, but the odds of that seem immeasurably small. What am I missing?

And after scratching my head for a bit, I have to ask - any idea *how* to iterate through all of the Indigo variables to accomplish that?

Seriously - thanks for your patience - beginners must drive you nuts.
-russg

Posted on
Tue Apr 02, 2019 12:06 am
kw123 offline
User avatar
Posts: 8333
Joined: May 12, 2013
Location: Dallas, TX

Re: Inserting static code snippets into python action script

I believe you want something like this

for var in indigo.variables:
varname = var.name
varvalue = var.value

You should put the names, values into your dict in the loop.


But I would not do that. It naturally depends on how often you do that and how many variables you have.

Consider
varvalue = indigo.variables[1234].value
In your script should not be that difficult
Why do you want to copy everything.?

Karl

Ps I am typing this on my phone from memory. There might be some typos.




Sent from my iPhone using Tapatalk

Posted on
Tue Apr 02, 2019 8:52 am
jay (support) offline
Site Admin
User avatar
Posts: 18199
Joined: Mar 19, 2008
Location: Austin, Texas

Re: Inserting static code snippets into python action script

russg wrote:
now I am wondering why I couldn't just loop through and enumerate all of the variables for the definition of VAR_MAP?


Because that would be pretty inefficient. Scripts run in a separate process, so getting all of your variables requires a network socket roundtrip to the server. And your script would be doing work that's not needed. Not that it's a big deal, its just really unnecessary.

It really feels like you're taking the long way around using/updating variables. It's actually quite simple:

Code: Select all
my_var = indigo.variables[IDOFVAR] # get the variable object
my_var.value # use the variable value
indigo.variable.updateValue(my_var, value="some new value") # update the value


Your original request was a way to share code that used basically the same set of variables - so that's the solution I presented. In general however, it's overkill, unless you have a lot of scripts that are using basically the same set of variables. If not, then you really should just directly use the API calls.

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Posted on
Tue Apr 02, 2019 11:02 pm
russg offline
Posts: 46
Joined: Oct 02, 2014

Re: Inserting static code snippets into python action script

Ah, didn't realize the network socket issue - thanks. More importantly, I think you are right - I'm running down a rabbit hole.

I actually use the script a lot. We have a spread out house in the country, and some areas are hard to access for me (spine injury), so I have a more extensive notification/alarming scheme than most. It's a combo of logging, email, text messages, visual/lights & audio. Events typically fall into Sev 0 (info), 1 (note), 2 (warning) and 3 (danger). Each of those, in turn, trigger various combinations of notifications depending on the person, location, time of day, etc.

Because of that, I isolate each notification method into it's own python script. I chose to use Indigo variables both for configuration and to trigger events up the alarm tree. Each potential device can/does configure unique log/email/text/visual/audio responses in Indigo variables. That leaves me with around 15 variables that get uniquely configured in each event.

Outside of the normal light switches and outlets I also have 16 water sensors, 3 gas leak sensors, ?? motion sensors, security can events, security system events, 20+ temp sensors, 2 solar systems, an off-grid battery system, a geothermal, septic, well systems, and more on the way. An Indigo target rich environment. :).

Where I got bit was having the chunk of python code that configures the alarm variables in each device event and having people (ok, the wife) request changes. I found myself going back and tweaking all of these each time something changed, and eventually missing something. Thus the original goal of having a single piece of python which pulled all the variable definitions in, and a single piece that updated them all.

I'm sure there was a better way to architect all of this, but I'm a python newbie, a good decade out of coding and while Indigo is pretty straight forward at the simplest level, it also has an incredibly flexible and (to me) complex architecture. Being an engineer, I find that incredibly alluring (a bad habit, I know).

All of that said, I owe you an apology for dragging you down the rabbit hole with me. Thanks for getting me out of this hole, and for explaining the how's and why's!

-russg

Page 1 of 1

Who is online

Users browsing this forum: No registered users and 2 guests