Using variables as values inside a string

Posted on
Sun Jan 06, 2019 10:51 am
indigobo offline
User avatar
Posts: 18
Joined: Jan 04, 2019
Location: Raleigh, NC

Using variables as values inside a string

Greetings All,

I’m reaching out to get help with my first script. I have spent days trying to put this together and have had some degree of success/luck but I've become hopelessly stuck! Any help would be greatly appreciated!

What I am trying to put together is a script that does the following:

1. Set computer volume (not iTunes volume) to value of a variable.
2. If iTunes is running pause it.
3. Set computer volume to a particular value of a variable.
4. Pause iTunes if running.
5. Make an announcement of that device using it’s device name each time that device is opened.
6. Wait for the announcement to complete.
7. Return computer volume back to the value of a variable.

I have been successful with steps 1-5, but can't figure out how to have the script wait for the announcement to finish before returning the volume back to the a value that is assigned to a variable (step 7).

Please help!!

currentVol = indigo.variables[747606079].value # current volume of Mac
currentVol = str(currentVol)
previousVol = indigo.variables[181323221].value # previous volume of Mac
previousVol = str(previousVol)

indigo.variable.updateValue(181323221, currVol) # replace value of previousVolume with current volume.

itunesId = "com.perceptiveautomation.indigoplugin.itunes"
itunesPlugin = indigo.server.getPlugin(itunesId)
if itunesPlugin.isEnabled():
speakValue = indigo.variables[102235652].value # here I'd like for the speaking to finish before moving to next step
currentVolume = indigo.variables[747606079].value

from subprocess import call # change of Mac volume
call(["osascript -e 'set volume output volume 55'"], shell=True) # here i'd like to replace the "55" with the value of a variable
itunesPlugin.executeAction("pauseAndSay", deviceId=180651512, props={'message':speakValue})

prevVol = indigo.variables[181323221].value
prevVol = str(prevVol)
indigo.variable.updateValue (747606079, prevVol)

Posted on
Sun Jan 06, 2019 8:57 pm
jay (support) offline
Site Admin
User avatar
Posts: 15856
Joined: Mar 19, 2008
Location: Austin, Texas

Re: Using variables as values inside a string

I have some questions:

  • Steps 1 & 2 seem to be identical to steps 3 & 4 - am I missing something?
  • I don't understand what you're trying to do with step #5 - in your script, all that's being spoken is the value of a variable so can you confirm that's all you want?
  • The pauseAndSpeak iTunes action will pause iTunes, speak the message, and when it's done it'll start playing again (if it was playing before). All in the one command. Aside from setting the volume, that's what you want right?
  • If, however, you want to pause iTunes before you set the volume, speak the message at the new volume, set the volume back, then restart iTunes, then you can't use that action (which isn't really a problem, I just need to know if that's right).
  • Are you using the current and previous Indigo variables for anything else? I ask because if they're only used for this script then they are unnecessary really.

The hardest part of writing a script is clearly defining exactly what you want to do.

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Posted on
Sun Jan 06, 2019 9:40 pm
indigobo offline
User avatar
Posts: 18
Joined: Jan 04, 2019
Location: Raleigh, NC

Re: Using variables as values inside a string

Hi Jay,

Thanks for your reply! It's sometimes lonely learning Python :shock:

I re-read what I wrote, and I got confused myself, so I'll try again :D .

Step 1 - My Mac's computer volume is always set to the max of 100. This way I can have the full range of volume control when playing iTunes which uses a seperate volume control the computer's.

I have announcements that are made each time someone opens up a door or window, but since I don't want the announcement made at max volume, I want to lower computer's volume, speak the announcement, and then return the computer's volume back to 100.

Here's a revised list... I hope it's more understandable.

When someone opens a door or window:

1. Set computer volume (not iTunes volume) to value of a variable (the code below does make this happen, but I'd like to use the value of a variable rather than :arrow: 55)

from subprocess import call
call(["osascript -e 'set volume output volume :arrow: 55'"], shell=True)

2. Pause iTunes if running.

3. Make an announcement that a door/window has opened using its devices name.

4. Wait for the announcement to complete.

5. Return computer's volume back to 100.

The two problems that I have are that I can't figure out the correct syntax to insert the value of a variable in place of the "55" or how to make the script wait until the announcement is finished before returning the Mac's volume back to its original setting of 100.

Thanks again for you help!

Posted on
Mon Jan 07, 2019 12:16 pm
jay (support) offline
Site Admin
User avatar
Posts: 15856
Joined: Mar 19, 2008
Location: Austin, Texas

Re: Using variables as values inside a string

Ok, that makes sense. Here's a script (just one way) that simplifies things I think:

Code: Select all
import applescript

# Get the current System Volume so we can set it back later
getVolScriptSource = "output volume of (get volume settings)"
getVolScript = applescript.AppleScript(source=getVolScriptSource)
curVolume = getVolScript.run()

# Get the 2 Indigo variables that you'll be using:
#   1) the desired volume for the announcement
#   2) the string to speak (you could make this whole script a shared
#      function and pass the name of the device in)
desiredVol = indigo.variables[ID_OF_VAR_FOR_ANNOUNCEMENT_VOLUME].value
speakValue = indigo.variables[102235652].value

# Create the set volume script source with a placeholder for the value
# since we need to set the volume twice
setVolScriptSource = "set volume output volume {}"

# Set the volume to the desired level for the announcement
volumeScript = applescript.AppleScript(source=setVolScriptSource.format(desiredVol))
volumeScript.run()

# Run the pauseAndSay action which will pause iTunes, speak the text, then resume iTunes
itunesId = "com.perceptiveautomation.indigoplugin.itunes"
itunesPlugin = indigo.server.getPlugin(itunesId)
if itunesPlugin.isEnabled():
    itunesPlugin.executeAction("pauseAndSay", deviceId=180651512, props={'message':speakValue})

# Set the volume back to the original level
resetVolumeScript = applescript.AppleScript(source=setVolScriptSource.format(curVolume))
resetVolumeScript.run()


Untested but probably close. As I mention in a comment, you could make this a shared function and pass in the device name (or id) and volume then you wouldn't need any Indigo variables. Another thing you could do is use the indigo.server.speak() method to speak the message if the iTunes Plugin is disabled for some reason (I'll leave those as exercises for you).

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Posted on
Mon Jan 07, 2019 12:30 pm
indigobo offline
User avatar
Posts: 18
Joined: Jan 04, 2019
Location: Raleigh, NC

Re: Using variables as values inside a string

Jay,

Thank you so much for your detailed reply!

When I run the script I get the following:
getVolScript = applescript.AppleScript(source=getVolScriptSource)
This gives me an error of embedded script.global name 'applescript' is not defined.

Do I need to create a separate AppleScript for the script to use?

Thanks again!!!

Posted on
Mon Jan 07, 2019 7:09 pm
jay (support) offline
Site Admin
User avatar
Posts: 15856
Joined: Mar 19, 2008
Location: Austin, Texas

Re: Using variables as values inside a string

You need to run the script from Indigo (not directly from a Python interpreter). Use the Execute Script action.

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Posted on
Mon Jan 14, 2019 12:29 pm
indigobo offline
User avatar
Posts: 18
Joined: Jan 04, 2019
Location: Raleigh, NC

Re: Using variables as values inside a string

Hi Jay,

Sorry for the long delay... It's been a very time-demanding week for me.

Your time and advice is much appreciated!

While working with the code I decided that I may be able to simplify things even further, so I came up with this. Do you see anything that, in your advice, could be done better?

Also, a newbie question:
Am I correct in assuming that each time a script is run that it has to import all necessary modules (time, call, etc.). Seems like that would slow things down a bit. The question is, is there a way that would automatically import these modules so that they are readily available, like maybe doing it at server start up?

Code: Select all
from subprocess import call
import time

speakDevice = indigo.variables[451096250].value
sleepValue = indigo.variables[449822468].value
sleepValueFloat =int(sleepValue)

call(["osascript -e 'set volume output volume 55'"], shell=True)

itunesId = "com.perceptiveautomation.indigoplugin.itunes"
itunesPlugin = indigo.server.getPlugin(itunesId)
if itunesPlugin.isEnabled():
   itunesPlugin.executeAction("pauseAndSay", deviceId=180651512, props={'message':speakDevice})

time.sleep(sleepValueFloat)

call(["osascript -e 'set volume output volume 100'"], shell=True)

Posted on
Mon Jan 14, 2019 5:52 pm
jay (support) offline
Site Admin
User avatar
Posts: 15856
Joined: Mar 19, 2008
Location: Austin, Texas

Re: Using variables as values inside a string

indigobo wrote:
Do you see anything that, in your advice, could be done better?


Yes - use the py-applescript module as in my script rather than subprocess.call - the latter will generate extra unix processes and spawning processes is generally expensive. Using the py-applescript module doesn't require any extra processes because the module uses the automatically bridged OS SDK calls to run the AppleScripts inside the current python interpreter process. It won't make that much difference in practice probably, but it is more efficient. See below. I also think it's much more pythonic and readable.

indigobo wrote:
Am I correct in assuming that each time a script is run that it has to import all necessary modules (time, call, etc.). Seems like that would slow things down a bit. The question is, is there a way that would automatically import these modules so that they are readily available, like maybe doing it at server start up?


Importing modules does nothing more than tell the interpreter to look for those modules in it's various paths. Python is exceptionally efficient at dynamic module loading so doing it at the top of a script is not a performance hit. You can actually import the module anywhere in the code, it's just easier to read if you do it at the top.

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Posted on
Mon Jan 14, 2019 9:34 pm
indigobo offline
User avatar
Posts: 18
Joined: Jan 04, 2019
Location: Raleigh, NC

Re: Using variables as values inside a string

Hi Jay,

Okay, I implemented the code just as you wrote it. It works perfectly with one exception. When the script runs, it adjusts the volume to desiredVol variable, speaks the speakValue variable, however it immediately turns the volume back to the original setting. It does it so fast that by the time the speaking starts the volume has already returned to its original setting. So, I inserted time.sleep(2) just before the part where the script sets the volume back and it works perfectly. I might be missing something though.

Also, I know that AppleScript will be deprecated in the next release so will this script continue to work?

P.S. You've helped me so much that the next time I'm in Houston I'll wash your car and mow your grass! :D



Code: Select all
import applescript
import time

# Get the current System Volume so we can set it back later
getVolScriptSource = "output volume of (get volume settings)"
getVolScript = applescript.AppleScript(source=getVolScriptSource)
curVolume = getVolScript.run()

# Get the 2 Indigo variables that you'll be using:
#   1) the desired volume for the announcement
#   2) the string to speak (you could make this whole script a shared
#      function and pass the name of the device in)
desiredVol = indigo.variables[278665072].value
speakValue = indigo.variables[451096250].value

# Create the set volume script source with a placeholder for the value
# since we need to set the volume twice
setVolScriptSource = "set volume output volume {}"

# Set the volume to the desired level for the announcement
volumeScript = applescript.AppleScript(source=setVolScriptSource.format(desiredVol))
volumeScript.run()

# Run the pauseAndSay action which will pause iTunes, speak the text, then resume iTunes
itunesId = "com.perceptiveautomation.indigoplugin.itunes"
itunesPlugin = indigo.server.getPlugin(itunesId)
if itunesPlugin.isEnabled():
    itunesPlugin.executeAction("pauseAndSay", deviceId=180651512, props={'message':speakValue})

time.sleep(2)

# Set the volume back to the original level
resetVolumeScript = applescript.AppleScript(source=setVolScriptSource.format(curVolume))
resetVolumeScript.run()

Posted on
Tue Jan 15, 2019 12:14 pm
jay (support) offline
Site Admin
User avatar
Posts: 15856
Joined: Mar 19, 2008
Location: Austin, Texas

Re: Using variables as values inside a string

indigobo wrote:
So, I inserted time.sleep(2) just before the part where the script sets the volume back and it works perfectly. I might be missing something though.


Nope, that's fine. You don't want embedded scripts that do much pausing (they will be killed if they take longer than 10 seconds to run), but you're well under that threshold. If you do end up needing to pause longer, just run the script as an external script and you won't have to worry about the length of time the script needs to run.

indigobo wrote:
Also, I know that AppleScript will be deprecated in the next release so will this script continue to work?


AppleScript that targets the IndigoServer is being deprecated. This script, in fact, is a great, real-world example of how to integrate AppleScript with Indigo moving forward.

indigobo wrote:
P.S. You've helped me so much that the next time I'm in Houston I'll wash your car and mow your grass! :D


You'd have to drive 3 hours to Austin... :P

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Page 1 of 1

Who is online

Users browsing this forum: No registered users and 1 guest